/ Check-in Differences
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From root:andygoth-tcl-function To andygoth-tcl-function

2019-02-03
07:30
Rename -new to -initial Leaf check-in: e4a7d847d1 user: andy tags: andygoth-tcl-function
06:44
Add missing initialization check-in: 5e7cff8436 user: andy tags: andygoth-tcl-function
2019-01-30
17:30
Initial implementation of user-defined Tcl aggregate functions. Testing and documentation to come. check-in: e0689f05d1 user: andygoth tags: andygoth-tcl-function
15:47
Add the --progress, --using, and -q options to the index_usage utility program. check-in: a5e6be7cbc user: drh tags: trunk
14:01
Enhancements to the index_usage utility program. check-in: 19c739b4a8 user: drh tags: trunk
12:15
Fix another buffer overread in fts5 that may occur when accessing a corrupt database. check-in: 760d14374d user: dan tags: trunk

Changes to src/tclsqlite.c.

83
84
85
86
87
88
89




















90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106









107
108
109
110
111
112
113
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT
#endif /* BUILD_sqlite */

#define NUM_PREPARED_STMTS 10
#define MAX_PREPARED_STMTS 100





















/* Forward declaration */
typedef struct SqliteDb SqliteDb;

/*
** New SQL functions can be created as TCL scripts.  Each such function
** is described by an instance of the following structure.
*/
typedef struct SqlFunc SqlFunc;
struct SqlFunc {
  Tcl_Interp *interp;   /* The TCL interpret to execute the function */
  Tcl_Obj *pScript;     /* The Tcl_Obj representation of the script */

  SqliteDb *pDb;        /* Database connection that owns this function */
  int useEvalObjv;      /* True if it is safe to use Tcl_EvalObjv */
  char *zName;          /* Name of this function */
  SqlFunc *pNext;       /* Next function on the list of them all */
};










/*
** New collation sequences function can be created as TCL scripts.  Each such
** function is described by an instance of the following structure.
*/
typedef struct SqlCollate SqlCollate;
struct SqlCollate {
  Tcl_Interp *interp;   /* The TCL interpret to execute the function */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|






>

|




>
>
>
>
>
>
>
>
>







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT
#endif /* BUILD_sqlite */

#define NUM_PREPARED_STMTS 10
#define MAX_PREPARED_STMTS 100

/*
** Fixed string table index enumeration.  These enumeration values are used as
** indexes into the pFixedStrings array.
*/
typedef enum {
  FS_NEW, FS_STEP, FS_INVERSE, FS_VALUE, FS_DESTROY, FS_NUM_FIXED_STRINGS
} FixedString;

/*
** Fixed string table, indexed by the above enumeration.  This table is
** initialized by Sqlite3_Init() and cleaned up by Sqlite3_Unload().
*/
static Tcl_Obj *pFixedStrings[FS_NUM_FIXED_STRINGS];

/*
** TCL SQL function flags.
*/
#define FF_AGGREGATE 1  /* This is an aggregate or window function */
#define FF_CLASS 2      /* pScript is a TclOO-style class name */

/* Forward declaration */
typedef struct SqliteDb SqliteDb;

/*
** New SQL functions can be created as TCL command prefixes.  Each such function
** is described by an instance of the following structure.
*/
typedef struct SqlFunc SqlFunc;
struct SqlFunc {
  Tcl_Interp *interp;   /* The TCL interpret to execute the function */
  Tcl_Obj *pScript;     /* The Tcl_Obj representation of the script */
  Tcl_Obj *pInitial;    /* Constructor argument(s) to the "new" method */
  SqliteDb *pDb;        /* Database connection that owns this function */
  int flags;            /* Function configuration FF_* bitmask */
  char *zName;          /* Name of this function */
  SqlFunc *pNext;       /* Next function on the list of them all */
};

/*
** Persistent aggregate/window function state data structure.
*/
typedef struct {
  int initialized;      /* Nonzero if initialization has taken place */
  int window;           /* Nonzero if tclSqlFuncValue() was called */
  Tcl_Obj *pObj;        /* TCL state data. */
} SqlFuncState;

/*
** New collation sequences function can be created as TCL scripts.  Each such
** function is described by an instance of the following structure.
*/
typedef struct SqlCollate SqlCollate;
struct SqlCollate {
  Tcl_Interp *interp;   /* The TCL interpret to execute the function */
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482
  Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE);
  return TCL_OK;
}
#else  /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */
  #define closeIncrblobChannels(pDb)
#endif

