1 /* 2 ** 2018 June 17 3 ** 4 ** The author disclaims copyright to this source code. In place of 5 ** a legal notice, here is a blessing: 6 ** 7 ** May you do good and not evil. 8 ** May you find forgiveness for yourself and forgive others. 9 ** May you share freely, never taking more than you give. 10 ** 11 ************************************************************************* 12 */ 13 14 #include "sqlite3.h" 15 16 #ifdef SQLITE_TEST 17 18 #include "sqliteInt.h" 19 #include <tcl.h> 20 21 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); 22 extern const char *sqlite3ErrName(int); 23 24 typedef struct TestWindow TestWindow; 25 struct TestWindow { 26 Tcl_Obj *xStep; 27 Tcl_Obj *xFinal; 28 Tcl_Obj *xValue; 29 Tcl_Obj *xInverse; 30 Tcl_Interp *interp; 31 }; 32 33 typedef struct TestWindowCtx TestWindowCtx; 34 struct TestWindowCtx { 35 Tcl_Obj *pVal; 36 }; 37 38 static void doTestWindowStep( 39 int bInverse, 40 sqlite3_context *ctx, 41 int nArg, 42 sqlite3_value **apArg 43 ){ 44 int i; 45 TestWindow *p = (TestWindow*)sqlite3_user_data(ctx); 46 Tcl_Obj *pEval = Tcl_DuplicateObj(bInverse ? p->xInverse : p->xStep); 47 TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx)); 48 49 Tcl_IncrRefCount(pEval); 50 if( pCtx ){ 51 const char *zResult; 52 int rc; 53 if( pCtx->pVal ){ 54 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal)); 55 }else{ 56 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1)); 57 } 58 for(i=0; i<nArg; i++){ 59 Tcl_Obj *pArg; 60 pArg = Tcl_NewStringObj((const char*)sqlite3_value_text(apArg[i]), -1); 61 Tcl_ListObjAppendElement(p->interp, pEval, pArg); 62 } 63 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); 64 if( rc!=TCL_OK ){ 65 zResult = Tcl_GetStringResult(p->interp); 66 sqlite3_result_error(ctx, zResult, -1); 67 }else{ 68 if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal); 69 pCtx->pVal = Tcl_DuplicateObj(Tcl_GetObjResult(p->interp)); 70 Tcl_IncrRefCount(pCtx->pVal); 71 } 72 } 73 Tcl_DecrRefCount(pEval); 74 } 75 76 static void doTestWindowFinalize(int bValue, sqlite3_context *ctx){ 77 TestWindow *p = (TestWindow*)sqlite3_user_data(ctx); 78 Tcl_Obj *pEval = Tcl_DuplicateObj(bValue ? p->xValue : p->xFinal); 79 TestWindowCtx *pCtx = sqlite3_aggregate_context(ctx, sizeof(TestWindowCtx)); 80 81 Tcl_IncrRefCount(pEval); 82 if( pCtx ){ 83 const char *zResult; 84 int rc; 85 if( pCtx->pVal ){ 86 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_DuplicateObj(pCtx->pVal)); 87 }else{ 88 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj("", -1)); 89 } 90 91 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); 92 zResult = Tcl_GetStringResult(p->interp); 93 if( rc!=TCL_OK ){ 94 sqlite3_result_error(ctx, zResult, -1); 95 }else{ 96 sqlite3_result_text(ctx, zResult, -1, SQLITE_TRANSIENT); 97 } 98 99 if( bValue==0 ){ 100 if( pCtx->pVal ) Tcl_DecrRefCount(pCtx->pVal); 101 pCtx->pVal = 0; 102 } 103 } 104 Tcl_DecrRefCount(pEval); 105 } 106 107 static void testWindowStep( 108 sqlite3_context *ctx, 109 int nArg, 110 sqlite3_value **apArg 111 ){ 112 doTestWindowStep(0, ctx, nArg, apArg); 113 } 114 static void testWindowInverse( 115 sqlite3_context *ctx, 116 int nArg, 117 sqlite3_value **apArg 118 ){ 119 doTestWindowStep(1, ctx, nArg, apArg); 120 } 121 122 static void testWindowFinal(sqlite3_context *ctx){ 123 doTestWindowFinalize(0, ctx); 124 } 125 static void testWindowValue(sqlite3_context *ctx){ 126 doTestWindowFinalize(1, ctx); 127 } 128 129 static void testWindowDestroy(void *pCtx){ 130 ckfree(pCtx); 131 } 132 133 /* 134 ** Usage: sqlite3_create_window_function DB NAME XSTEP XFINAL XVALUE XINVERSE 135 */ 136 static int SQLITE_TCLAPI test_create_window( 137 void * clientData, 138 Tcl_Interp *interp, 139 int objc, 140 Tcl_Obj *CONST objv[] 141 ){ 142 TestWindow *pNew; 143 sqlite3 *db; 144 const char *zName; 145 int rc; 146 147 if( objc!=7 ){ 148 Tcl_WrongNumArgs(interp, 1, objv, "DB NAME XSTEP XFINAL XVALUE XINVERSE"); 149 return TCL_ERROR; 150 } 151 152 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 153 zName = Tcl_GetString(objv[2]); 154 pNew = ckalloc(sizeof(TestWindow)); 155 memset(pNew, 0, sizeof(TestWindow)); 156 pNew->xStep = Tcl_DuplicateObj(objv[3]); 157 pNew->xFinal = Tcl_DuplicateObj(objv[4]); 158 pNew->xValue = Tcl_DuplicateObj(objv[5]); 159 pNew->xInverse = Tcl_DuplicateObj(objv[6]); 160 pNew->interp = interp; 161 162 Tcl_IncrRefCount(pNew->xStep); 163 Tcl_IncrRefCount(pNew->xFinal); 164 Tcl_IncrRefCount(pNew->xValue); 165 Tcl_IncrRefCount(pNew->xInverse); 166 167 rc = sqlite3_create_window_function(db, zName, -1, SQLITE_UTF8, (void*)pNew, 168 testWindowStep, testWindowFinal, testWindowValue, testWindowInverse, 169 testWindowDestroy 170 ); 171 if( rc!=SQLITE_OK ){ 172 Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); 173 return TCL_ERROR; 174 } 175 176 return TCL_OK; 177 } 178 179 static int SQLITE_TCLAPI test_create_window_misuse( 180 void * clientData, 181 Tcl_Interp *interp, 182 int objc, 183 Tcl_Obj *CONST objv[] 184 ){ 185 sqlite3 *db; 186 int rc; 187 188 if( objc!=2 ){ 189 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 190 return TCL_ERROR; 191 } 192 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 193 194 rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0, 195 0, testWindowFinal, testWindowValue, testWindowInverse, 196 0 197 ); 198 if( rc!=SQLITE_MISUSE ) goto error; 199 rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0, 200 testWindowStep, 0, testWindowValue, testWindowInverse, 201 0 202 ); 203 if( rc!=SQLITE_MISUSE ) goto error; 204 rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0, 205 testWindowStep, testWindowFinal, 0, testWindowInverse, 206 0 207 ); 208 if( rc!=SQLITE_MISUSE ) goto error; 209 rc = sqlite3_create_window_function(db, "fff", -1, SQLITE_UTF8, 0, 210 testWindowStep, testWindowFinal, testWindowValue, 0, 211 0 212 ); 213 if( rc!=SQLITE_MISUSE ) goto error; 214 215 return TCL_OK; 216 217 error: 218 Tcl_SetObjResult(interp, Tcl_NewStringObj("misuse test error", -1)); 219 return TCL_ERROR; 220 } 221 222 /* 223 ** xStep for sumint(). 224 */ 225 static void sumintStep( 226 sqlite3_context *ctx, 227 int nArg, 228 sqlite3_value *apArg[] 229 ){ 230 sqlite3_int64 *pInt; 231 232 assert( nArg==1 ); 233 if( sqlite3_value_type(apArg[0])!=SQLITE_INTEGER ){ 234 sqlite3_result_error(ctx, "invalid argument", -1); 235 return; 236 } 237 pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64)); 238 if( pInt ){ 239 *pInt += sqlite3_value_int64(apArg[0]); 240 } 241 } 242 243 /* 244 ** xInverse for sumint(). 245 */ 246 static void sumintInverse( 247 sqlite3_context *ctx, 248 int nArg, 249 sqlite3_value *apArg[] 250 ){ 251 sqlite3_int64 *pInt; 252 pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, sizeof(sqlite3_int64)); 253 *pInt -= sqlite3_value_int64(apArg[0]); 254 } 255 256 /* 257 ** xFinal for sumint(). 258 */ 259 static void sumintFinal(sqlite3_context *ctx){ 260 sqlite3_int64 res = 0; 261 sqlite3_int64 *pInt; 262 pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0); 263 if( pInt ) res = *pInt; 264 sqlite3_result_int64(ctx, res); 265 } 266 267 /* 268 ** xValue for sumint(). 269 */ 270 static void sumintValue(sqlite3_context *ctx){ 271 sqlite3_int64 res = 0; 272 sqlite3_int64 *pInt; 273 pInt = (sqlite3_int64*)sqlite3_aggregate_context(ctx, 0); 274 if( pInt ) res = *pInt; 275 sqlite3_result_int64(ctx, res); 276 } 277 278 static int SQLITE_TCLAPI test_create_sumint( 279 void * clientData, 280 Tcl_Interp *interp, 281 int objc, 282 Tcl_Obj *CONST objv[] 283 ){ 284 sqlite3 *db; 285 int rc; 286 287 if( objc!=2 ){ 288 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 289 return TCL_ERROR; 290 } 291 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 292 293 rc = sqlite3_create_window_function(db, "sumint", 1, SQLITE_UTF8, 0, 294 sumintStep, sumintFinal, sumintValue, sumintInverse, 295 0 296 ); 297 298 if( rc!=SQLITE_OK ){ 299 Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); 300 return TCL_ERROR; 301 } 302 return TCL_OK; 303 } 304 305 static int SQLITE_TCLAPI test_override_sum( 306 void * clientData, 307 Tcl_Interp *interp, 308 int objc, 309 Tcl_Obj *CONST objv[] 310 ){ 311 sqlite3 *db; 312 int rc; 313 314 if( objc!=2 ){ 315 Tcl_WrongNumArgs(interp, 1, objv, "DB"); 316 return TCL_ERROR; 317 } 318 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; 319 320 rc = sqlite3_create_function(db, "sum", -1, SQLITE_UTF8, 0, 321 0, sumintStep, sumintFinal 322 ); 323 324 if( rc!=SQLITE_OK ){ 325 Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); 326 return TCL_ERROR; 327 } 328 return TCL_OK; 329 } 330 331 int Sqlitetest_window_Init(Tcl_Interp *interp){ 332 static struct { 333 char *zName; 334 Tcl_ObjCmdProc *xProc; 335 int clientData; 336 } aObjCmd[] = { 337 { "sqlite3_create_window_function", test_create_window, 0 }, 338 { "test_create_window_function_misuse", test_create_window_misuse, 0 }, 339 { "test_create_sumint", test_create_sumint, 0 }, 340 { "test_override_sum", test_override_sum, 0 }, 341 }; 342 int i; 343 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 344 ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData); 345 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); 346 } 347 return TCL_OK; 348 } 349 #endif 350