xref: /sqlite-3.40.0/src/test_window.c (revision 5db9a2b3)
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 
doTestWindowStep(int bInverse,sqlite3_context * ctx,int nArg,sqlite3_value ** apArg)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 
doTestWindowFinalize(int bValue,sqlite3_context * ctx)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 
testWindowStep(sqlite3_context * ctx,int nArg,sqlite3_value ** apArg)107 static void testWindowStep(
108   sqlite3_context *ctx,
109   int nArg,
110   sqlite3_value **apArg
111 ){
112   doTestWindowStep(0, ctx, nArg, apArg);
113 }
testWindowInverse(sqlite3_context * ctx,int nArg,sqlite3_value ** apArg)114 static void testWindowInverse(
115   sqlite3_context *ctx,
116   int nArg,
117   sqlite3_value **apArg
118 ){
119   doTestWindowStep(1, ctx, nArg, apArg);
120 }
121 
testWindowFinal(sqlite3_context * ctx)122 static void testWindowFinal(sqlite3_context *ctx){
123   doTestWindowFinalize(0, ctx);
124 }
testWindowValue(sqlite3_context * ctx)125 static void testWindowValue(sqlite3_context *ctx){
126   doTestWindowFinalize(1, ctx);
127 }
128 
testWindowDestroy(void * pCtx)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 */
test_create_window(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 = (TestWindow*)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 
test_create_window_misuse(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 */
sumintStep(sqlite3_context * ctx,int nArg,sqlite3_value * apArg[])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 */
sumintInverse(sqlite3_context * ctx,int nArg,sqlite3_value * apArg[])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 */
sumintFinal(sqlite3_context * ctx)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 */
sumintValue(sqlite3_context * ctx)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 
test_create_sumint(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
test_override_sum(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])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 
Sqlitetest_window_Init(Tcl_Interp * interp)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