/*
** Look at the script prefix in pCmd.  We will be executing this script
** after first appending one or more arguments.  This routine analyzes
** the script to see if it is safe to use Tcl_EvalObjv() on the script
** rather than the more general Tcl_EvalEx().  Tcl_EvalObjv() is much
** faster.
**
** Scripts that are safe to use with Tcl_EvalObjv() consists of a
** command name followed by zero or more arguments with no [...] or $
** or {...} or ; to be seen anywhere.  Most callback scripts consist
** of just a single procedure name and they meet this requirement.
*/
static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){
  /* We could try to do something with Tcl_Parse().  But we will instead
  ** just do a search for forbidden characters.  If any of the forbidden
  ** characters appear in pCmd, we will report the string as unsafe.
  */
  const char *z;
  int n;
  z = Tcl_GetStringFromObj(pCmd, &n);
  while( n-- > 0 ){
    int c = *(z++);
    if( c=='$' || c=='[' || c==';' ) return 0;
  }
  return 1;
}

/*
** Find an SqlFunc structure with the given name.  Or create a new
** one if an existing one cannot be found.  Return a pointer to the
** structure.
*/
static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
  SqlFunc *p, *pNew;
  int nName = strlen30(zName);
  pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
  pNew->zName = (char*)&pNew[1];
  memcpy(pNew->zName, zName, nName+1);
  for(p=pDb->pFunc; p; p=p->pNext){
    if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
      Tcl_Free((char*)pNew);
      return p;
    }
  }
  pNew->interp = pDb->interp;
  pNew->pDb = pDb;
  pNew->pScript = 0;

  pNew->pNext = pDb->pFunc;
  pDb->pFunc = pNew;
  return pNew;
}

/*
** Free a single SqlPreparedStmt object.







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




















>







452
453
454
455
456
457
458



























459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  Tcl_SetResult(interp, (char *)Tcl_GetChannelName(p->channel), TCL_VOLATILE);
  return TCL_OK;
}
#else  /* else clause for "#ifndef SQLITE_OMIT_INCRBLOB" */
  #define closeIncrblobChannels(pDb)
#endif




























/*
** Find an SqlFunc structure with the given name.  Or create a new
** one if an existing one cannot be found.  Return a pointer to the
** structure.
*/
static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
  SqlFunc *p, *pNew;
  int nName = strlen30(zName);
  pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
  pNew->zName = (char*)&pNew[1];
  memcpy(pNew->zName, zName, nName+1);
  for(p=pDb->pFunc; p; p=p->pNext){
    if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
      Tcl_Free((char*)pNew);
      return p;
    }
  }
  pNew->interp = pDb->interp;
  pNew->pDb = pDb;
  pNew->pScript = 0;
  pNew->pInitial = 0;
  pNew->pNext = pDb->pFunc;
  pDb->pFunc = pNew;
  return pNew;
}

/*
** Free a single SqlPreparedStmt object.
517
518
519
520
521
522
523



524
525
526
527
528
529
530
  closeIncrblobChannels(pDb);
  sqlite3_close(pDb->db);
  while( pDb->pFunc ){
    SqlFunc *pFunc = pDb->pFunc;
    pDb->pFunc = pFunc->pNext;
    assert( pFunc->pDb==pDb );
    Tcl_DecrRefCount(pFunc->pScript);



    Tcl_Free((char*)pFunc);
  }
  while( pDb->pCollate ){
    SqlCollate *pCollate = pDb->pCollate;
    pDb->pCollate = pCollate->pNext;
    Tcl_Free((char*)pCollate);
  }







>
>
>







521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  closeIncrblobChannels(pDb);
  sqlite3_close(pDb->db);
  while( pDb->pFunc ){
    SqlFunc *pFunc = pDb->pFunc;
    pDb->pFunc = pFunc->pNext;
    assert( pFunc->pDb==pDb );
    Tcl_DecrRefCount(pFunc->pScript);
    if( pFunc->pInitial ){
      Tcl_DecrRefCount(pFunc->pInitial);
    }
    Tcl_Free((char*)pFunc);
  }
  while( pDb->pCollate ){
    SqlCollate *pCollate = pDb->pCollate;
    pDb->pCollate = pCollate->pNext;
    Tcl_Free((char*)pCollate);
  }
894
895
896
897
898
899
900



901











902
903











904

905




906
907

908
909
910






911

912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937






































938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988




989
990
991

992










993

994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019




































































































































































1020
1021
1022
1023
1024
1025
1026
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA));
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB));
  Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
  Tcl_DecrRefCount(pCmd);
  return (atoi(Tcl_GetStringResult(p->interp)));
}




/*











** This routine is called to evaluate an SQL function implemented
** using TCL script.











*/

