xref: /sqlite-3.40.0/src/test_window.c (revision f87e10c7)
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