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
|
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
** This routine is called to evaluate an SQL function implemented
** using TCL script.
** 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(
static void tclSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv){
SqlFunc *p = sqlite3_user_data(context);
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( argc==0 ){
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. */
pCmd = p->pScript;
Tcl_IncrRefCount(pCmd);
rc = Tcl_EvalObjEx(p->interp, pCmd, 0);
flags = TCL_EVAL_DIRECT;
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;
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++){
sqlite3_value *pIn = argv[i];
Tcl_Obj *pVal;
/* Set pVal to contain the i'th column of this row. */
switch( sqlite3_value_type(pIn) ){
switch( sqlite3_value_type(argv[i]) ){
case SQLITE_BLOB: {
int bytes = sqlite3_value_bytes(pIn);
pVal = Tcl_NewByteArrayObj(sqlite3_value_blob(pIn), bytes);
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(pIn);
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: {
double r = sqlite3_value_double(pIn);
pVal = Tcl_NewDoubleObj(r);
pVal = Tcl_NewDoubleObj(sqlite3_value_double(argv[i]));
break;
}
case SQLITE_NULL: {
pVal = Tcl_NewStringObj(p->pDb->zNull, -1);
pVal = Tcl_NewStringObj(pFunc->pDb->zNull, -1);
break;
}
default: {
int bytes = sqlite3_value_bytes(pIn);
pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes);
int bytes = sqlite3_value_bytes(argv[i]);
pVal = Tcl_NewStringObj((char *)sqlite3_value_text(argv[i]), bytes);
break;
}
}
rc = Tcl_ListObjAppendElement(p->interp, pCmd, pVal);
if( Tcl_ListObjAppendElement(pFunc->interp, pCmd, pVal) ){
if( rc ){
Tcl_DecrRefCount(pCmd);
sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
return;
sqlite3_result_error(context, Tcl_GetStringResult(pFunc->interp), -1);
return 0;
}
}
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);
}
/* 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(p->interp), -1);
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 = 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);
}
}
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
|
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 [-argcount N] [-deterministic] SCRIPT
** $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 flags = SQLITE_UTF8;
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]);
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_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++;
}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;
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->useEvalObjv = safeToUseEvalObjv(interp, pScript);
rc = sqlite3_create_function(pDb->db, zName, nArg, flags,
pFunc, tclSqlFunc, 0, 0);
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
|
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){ return TCL_OK; }
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
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;}
|