static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){




  SqlFunc *p = sqlite3_user_data(context);
  Tcl_Obj *pCmd;

  int i;
  int rc;







  if( argc==0 ){

    /* If there are no arguments to the function, call Tcl_EvalObjEx on the
    ** script object directly.  This allows the TCL compiler to generate
    ** bytecode for the command on the first invocation and thus make
    ** subsequent invocations much faster. */
    pCmd = p->pScript;
    Tcl_IncrRefCount(pCmd);
    rc = Tcl_EvalObjEx(p->interp, pCmd, 0);
    Tcl_DecrRefCount(pCmd);
  }else{
    /* If there are arguments to the function, make a shallow copy of the
    ** script object, lappend the arguments, then evaluate the copy.
    **
    ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated.
    ** The new Tcl_Obj contains pointers to the original list elements.
    ** That way, when Tcl_EvalObjv() is run and shimmers the first element
    ** of the list to tclCmdNameType, that alternate representation will
    ** be preserved and reused on the next invocation.
    */
    Tcl_Obj **aArg;
    int nArg;
    if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){
      sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
      return;
    }
    pCmd = Tcl_NewListObj(nArg, aArg);
    Tcl_IncrRefCount(pCmd);






































    for(i=0; i<argc; i++){
      sqlite3_value *pIn = argv[i];
      Tcl_Obj *pVal;

      /* Set pVal to contain the i'th column of this row. */
      switch( sqlite3_value_type(pIn) ){
        case SQLITE_BLOB: {
          int bytes = sqlite3_value_bytes(pIn);
          pVal = Tcl_NewByteArrayObj(sqlite3_value_blob(pIn), bytes);
          break;
        }
        case SQLITE_INTEGER: {
          sqlite_int64 v = sqlite3_value_int64(pIn);
          if( v>=-2147483647 && v<=2147483647 ){
            pVal = Tcl_NewIntObj((int)v);
          }else{
            pVal = Tcl_NewWideIntObj(v);
          }
          break;
        }
        case SQLITE_FLOAT: {
          double r = sqlite3_value_double(pIn);
          pVal = Tcl_NewDoubleObj(r);
          break;
        }
        case SQLITE_NULL: {
          pVal = Tcl_NewStringObj(p->pDb->zNull, -1);
          break;
        }
        default: {
          int bytes = sqlite3_value_bytes(pIn);
          pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes);
          break;
        }
      }
      rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal);
      if( rc ){
        Tcl_DecrRefCount(pCmd);
        sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
        return;
      }
    }
    if( !p->useEvalObjv ){
      /* Tcl_EvalObjEx() will automatically call Tcl_EvalObjv() if pCmd
      ** is a list without a string representation.  To prevent this from
      ** happening, make sure pCmd has a valid string representation */
      Tcl_GetString(pCmd);
    }
    rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
    Tcl_DecrRefCount(pCmd);
  }





  if( rc && rc!=TCL_RETURN ){
    sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);

  }else{










    Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);

    int n;
    u8 *data;
    const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
    char c = zType[0];
    if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
      /* Only return a BLOB type if the Tcl variable is a bytearray and
      ** has no string representation. */
      data = Tcl_GetByteArrayFromObj(pVar, &n);
      sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT);
    }else if( c=='b' && strcmp(zType,"boolean")==0 ){
      Tcl_GetIntFromObj(0, pVar, &n);
      sqlite3_result_int(context, n);
    }else if( c=='d' && strcmp(zType,"double")==0 ){
      double r;
      Tcl_GetDoubleFromObj(0, pVar, &r);
      sqlite3_result_double(context, r);
    }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
          (c=='i' && strcmp(zType,"int")==0) ){
      Tcl_WideInt v;
      Tcl_GetWideIntFromObj(0, pVar, &v);
      sqlite3_result_int64(context, v);
    }else{
      data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
      sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT);
    }
  }




































































































































































}

#ifndef SQLITE_OMIT_AUTHORIZATION
/*
** This is the authentication function.  It appends the authentication
** type code and the two arguments to zCmd[] then invokes the result
** on the interpreter.  The reply is examined to determine if the







>
>
>

>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>

>
|
>
>
>
>
|

>



>
>
>
>
>
>
|
>




<

|
<












|
|
|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<


<
|

|
|



|








<
|



|



|
|



|
<

|
|


<
<
<
<
<
|
<
<
|
>
>
>
>


|
>

>
>
>
>
>
>
>
>
>
>
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960

961
962

963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019

1020
1021

1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037

1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051

1052
1053
1054
1055
1056





1057


1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zA, nA));
  Tcl_ListObjAppendElement(p->interp, pCmd, Tcl_NewStringObj(zB, nB));
  Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
  Tcl_DecrRefCount(pCmd);
  return (atoi(Tcl_GetStringResult(p->interp)));
}

/* Forward declaration */
static SqlFuncState *tclSqlFuncState(sqlite3_context *context);

