xref: /sqlite-3.40.0/src/test_intarray.c (revision f3cdcdcc)
1522efc62Sdrh /*
2522efc62Sdrh ** 2009 November 10
3522efc62Sdrh **
4522efc62Sdrh ** The author disclaims copyright to this source code.  In place of
5522efc62Sdrh ** a legal notice, here is a blessing:
6522efc62Sdrh **
7522efc62Sdrh **    May you do good and not evil.
8522efc62Sdrh **    May you find forgiveness for yourself and forgive others.
9522efc62Sdrh **    May you share freely, never taking more than you give.
10522efc62Sdrh **
11522efc62Sdrh *************************************************************************
12522efc62Sdrh **
13522efc62Sdrh ** This file implements a read-only VIRTUAL TABLE that contains the
14522efc62Sdrh ** content of a C-language array of integer values.  See the corresponding
15522efc62Sdrh ** header file for full details.
16522efc62Sdrh */
17522efc62Sdrh #include "test_intarray.h"
18522efc62Sdrh #include <string.h>
19522efc62Sdrh #include <assert.h>
20522efc62Sdrh 
21522efc62Sdrh 
22522efc62Sdrh /*
23522efc62Sdrh ** Definition of the sqlite3_intarray object.
24522efc62Sdrh **
25522efc62Sdrh ** The internal representation of an intarray object is subject
26522efc62Sdrh ** to change, is not externally visible, and should be used by
27522efc62Sdrh ** the implementation of intarray only.  This object is opaque
28522efc62Sdrh ** to users.
29522efc62Sdrh */
30522efc62Sdrh struct sqlite3_intarray {
31522efc62Sdrh   int n;                    /* Number of elements in the array */
32522efc62Sdrh   sqlite3_int64 *a;         /* Contents of the array */
33522efc62Sdrh   void (*xFree)(void*);     /* Function used to free a[] */
34522efc62Sdrh };
35522efc62Sdrh 
36522efc62Sdrh /* Objects used internally by the virtual table implementation */
37522efc62Sdrh typedef struct intarray_vtab intarray_vtab;
38522efc62Sdrh typedef struct intarray_cursor intarray_cursor;
39522efc62Sdrh 
4060ec914cSpeter.d.reid /* An intarray table object */
41522efc62Sdrh struct intarray_vtab {
42522efc62Sdrh   sqlite3_vtab base;            /* Base class */
43522efc62Sdrh   sqlite3_intarray *pContent;   /* Content of the integer array */
44522efc62Sdrh };
45522efc62Sdrh 
4660ec914cSpeter.d.reid /* An intarray cursor object */
47522efc62Sdrh struct intarray_cursor {
48522efc62Sdrh   sqlite3_vtab_cursor base;    /* Base class */
49522efc62Sdrh   int i;                       /* Current cursor position */
50522efc62Sdrh };
51522efc62Sdrh 
52522efc62Sdrh /*
53522efc62Sdrh ** None of this works unless we have virtual tables.
54522efc62Sdrh */
55522efc62Sdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
56522efc62Sdrh 
57522efc62Sdrh /*
5863b38789Sdrh ** Free an sqlite3_intarray object.
59522efc62Sdrh */
60522efc62Sdrh static void intarrayFree(sqlite3_intarray *p){
61522efc62Sdrh   if( p->xFree ){
62522efc62Sdrh     p->xFree(p->a);
63522efc62Sdrh   }
64522efc62Sdrh   sqlite3_free(p);
65522efc62Sdrh }
66522efc62Sdrh 
67522efc62Sdrh /*
68522efc62Sdrh ** Table destructor for the intarray module.
69522efc62Sdrh */
70522efc62Sdrh static int intarrayDestroy(sqlite3_vtab *p){
71522efc62Sdrh   intarray_vtab *pVtab = (intarray_vtab*)p;
72522efc62Sdrh   sqlite3_free(pVtab);
73522efc62Sdrh   return 0;
74522efc62Sdrh }
75522efc62Sdrh 
76522efc62Sdrh /*
77522efc62Sdrh ** Table constructor for the intarray module.
78522efc62Sdrh */
79522efc62Sdrh static int intarrayCreate(
80522efc62Sdrh   sqlite3 *db,              /* Database where module is created */
81522efc62Sdrh   void *pAux,               /* clientdata for the module */
82522efc62Sdrh   int argc,                 /* Number of arguments */
83522efc62Sdrh   const char *const*argv,   /* Value for all arguments */
84522efc62Sdrh   sqlite3_vtab **ppVtab,    /* Write the new virtual table object here */
85522efc62Sdrh   char **pzErr              /* Put error message text here */
86522efc62Sdrh ){
87522efc62Sdrh   int rc = SQLITE_NOMEM;
88*f3cdcdccSdrh   intarray_vtab *pVtab = sqlite3_malloc64(sizeof(intarray_vtab));
89522efc62Sdrh 
90522efc62Sdrh   if( pVtab ){
91522efc62Sdrh     memset(pVtab, 0, sizeof(intarray_vtab));
92522efc62Sdrh     pVtab->pContent = (sqlite3_intarray*)pAux;
93522efc62Sdrh     rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)");
94522efc62Sdrh   }
95522efc62Sdrh   *ppVtab = (sqlite3_vtab *)pVtab;
96522efc62Sdrh   return rc;
97522efc62Sdrh }
98522efc62Sdrh 
99522efc62Sdrh /*
100522efc62Sdrh ** Open a new cursor on the intarray table.
101522efc62Sdrh */
102522efc62Sdrh static int intarrayOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
103522efc62Sdrh   int rc = SQLITE_NOMEM;
104522efc62Sdrh   intarray_cursor *pCur;
105*f3cdcdccSdrh   pCur = sqlite3_malloc64(sizeof(intarray_cursor));
106522efc62Sdrh   if( pCur ){
107522efc62Sdrh     memset(pCur, 0, sizeof(intarray_cursor));
108522efc62Sdrh     *ppCursor = (sqlite3_vtab_cursor *)pCur;
109522efc62Sdrh     rc = SQLITE_OK;
110522efc62Sdrh   }
111522efc62Sdrh   return rc;
112522efc62Sdrh }
113522efc62Sdrh 
114522efc62Sdrh /*
115522efc62Sdrh ** Close a intarray table cursor.
116522efc62Sdrh */
117522efc62Sdrh static int intarrayClose(sqlite3_vtab_cursor *cur){
118522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor *)cur;
119522efc62Sdrh   sqlite3_free(pCur);
120522efc62Sdrh   return SQLITE_OK;
121522efc62Sdrh }
122522efc62Sdrh 
123522efc62Sdrh /*
124522efc62Sdrh ** Retrieve a column of data.
125522efc62Sdrh */
126522efc62Sdrh static int intarrayColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
127522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor*)cur;
128522efc62Sdrh   intarray_vtab *pVtab = (intarray_vtab*)cur->pVtab;
129522efc62Sdrh   if( pCur->i>=0 && pCur->i<pVtab->pContent->n ){
130522efc62Sdrh     sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]);
131522efc62Sdrh   }
132522efc62Sdrh   return SQLITE_OK;
133522efc62Sdrh }
134522efc62Sdrh 
135522efc62Sdrh /*
136522efc62Sdrh ** Retrieve the current rowid.
137522efc62Sdrh */
138522efc62Sdrh static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
139522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor *)cur;
140522efc62Sdrh   *pRowid = pCur->i;
141522efc62Sdrh   return SQLITE_OK;
142522efc62Sdrh }
143522efc62Sdrh 
144522efc62Sdrh static int intarrayEof(sqlite3_vtab_cursor *cur){
145522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor *)cur;
146522efc62Sdrh   intarray_vtab *pVtab = (intarray_vtab *)cur->pVtab;
147522efc62Sdrh   return pCur->i>=pVtab->pContent->n;
148522efc62Sdrh }
149522efc62Sdrh 
150522efc62Sdrh /*
151522efc62Sdrh ** Advance the cursor to the next row.
152522efc62Sdrh */
153522efc62Sdrh static int intarrayNext(sqlite3_vtab_cursor *cur){
154522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor *)cur;
155522efc62Sdrh   pCur->i++;
156522efc62Sdrh   return SQLITE_OK;
157522efc62Sdrh }
158522efc62Sdrh 
159522efc62Sdrh /*
160522efc62Sdrh ** Reset a intarray table cursor.
161522efc62Sdrh */
162522efc62Sdrh static int intarrayFilter(
163522efc62Sdrh   sqlite3_vtab_cursor *pVtabCursor,
164522efc62Sdrh   int idxNum, const char *idxStr,
165522efc62Sdrh   int argc, sqlite3_value **argv
166522efc62Sdrh ){
167522efc62Sdrh   intarray_cursor *pCur = (intarray_cursor *)pVtabCursor;
168522efc62Sdrh   pCur->i = 0;
169522efc62Sdrh   return SQLITE_OK;
170522efc62Sdrh }
171522efc62Sdrh 
172522efc62Sdrh /*
173522efc62Sdrh ** Analyse the WHERE condition.
174522efc62Sdrh */
175522efc62Sdrh static int intarrayBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
176522efc62Sdrh   return SQLITE_OK;
177522efc62Sdrh }
178522efc62Sdrh 
179522efc62Sdrh /*
180522efc62Sdrh ** A virtual table module that merely echos method calls into TCL
181522efc62Sdrh ** variables.
182522efc62Sdrh */
183522efc62Sdrh static sqlite3_module intarrayModule = {
184522efc62Sdrh   0,                           /* iVersion */
185522efc62Sdrh   intarrayCreate,              /* xCreate - create a new virtual table */
186522efc62Sdrh   intarrayCreate,              /* xConnect - connect to an existing vtab */
187522efc62Sdrh   intarrayBestIndex,           /* xBestIndex - find the best query index */
188522efc62Sdrh   intarrayDestroy,             /* xDisconnect - disconnect a vtab */
189522efc62Sdrh   intarrayDestroy,             /* xDestroy - destroy a vtab */
190522efc62Sdrh   intarrayOpen,                /* xOpen - open a cursor */
191522efc62Sdrh   intarrayClose,               /* xClose - close a cursor */
192522efc62Sdrh   intarrayFilter,              /* xFilter - configure scan constraints */
193522efc62Sdrh   intarrayNext,                /* xNext - advance a cursor */
194522efc62Sdrh   intarrayEof,                 /* xEof */
195522efc62Sdrh   intarrayColumn,              /* xColumn - read data */
196522efc62Sdrh   intarrayRowid,               /* xRowid - read data */
197522efc62Sdrh   0,                           /* xUpdate */
198522efc62Sdrh   0,                           /* xBegin */
199522efc62Sdrh   0,                           /* xSync */
200522efc62Sdrh   0,                           /* xCommit */
201522efc62Sdrh   0,                           /* xRollback */
202522efc62Sdrh   0,                           /* xFindMethod */
203522efc62Sdrh   0,                           /* xRename */
204522efc62Sdrh };
205522efc62Sdrh 
206522efc62Sdrh #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
207522efc62Sdrh 
208522efc62Sdrh /*
209522efc62Sdrh ** Invoke this routine to create a specific instance of an intarray object.
210522efc62Sdrh ** The new intarray object is returned by the 3rd parameter.
211522efc62Sdrh **
212522efc62Sdrh ** Each intarray object corresponds to a virtual table in the TEMP table
213522efc62Sdrh ** with a name of zName.
214522efc62Sdrh **
215522efc62Sdrh ** Destroy the intarray object by dropping the virtual table.  If not done
216522efc62Sdrh ** explicitly by the application, the virtual table will be dropped implicitly
217522efc62Sdrh ** by the system when the database connection is closed.
218522efc62Sdrh */
219fb90841aSdrh SQLITE_API int sqlite3_intarray_create(
220522efc62Sdrh   sqlite3 *db,
221522efc62Sdrh   const char *zName,
222522efc62Sdrh   sqlite3_intarray **ppReturn
223522efc62Sdrh ){
2245c03f30aSdrh   int rc = SQLITE_OK;
2255c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
226522efc62Sdrh   sqlite3_intarray *p;
227522efc62Sdrh 
228*f3cdcdccSdrh   *ppReturn = p = sqlite3_malloc64( sizeof(*p) );
229522efc62Sdrh   if( p==0 ){
230522efc62Sdrh     return SQLITE_NOMEM;
231522efc62Sdrh   }
232522efc62Sdrh   memset(p, 0, sizeof(*p));
233522efc62Sdrh   rc = sqlite3_create_module_v2(db, zName, &intarrayModule, p,
234522efc62Sdrh                                 (void(*)(void*))intarrayFree);
235522efc62Sdrh   if( rc==SQLITE_OK ){
236522efc62Sdrh     char *zSql;
237522efc62Sdrh     zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q",
238522efc62Sdrh                            zName, zName);
239522efc62Sdrh     rc = sqlite3_exec(db, zSql, 0, 0, 0);
240522efc62Sdrh     sqlite3_free(zSql);
241522efc62Sdrh   }
2425c03f30aSdrh #endif
243522efc62Sdrh   return rc;
244522efc62Sdrh }
245522efc62Sdrh 
246522efc62Sdrh /*
247522efc62Sdrh ** Bind a new array array of integers to a specific intarray object.
248522efc62Sdrh **
249522efc62Sdrh ** The array of integers bound must be unchanged for the duration of
250522efc62Sdrh ** any query against the corresponding virtual table.  If the integer
251522efc62Sdrh ** array does change or is deallocated undefined behavior will result.
252522efc62Sdrh */
253fb90841aSdrh SQLITE_API int sqlite3_intarray_bind(
254522efc62Sdrh   sqlite3_intarray *pIntArray,   /* The intarray object to bind to */
255522efc62Sdrh   int nElements,                 /* Number of elements in the intarray */
256522efc62Sdrh   sqlite3_int64 *aElements,      /* Content of the intarray */
257522efc62Sdrh   void (*xFree)(void*)           /* How to dispose of the intarray when done */
258522efc62Sdrh ){
259522efc62Sdrh   if( pIntArray->xFree ){
260522efc62Sdrh     pIntArray->xFree(pIntArray->a);
261522efc62Sdrh   }
262522efc62Sdrh   pIntArray->n = nElements;
263522efc62Sdrh   pIntArray->a = aElements;
264522efc62Sdrh   pIntArray->xFree = xFree;
265522efc62Sdrh   return SQLITE_OK;
266522efc62Sdrh }
267522efc62Sdrh 
268522efc62Sdrh 
269522efc62Sdrh /*****************************************************************************
270522efc62Sdrh ** Everything below is interface for testing this module.
271522efc62Sdrh */
272522efc62Sdrh #ifdef SQLITE_TEST
273522efc62Sdrh #include <tcl.h>
274522efc62Sdrh 
275522efc62Sdrh /*
276522efc62Sdrh ** Routines to encode and decode pointers
277522efc62Sdrh */
278522efc62Sdrh extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
279d8267b89Sdrh extern void *sqlite3TestTextToPtr(const char*);
280522efc62Sdrh extern int sqlite3TestMakePointerStr(Tcl_Interp*, char *zPtr, void*);
281e84d8d32Smistachkin extern const char *sqlite3ErrName(int);
282522efc62Sdrh 
283522efc62Sdrh /*
284522efc62Sdrh **    sqlite3_intarray_create  DB  NAME
285522efc62Sdrh **
286522efc62Sdrh ** Invoke the sqlite3_intarray_create interface.  A string that becomes
287522efc62Sdrh ** the first parameter to sqlite3_intarray_bind.
288522efc62Sdrh */
289522efc62Sdrh static int test_intarray_create(
290522efc62Sdrh   ClientData clientData, /* Not used */
291522efc62Sdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
292522efc62Sdrh   int objc,              /* Number of arguments */
293522efc62Sdrh   Tcl_Obj *CONST objv[]  /* Command arguments */
294522efc62Sdrh ){
295522efc62Sdrh   sqlite3 *db;
296522efc62Sdrh   const char *zName;
297522efc62Sdrh   sqlite3_intarray *pArray;
2985c03f30aSdrh   int rc = SQLITE_OK;
299522efc62Sdrh   char zPtr[100];
300522efc62Sdrh 
301522efc62Sdrh   if( objc!=3 ){
302522efc62Sdrh     Tcl_WrongNumArgs(interp, 1, objv, "DB");
303522efc62Sdrh     return TCL_ERROR;
304522efc62Sdrh   }
305522efc62Sdrh   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
306522efc62Sdrh   zName = Tcl_GetString(objv[2]);
3075c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
308522efc62Sdrh   rc = sqlite3_intarray_create(db, zName, &pArray);
3095c03f30aSdrh #endif
310522efc62Sdrh   if( rc!=SQLITE_OK ){
311522efc62Sdrh     assert( pArray==0 );
312e84d8d32Smistachkin     Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
313522efc62Sdrh     return TCL_ERROR;
314522efc62Sdrh   }
315522efc62Sdrh   sqlite3TestMakePointerStr(interp, zPtr, pArray);
316522efc62Sdrh   Tcl_AppendResult(interp, zPtr, (char*)0);
317522efc62Sdrh   return TCL_OK;
318522efc62Sdrh }
319522efc62Sdrh 
320522efc62Sdrh /*
321522efc62Sdrh **    sqlite3_intarray_bind  INTARRAY  ?VALUE ...?
322522efc62Sdrh **
323522efc62Sdrh ** Invoke the sqlite3_intarray_bind interface on the given array of integers.
324522efc62Sdrh */
325522efc62Sdrh static int test_intarray_bind(
326522efc62Sdrh   ClientData clientData, /* Not used */
327522efc62Sdrh   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
328522efc62Sdrh   int objc,              /* Number of arguments */
329522efc62Sdrh   Tcl_Obj *CONST objv[]  /* Command arguments */
330522efc62Sdrh ){
331522efc62Sdrh   sqlite3_intarray *pArray;
3325c03f30aSdrh   int rc = SQLITE_OK;
333522efc62Sdrh   int i, n;
334522efc62Sdrh   sqlite3_int64 *a;
335522efc62Sdrh 
336522efc62Sdrh   if( objc<2 ){
337522efc62Sdrh     Tcl_WrongNumArgs(interp, 1, objv, "INTARRAY");
338522efc62Sdrh     return TCL_ERROR;
339522efc62Sdrh   }
340522efc62Sdrh   pArray = (sqlite3_intarray*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
341522efc62Sdrh   n = objc - 2;
3425c03f30aSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
343*f3cdcdccSdrh   a = sqlite3_malloc64( sizeof(a[0])*n );
344522efc62Sdrh   if( a==0 ){
345522efc62Sdrh     Tcl_AppendResult(interp, "SQLITE_NOMEM", (char*)0);
346522efc62Sdrh     return TCL_ERROR;
347522efc62Sdrh   }
348522efc62Sdrh   for(i=0; i<n; i++){
349b3f787f4Sdrh     Tcl_WideInt x = 0;
350b3f787f4Sdrh     Tcl_GetWideIntFromObj(0, objv[i+2], &x);
351b3f787f4Sdrh     a[i] = x;
352522efc62Sdrh   }
353522efc62Sdrh   rc = sqlite3_intarray_bind(pArray, n, a, sqlite3_free);
354522efc62Sdrh   if( rc!=SQLITE_OK ){
355e84d8d32Smistachkin     Tcl_AppendResult(interp, sqlite3ErrName(rc), (char*)0);
356522efc62Sdrh     return TCL_ERROR;
357522efc62Sdrh   }
3585c03f30aSdrh #endif
359522efc62Sdrh   return TCL_OK;
360522efc62Sdrh }
361522efc62Sdrh 
362522efc62Sdrh /*
363522efc62Sdrh ** Register commands with the TCL interpreter.
364522efc62Sdrh */
365522efc62Sdrh int Sqlitetestintarray_Init(Tcl_Interp *interp){
366522efc62Sdrh   static struct {
367522efc62Sdrh      char *zName;
368522efc62Sdrh      Tcl_ObjCmdProc *xProc;
369522efc62Sdrh      void *clientData;
370522efc62Sdrh   } aObjCmd[] = {
371522efc62Sdrh      { "sqlite3_intarray_create", test_intarray_create, 0 },
372522efc62Sdrh      { "sqlite3_intarray_bind", test_intarray_bind, 0 },
373522efc62Sdrh   };
374522efc62Sdrh   int i;
375522efc62Sdrh   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
376522efc62Sdrh     Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
377522efc62Sdrh         aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
378522efc62Sdrh   }
379522efc62Sdrh   return TCL_OK;
380522efc62Sdrh }
381522efc62Sdrh 
382522efc62Sdrh #endif /* SQLITE_TEST */
383