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 int Sqlitetest_window_Init(Tcl_Interp *interp){ 223 static struct { 224 char *zName; 225 Tcl_ObjCmdProc *xProc; 226 int clientData; 227 } aObjCmd[] = { 228 { "sqlite3_create_window_function", test_create_window, 0 }, 229 { "test_create_window_function_misuse", test_create_window_misuse, 0 }, 230 }; 231 int i; 232 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ 233 ClientData c = (ClientData)SQLITE_INT_TO_PTR(aObjCmd[i].clientData); 234 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0); 235 } 236 return TCL_OK; 237 } 238 #endif 239