/*
** This function calls a TCL command prefix to implement an SQL function.
**
** The command prefix is automatically determined.  If the function is a scalar
** function, or the "-state" calling convention is used, or the method is "new",
** the TCL script provided at the time the function was defined is used as the
** command prefix.  If the function is an aggregate function, and the "-class"
** calling convention is used, and the method is not "new", the current state
** value is used as the command prefix.
**
** If the function is a scalar function, the next paragraph does not apply, and
** the method argument is ignored.  For scalar functions, the only arguments are
** SQL function argument values.
**
** The first argument to the command prefix is the method name.  If the method
** is not "new" and the "-state" calling convention is used, the second argument
** is the current state value.  If the method is "new" and the "-state" calling
** convention is used, the value of the "-initial" switch (defaulting to empty
** string) is the second argument.  If the method is "new" and the "-class"
** calling convention is used, the elements of the value of the "-initial"
** switch (defaulting to empty list) are the second and subsequent arguments.
** After these arguments are appended SQL function argument values, if supplied.
**
** On success, the return value is a pointer to the Tcl_Obj result.  On failure,
** the return value is NULL, and an error message is stored in the SQL context.
*/
static Tcl_Obj *tclSqlFuncCall(
  sqlite3_context *context,
  FixedString method,
  int argc,
  sqlite3_value **argv
){
  SqlFunc *pFunc = sqlite3_user_data(context);
  Tcl_Obj *pCmd;
  int flags = 0;
  int i;
  int rc;

  /* Determine the command prefix. */
  if( (~pFunc->flags & (FF_AGGREGATE|FF_CLASS)) || method==FS_NEW ){
    pCmd = pFunc->pScript;
  }else{
    pCmd = tclSqlFuncState(context)->pObj;
  }

  if( !(pFunc->flags & FF_AGGREGATE) && argc==0 ){
    /* If there are no arguments to the function, call Tcl_EvalObjEx on the
    ** script object directly.  This allows the TCL compiler to generate
    ** bytecode for the command on the first invocation and thus make
    ** subsequent invocations much faster. */

    Tcl_IncrRefCount(pCmd);
    flags = TCL_EVAL_DIRECT;

  }else{
    /* If there are arguments to the function, make a shallow copy of the
    ** script object, lappend the arguments, then evaluate the copy.
    **
    ** By "shallow" copy, we mean only the outer list Tcl_Obj is duplicated.
    ** The new Tcl_Obj contains pointers to the original list elements.
    ** That way, when Tcl_EvalObjv() is run and shimmers the first element
    ** of the list to tclCmdNameType, that alternate representation will
    ** be preserved and reused on the next invocation.
    */
    Tcl_Obj **aArg;
    int nArg;
    if( Tcl_ListObjGetElements(pFunc->interp, pCmd, &nArg, &aArg) ){
      sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
      return 0;
    }
    pCmd = Tcl_NewListObj(nArg, aArg);
    Tcl_IncrRefCount(pCmd);

    /* Append aggregate/window function initial arguments. */
    if( pFunc->flags & FF_AGGREGATE ){
      /* Append method name argument. */
      if( Tcl_ListObjAppendElement(pFunc->interp, pCmd,
          pFixedStrings[method]) ){
        Tcl_DecrRefCount(pCmd);
        sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
        return 0;
      }

      /* Append state argument or "new" constructor arguments. */
      if( !(pFunc->flags & FF_CLASS) ){
        Tcl_Obj *pState;
        if( method!=FS_NEW ){
          pState = tclSqlFuncState(context)->pObj;
        }else if( pFunc->pInitial ){
          pState = pFunc->pInitial;
        }else{
          pState = Tcl_NewObj();
        }
        if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, pState) ){
          if( method==FS_NEW && !pFunc->pInitial ){
            Tcl_DecrRefCount(pState);
          }
          Tcl_DecrRefCount(pCmd);
          sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
          return 0;
        }
      }else if( method==FS_NEW && pFunc->pInitial
             && Tcl_ListObjAppendList(pFunc->interp, pCmd, pFunc->pInitial) ){
        Tcl_DecrRefCount(pCmd);
        sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
        return 0;
      }
    }

    /* Append SQL arguments. */
    for(i=0; i<argc; i++){

      Tcl_Obj *pVal;


      switch( sqlite3_value_type(argv[i]) ){
        case SQLITE_BLOB: {
          int bytes = sqlite3_value_bytes(argv[i]);
          pVal = Tcl_NewByteArrayObj(sqlite3_value_blob(argv[i]), bytes);
          break;
        }
        case SQLITE_INTEGER: {
          sqlite_int64 v = sqlite3_value_int64(argv[i]);
          if( v>=-2147483647 && v<=2147483647 ){
            pVal = Tcl_NewIntObj((int)v);
          }else{
            pVal = Tcl_NewWideIntObj(v);
          }
          break;
        }
        case SQLITE_FLOAT: {

          pVal = Tcl_NewDoubleObj(sqlite3_value_double(argv[i]));
          break;
        }
        case SQLITE_NULL: {
          pVal = Tcl_NewStringObj(pFunc->pDb->zNull, -1);
          break;
        }
        default: {
          int bytes = sqlite3_value_bytes(argv[i]);
          pVal = Tcl_NewStringObj((char *)sqlite3_value_text(argv[i]), bytes);
          break;
        }
      }
      if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, pVal) ){

        Tcl_DecrRefCount(pCmd);
        sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
        return 0;
      }
    }





  }



  /* Invoke the completed command object, then deallocate it if it was a
  ** temporary list object created above. */
  rc = Tcl_EvalObjEx(pFunc->interp, pCmd, flags);
  Tcl_DecrRefCount(pCmd);

  if( rc && rc!=TCL_RETURN ){
    sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
    return 0;
  }else{
    return Tcl_GetObjResult(pFunc->interp);
  }
}

