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