Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add method argument to aggregate function invocations |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | andygoth-tcl-function |
Files: | files | file ages | folders |
SHA3-256: |
4fc35d5e09e2a4865cff14f2c6fff175 |
User & Date: | andygoth 2019-01-31 05:25:24 |
Context
2019-02-02
| ||
20:17 | Add support for window functions check-in: 4be97e0cfe user: andygoth tags: andygoth-tcl-function | |
2019-01-31
| ||
05:25 | Add method argument to aggregate function invocations check-in: 4fc35d5e09 user: andygoth 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 | |
Changes
Changes to src/tclsqlite.c.
︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 | #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. */ | > > > > > > > > > > > > > > > > | 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 | #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 fixedStrings array. */ enum { FS_STEP, FS_FINAL, FS_NUM_FIXED_STRINGS }; /* ** Fixed string table, indexed by the above enumeration. This table is ** initialized by Sqlite3_Init() and cleaned up by Sqlite3_Unload(). */ static Tcl_Obj *fixedStrings[FS_NUM_FIXED_STRINGS]; /* 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. */ |
︙ | ︙ | |||
901 902 903 904 905 906 907 | /* ** Utility function shared by the following functions: ** - tclSqlFuncScalar() ** - tclSqlFuncStep() ** - tclSqlFuncFinal() ** This function calls a user-defined TCL script to implement an SQL function. ** Before being invoked, additional arguments may be appended to the script. | | | | | | | | 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 | /* ** Utility function shared by the following functions: ** - tclSqlFuncScalar() ** - tclSqlFuncStep() ** - tclSqlFuncFinal() ** This function calls a user-defined TCL script to implement an SQL function. ** Before being invoked, additional arguments may be appended to the script. ** Any number of initial arguments may supplied as TCL objects, and subsequent ** arguments are TCL objects constructed from SQL values. On success, the ** return value is a pointer to the TCL object 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, Tcl_Obj **apPrefix, int argc, sqlite3_value **argv ){ SqlFunc *p = sqlite3_user_data(context); Tcl_Obj *pCmd; int i; int rc; if( argc==0 && !apPrefix ){ /* 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); |
︙ | ︙ | |||
944 945 946 947 948 949 950 | int nArg; if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return 0; } pCmd = Tcl_NewListObj(nArg, aArg); Tcl_IncrRefCount(pCmd); | | > | | | | | > | 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 | int nArg; if( Tcl_ListObjGetElements(p->interp, p->pScript, &nArg, &aArg) ){ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return 0; } pCmd = Tcl_NewListObj(nArg, aArg); Tcl_IncrRefCount(pCmd); if( apPrefix ){ for(i=0; apPrefix[i]; i++){ rc = Tcl_ListObjAppendElement(p->interp, pCmd, apPrefix[i]); if( rc ){ Tcl_DecrRefCount(pCmd); sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); return 0; } } } 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. */ |
︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 | sqlite3_value **argv ){ tclSqlFuncResult(context, tclSqlFuncCall(context, NULL, argc, argv)); } /* ** This routine is called to evaluate an aggregate SQL function implemented | | | | > > < < < < < < < < < < < < > > > | | 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 | sqlite3_value **argv ){ tclSqlFuncResult(context, tclSqlFuncCall(context, NULL, argc, argv)); } /* ** This routine is called to evaluate an aggregate SQL function implemented ** using TCL script. It is invoked for each row in the group. The first ** argument is the fixed string "step". The second argument is the return value ** of the prior invocation, or empty string for the first invocation. Remaining ** arguments to the TCL script are the arguments to the aggregate SQL function. */ static void tclSqlFuncStep( sqlite3_context *context, int argc, sqlite3_value **argv ){ Tcl_Obj **ppState; Tcl_Obj *apPrefix[3]; Tcl_Obj *pResult; ppState = tclSqlFuncState(context); apPrefix[0] = fixedStrings[FS_STEP]; apPrefix[1] = *ppState; apPrefix[2] = NULL; pResult = tclSqlFuncCall(context, apPrefix, argc, argv); if( pResult ){ /* If the step function succeeded, increment the new state reference count ** before decrementing the old state reference count. This sequence will ** deallocate the old state if it is a distinct TCL object from the new, but ** it will not deallocate the old state if the TCL script modified it in ** place and then returned it again. ** |
︙ | ︙ | |||
1180 1181 1182 1183 1184 1185 1186 | Tcl_DecrRefCount(*ppState); *ppState = &tclSqlFuncError; } } /* ** This routine is called to evaluate an aggregate SQL function implemented | | > | | > > > | > > > | | 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 | Tcl_DecrRefCount(*ppState); *ppState = &tclSqlFuncError; } } /* ** This routine is called to evaluate an aggregate SQL function implemented ** using TCL script. It is invoked at the end of each group. The first ** argument is the fixed string "final". The TCL result of the prior invocation ** (via tclSqlFuncStep()) is the second argument to this invocation, or empty ** string if there were no rows. */ static void tclSqlFuncFinal(sqlite3_context *context){ Tcl_Obj **ppState; Tcl_Obj *apPrefix[3]; ppState = tclSqlFuncState(context); if( *ppState!=&tclSqlFuncError ){ apPrefix[0] = fixedStrings[FS_FINAL]; apPrefix[1] = *ppState; apPrefix[2] = NULL; tclSqlFuncResult(context, tclSqlFuncCall(context, apPrefix, 0, 0)); Tcl_DecrRefCount(*ppState); } } #ifndef SQLITE_OMIT_AUTHORIZATION /* ** This is the authentication function. It appends the authentication |
︙ | ︙ | |||
2852 2853 2854 2855 2856 2857 2858 | }else{ Tcl_AppendResult(interp, "bad option \"", z, "\": must be -argcount, -deterministic, or -aggregate", (char *)0 ); return TCL_ERROR; } } | < < < < < | 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 | }else{ Tcl_AppendResult(interp, "bad option \"", z, "\": must be -argcount, -deterministic, 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); |
︙ | ︙ | |||
3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 | ** 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); } | > > > > > > > > | > > > > > > > > > | > > | 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 | ** 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( !*fixedStrings ){ #define FIXED_STRING(i, s) \ (fixedStrings[(i)] = Tcl_NewStringObj((s), sizeof((s)) - 1), \ Tcl_IncrRefCount(fixedStrings[(i)])) FIXED_STRING(FS_STEP , "step" ); FIXED_STRING(FS_FINAL, "final"); #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(fixedStrings[i]); fixedStrings[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;} |
︙ | ︙ |