/*
** This function converts a Tcl_Obj result to an SQL value and stores it into
** the SQL context.
*/
static void tclSqlFuncResult(
  sqlite3_context *context,
  Tcl_Obj *pVar
){
  int n;
  u8 *data;
  const char *zType = pVar->typePtr ? pVar->typePtr->name : "";
  char c = zType[0];
  if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
    /* Only return a BLOB type if the Tcl variable is a bytearray and
    ** has no string representation. */
    data = Tcl_GetByteArrayFromObj(pVar, &n);
    sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT);
  }else if( c=='b' && strcmp(zType,"boolean")==0 ){
    Tcl_GetIntFromObj(0, pVar, &n);
    sqlite3_result_int(context, n);
  }else if( c=='d' && strcmp(zType,"double")==0 ){
    double r;
    Tcl_GetDoubleFromObj(0, pVar, &r);
    sqlite3_result_double(context, r);
  }else if( (c=='w' && strcmp(zType,"wideInt")==0) ||
        (c=='i' && strcmp(zType,"int")==0) ){
    Tcl_WideInt v;
    Tcl_GetWideIntFromObj(0, pVar, &v);
    sqlite3_result_int64(context, v);
  }else{
    data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n);
    sqlite3_result_text(context, (char *)data, n, SQLITE_TRANSIENT);
  }
}

/*
 * This function returns the address of a state structure that persists for the
 * duration of the execution of an aggregate or window function.
 *
 * If the "-state" (default) calling convention is selected, the pObj field
 * points to the Tcl_Obj most recently returned by the "new", "step", or
 * "inverse" methods of the TCL command prefix registered when the SQL function
 * was first created.  This Tcl_Obj will be used as the second argument (i.e.
 * the one following the method name) the next time the command prefix is used.
 *
 * If the "-class" calling convention is selected, the pObj field points to the
 * Tcl_Obj containing the command prefix to invoke.  This command prefix is the
 * result value obtained by invoking the "new" method, as with the "-state"
 * calling convention.
 *
 * In event of error, the pObj field will be NULL, and the TCL should not be
 * called again for the duration of the SQL function execution.
*/
static SqlFuncState *tclSqlFuncState(sqlite3_context *context){
  SqlFuncState *pState = sqlite3_aggregate_context(context, sizeof(*pState));
  if( !pState->initialized ){
    pState->initialized = 1;
    pState->window = 0;
    pState->pObj = tclSqlFuncCall(context, FS_NEW, 0, NULL);
    if( pState->pObj ){
      Tcl_IncrRefCount(pState->pObj);
    }
  }
  return pState;
}

/*
** Destructor for custom SQL functions defined in TCL.  When execution of an SQL
** function completes, either successfully or due to error, the "destroy" method
** is invoked so that script-level cleanup may be performed.  Next, the current
** state Tcl_Obj is deallocated and its pointer is set NULL so that TCL will not
** be invoked again for the current SQL function.
**/
static void tclSqlFuncDestroy(sqlite3_context *context)
{
  SqlFuncState *pState = tclSqlFuncState(context);
  if( pState->pObj ){
    tclSqlFuncCall(context, FS_DESTROY, 0, NULL);
    Tcl_DecrRefCount(pState->pObj);
    pState->pObj = NULL;
  }
}

/*
** Common implementation of tclSqlFuncStep() and tclSqlFuncInverse().
*/
static void tclSqlFuncStepOrInverse(
  sqlite3_context *context,
  FixedString method,
  int argc,
  sqlite3_value **argv
){
  SqlFuncState *pState = tclSqlFuncState(context);
  if( pState->pObj ){
    Tcl_Obj *pVal = tclSqlFuncCall(context, method, argc, argv);
    if( !pVal ){
      tclSqlFuncDestroy(context);
    }else if( !(((SqlFunc *)sqlite3_user_data(context))->flags & FF_CLASS) ){
      Tcl_IncrRefCount(pVal);
      Tcl_DecrRefCount(pState->pObj);
      pState->pObj = pVal;
    }
  }
}

/*
** This routine is called to evaluate a scalar SQL function implemented
** using TCL script.
*/
static void tclSqlFuncScalar(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  Tcl_Obj *pVal = tclSqlFuncCall(context, 0, argc, argv);
  if( pVal ){
    tclSqlFuncResult(context, pVal);
  }
}

/*
** This routine is called for each row when it is added to the aggregate or
** window to evaluate an SQL function implemented using TCL script.
**
** If this is the first row in the group, the "new" method (i.e. first argument
** to the command prefix) is used so that initialization may occur.
**
** The "step" method is invoked using the SQL arguments.
**
** When using the "-state" calling convention, the state object is replaced with
** the result value obtained from the "step" method.
*/
static void tclSqlFuncStep(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  tclSqlFuncStepOrInverse(context, FS_STEP, argc, argv);
}

/*
** This routine is called for the final row of each group, or in event of error,
** or if there were no rows, to evaluate an aggregate or window SQL function
** implemented using TCL script.
**
** If there were no rows, the "new" method will be invoked first to initialize.
**
** If this is a non-window function, the "value" method is invoked.  The check
** for window functions is done to avoid invoking "value" twice for the last row
** of each group.
** 
** For both window and non-window aggregate functions, "destroy" is invoked.
*/
static void tclSqlFuncFinal(sqlite3_context *context){
  SqlFuncState *pState = tclSqlFuncState(context);
  if( pState->pObj ){
    if( !pState->window ){
      Tcl_Obj *pVal = tclSqlFuncCall(context, FS_VALUE, 0, NULL);
      if( pVal ){
        tclSqlFuncResult(context, pVal);
      }
    }
    tclSqlFuncDestroy(context);
  }
}

/*
** This routine is called for each row to evaluate a window SQL function
** implemented using TCL script.  The "value" method is invoked.
*/
static void tclSqlFuncValue(sqlite3_context *context){
  SqlFuncState *pState = tclSqlFuncState(context);
  if( pState->pObj ){
    Tcl_Obj *pVal = tclSqlFuncCall(context, FS_VALUE, 0, NULL);
    if( pVal ){
      tclSqlFuncResult(context, pVal);
    }else{
      tclSqlFuncDestroy(context);
    }
    pState->window = 1;
  }
}

/*
** This routine is called for each row when it is removed from the window to
** evaluate an SQL function implemented using TCL script.
**
** The "inverse" method is invoked using the SQL arguments.
**
** When using the "-state" calling convention, the state object is replaced with
** the result value obtained from the "step" method.
*/
static void tclSqlFuncInverse(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  tclSqlFuncStepOrInverse(context, FS_INVERSE, argc, argv);
}

#ifndef SQLITE_OMIT_AUTHORIZATION
/*
** This is the authentication function.  It appends the authentication
** type code and the two arguments to zCmd[] then invokes the result
** on the interpreter.  The reply is examined to determine if the
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642









2643

2644
2645

2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656

2657
2658
2659
2660


2661
2662
2663
2664
2665
2666
2667
2668
2669
2670









2671
2672
2673



2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685



2686
2687
2688






2689
2690











2691
2692
2693
2694
2695
2696
2697
      cd2[1] = (void *)pScript;
      rc = DbEvalNextCmd(cd2, interp, TCL_OK);
    }
    break;
  }

  /*
  **     $db function NAME [-argcount N] [-deterministic] SCRIPT
  **
  ** Create a new SQL function called NAME.  Whenever that function is
  ** called, invoke SCRIPT to evaluate the function.
  */
  case DB_FUNCTION: {









    int flags = SQLITE_UTF8;

    SqlFunc *pFunc;
    Tcl_Obj *pScript;

    char *zName;
    int nArg = -1;
    int i;
    if( objc<4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT");
      return TCL_ERROR;
    }
    for(i=3; i<(objc-1); i++){
      const char *z = Tcl_GetString(objv[i]);
      int n = strlen30(z);
      if( n>2 && strncmp(z, "-argcount",n)==0 ){

        if( i==(objc-2) ){
          Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0);
          return TCL_ERROR;
        }


        if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR;
        if( nArg<0 ){
          Tcl_AppendResult(interp, "number of arguments must be non-negative",
                           (char*)0);
          return TCL_ERROR;
        }
        i++;
      }else
      if( n>2 && strncmp(z, "-deterministic",n)==0 ){
        flags |= SQLITE_DETERMINISTIC;









      }else{
        Tcl_AppendResult(interp, "bad option \"", z,
            "\": must be -argcount or -deterministic", (char*)0



        );
        return TCL_ERROR;
      }
    }

    pScript = objv[objc-1];
    zName = Tcl_GetStringFromObj(objv[2], 0);
    pFunc = findSqlFunc(pDb, zName);
    if( pFunc==0 ) return TCL_ERROR;
    if( pFunc->pScript ){
      Tcl_DecrRefCount(pFunc->pScript);
    }



    pFunc->pScript = pScript;
    Tcl_IncrRefCount(pScript);
    pFunc->useEvalObjv = safeToUseEvalObjv(interp, pScript);






    rc = sqlite3_create_function(pDb->db, zName, nArg, flags,
        pFunc, tclSqlFunc, 0, 0);











    if( rc!=SQLITE_OK ){
      rc = TCL_ERROR;
      Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
    }
    break;
  }








|





>
>
>
>
>
>
>
>
>
|
>


>









|
|
>
|
|
|
|
>
>







|
|
|
>
>
>
>
>
>
>
>
>
|
|
|
>
>
>
|
|
<









>
>
>


|
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>







2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951

2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
      cd2[1] = (void *)pScript;
      rc = DbEvalNextCmd(cd2, interp, TCL_OK);
    }
    break;
  }

  /*
  **     $db function NAME ?SWITCHES? SCRIPT
  **
  ** Create a new SQL function called NAME.  Whenever that function is
  ** called, invoke SCRIPT to evaluate the function.
  */
  case DB_FUNCTION: {
    static const char *SW_strs[] = {
      "-argcount", "-deterministic", "-state", "-class", "-initial",
      "-scalar", "-aggregate", "-window", 0
    };
    enum SW_enum {
      SW_ARGCOUNT, SW_DETERMINISTIC, SW_STATE, SW_CLASS, SW_INITIAL,
      SW_SCALAR, SW_AGGREGATE, SW_WINDOW
    };
    enum {TY_SCALAR, TY_AGGREGATE, TY_WINDOW} type = TY_SCALAR;
    int sqlFlags = SQLITE_UTF8;
    int tclFlags = 0;
    SqlFunc *pFunc;
    Tcl_Obj *pScript;
    Tcl_Obj *pInitial = NULL;
    char *zName;
    int nArg = -1;
    int i;
    if( objc<4 ){
      Tcl_WrongNumArgs(interp, 2, objv, "NAME ?SWITCHES? SCRIPT");
      return TCL_ERROR;
    }
    for(i=3; i<(objc-1); i++){
      const char *z = Tcl_GetString(objv[i]);
      if( Tcl_GetIndexFromObj(interp, objv[i], SW_strs, "option", 0, &choice) ){
        return TCL_ERROR;
      }
      if( i==(objc-2) && (choice==SW_ARGCOUNT || choice==SW_INITIAL) ){
        Tcl_AppendResult(interp, "option requires an argument: ", z, (char *)0);
        return TCL_ERROR;
      }
      switch( (enum SW_enum)choice ){
      case SW_ARGCOUNT:
        if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR;
        if( nArg<0 ){
          Tcl_AppendResult(interp, "number of arguments must be non-negative",
                           (char*)0);
          return TCL_ERROR;
        }
        i++;
        break;
      case SW_INITIAL      : pInitial = objv[++i]            ; break;
      case SW_DETERMINISTIC: sqlFlags |= SQLITE_DETERMINISTIC; break;
      case SW_STATE        : tclFlags &= ~FF_CLASS           ; break;
      case SW_CLASS        : tclFlags |= FF_CLASS            ; break;
      case SW_SCALAR       : type = TY_SCALAR                ; break;
      case SW_AGGREGATE    : type = TY_AGGREGATE             ; break;
      case SW_WINDOW       : type = TY_WINDOW                ; break;
      }
    }
    if( type!=TY_SCALAR ){
      tclFlags |= FF_AGGREGATE;
    }else if( tclFlags&FF_CLASS ){
      Tcl_AppendResult(interp, "-class requires -window or -aggregate",
          (char *)0);
      return TCL_ERROR;
    }else if( pInitial ){
      Tcl_AppendResult(interp, "-initial requires -window or -aggregate",
          (char *)0);
      return TCL_ERROR;

    }

    pScript = objv[objc-1];
    zName = Tcl_GetStringFromObj(objv[2], 0);
    pFunc = findSqlFunc(pDb, zName);
    if( pFunc==0 ) return TCL_ERROR;
    if( pFunc->pScript ){
      Tcl_DecrRefCount(pFunc->pScript);
    }
    if( pFunc->pInitial ){
      Tcl_DecrRefCount(pFunc->pInitial);
    }
    pFunc->pScript = pScript;
    Tcl_IncrRefCount(pScript);
    pFunc->pInitial = pInitial;
    if( pInitial ){
      Tcl_IncrRefCount(pInitial);
    }
    pFunc->flags = tclFlags;
    switch( type ){
    case TY_SCALAR:
      rc = sqlite3_create_function(pDb->db, zName, nArg, sqlFlags,
          pFunc, tclSqlFuncScalar, 0, 0);
      break;
    case TY_AGGREGATE:
      rc = sqlite3_create_function(pDb->db, zName, nArg, sqlFlags,
          pFunc, 0, tclSqlFuncStep, tclSqlFuncFinal);
      break;
    case TY_WINDOW:
      rc = sqlite3_create_window_function(pDb->db, zName, nArg, sqlFlags,
          pFunc, tclSqlFuncStep, tclSqlFuncFinal, tclSqlFuncValue,
          tclSqlFuncInverse, 0);
      break;
    }
    if( rc!=SQLITE_OK ){
      rc = TCL_ERROR;
      Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
    }
    break;
  }

3677
3678
3679
3680
3681
3682
3683











3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696









3697


3698
3699
3700
3701
3702
3703
3704
** for additional information.
**
** The EXTERN macros are required by TCL in order to work on windows.
*/
EXTERN int Sqlite3_Init(Tcl_Interp *interp){
  int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR;
  if( rc==TCL_OK ){











    Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#ifndef SQLITE_3_SUFFIX_ONLY
    /* The "sqlite" alias is undocumented.  It is here only to support
    ** legacy scripts.  All new scripts should use only the "sqlite3"
    ** command. */
    Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#endif
    rc = Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
  }
  return rc;
}
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }









EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }



/* Because it accesses the file-system and uses persistent state, SQLite
** is not considered appropriate for safe interpreters.  Hence, we cause
** the _SafeInit() interfaces return TCL_ERROR.
*/
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; }
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;}







>
>
>
>
>
>
>
>
>
>
>












|
>
>
>
>
>
>
>
>
>
|
>
>







3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
** for additional information.
**
** The EXTERN macros are required by TCL in order to work on windows.
*/
EXTERN int Sqlite3_Init(Tcl_Interp *interp){
  int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR;
  if( rc==TCL_OK ){
    if( !*pFixedStrings ){
#define FIXED_STRING(i, s) \
  (pFixedStrings[(i)] = Tcl_NewStringObj((s), sizeof((s)) - 1), \
  Tcl_IncrRefCount(pFixedStrings[(i)]))
      FIXED_STRING(FS_NEW    , "new"    );
      FIXED_STRING(FS_STEP   , "step"   );
      FIXED_STRING(FS_INVERSE, "inverse");
      FIXED_STRING(FS_VALUE  , "value"  );
      FIXED_STRING(FS_DESTROY, "destroy");
#undef FIXED_STRING
    }
    Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#ifndef SQLITE_3_SUFFIX_ONLY
    /* The "sqlite" alias is undocumented.  It is here only to support
    ** legacy scripts.  All new scripts should use only the "sqlite3"
    ** command. */
    Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#endif
    rc = Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
  }
  return rc;
}
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){
  int i;
  if( flags==TCL_UNLOAD_DETACH_FROM_PROCESS ){
    for(i=0; i<FS_NUM_FIXED_STRINGS; i++){
      Tcl_DecrRefCount(pFixedStrings[i]);
      pFixedStrings[i] = 0;
    }
  }
  return TCL_OK;
}
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){
  return Sqlite3_Unload(interp, flags);
}

/* Because it accesses the file-system and uses persistent state, SQLite
** is not considered appropriate for safe interpreters.  Hence, we cause
** the _SafeInit() interfaces return TCL_ERROR.
*/
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_ERROR; }
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){return TCL_ERROR;}