14be8b51eSdrh /*
24be8b51eSdrh ** 2006 June 13
34be8b51eSdrh **
44be8b51eSdrh ** The author disclaims copyright to this source code. In place of
54be8b51eSdrh ** a legal notice, here is a blessing:
64be8b51eSdrh **
74be8b51eSdrh ** May you do good and not evil.
84be8b51eSdrh ** May you find forgiveness for yourself and forgive others.
94be8b51eSdrh ** May you share freely, never taking more than you give.
104be8b51eSdrh **
114be8b51eSdrh *************************************************************************
124be8b51eSdrh ** Code for testing the virtual table interfaces. This code
134be8b51eSdrh ** is not included in the SQLite library. It is used for automated
144be8b51eSdrh ** testing of the SQLite library.
154be8b51eSdrh **
164be8b51eSdrh ** The emphasis of this file is a virtual table that provides
174be8b51eSdrh ** access to TCL variables.
18*4dd176eaSdrh **
19*4dd176eaSdrh ** The TCLVAR eponymous virtual table has a schema like this:
20*4dd176eaSdrh **
21*4dd176eaSdrh ** CREATE TABLE tclvar(
22*4dd176eaSdrh ** name TEXT, -- base name of the variable: "x" in "$x(y)"
23*4dd176eaSdrh ** arrayname TEXT, -- array index name: "y" in "$x(y)"
24*4dd176eaSdrh ** value TEXT, -- the value of the variable
25*4dd176eaSdrh ** fullname TEXT, -- the full name of the variable
26*4dd176eaSdrh ** PRIMARY KEY(fullname)
27*4dd176eaSdrh ** ) WITHOUT ROWID;
28*4dd176eaSdrh **
29*4dd176eaSdrh ** DELETE, INSERT, and UPDATE operations use the "fullname" field to
30*4dd176eaSdrh ** determine the variable to be modified. Changing "value" to NULL
31*4dd176eaSdrh ** deletes the variable.
32*4dd176eaSdrh **
33*4dd176eaSdrh ** For SELECT operations, the "name" and "arrayname" fields will always
34*4dd176eaSdrh ** match the "fullname" field. For DELETE, INSERT, and UPDATE, the
35*4dd176eaSdrh ** "name" and "arrayname" fields are ignored and the variable is modified
36*4dd176eaSdrh ** according to "fullname" and "value" only.
374be8b51eSdrh */
384be8b51eSdrh #include "sqliteInt.h"
3952b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
4052b1dbb5Smistachkin # include "sqlite_tcl.h"
4152b1dbb5Smistachkin #else
424be8b51eSdrh # include "tcl.h"
4352b1dbb5Smistachkin #endif
444be8b51eSdrh #include <stdlib.h>
454be8b51eSdrh #include <string.h>
464be8b51eSdrh
47169f8a0cSdanielk1977 #ifndef SQLITE_OMIT_VIRTUALTABLE
48169f8a0cSdanielk1977
4943970dd7Sdan /*
5043970dd7Sdan ** Characters that make up the idxStr created by xBestIndex for xFilter.
5143970dd7Sdan */
5243970dd7Sdan #define TCLVAR_NAME_EQ 'e'
5343970dd7Sdan #define TCLVAR_NAME_MATCH 'm'
5443970dd7Sdan #define TCLVAR_VALUE_GLOB 'g'
5543970dd7Sdan #define TCLVAR_VALUE_REGEXP 'r'
5643970dd7Sdan #define TCLVAR_VALUE_LIKE 'l'
5743970dd7Sdan
584be8b51eSdrh typedef struct tclvar_vtab tclvar_vtab;
594be8b51eSdrh typedef struct tclvar_cursor tclvar_cursor;
604be8b51eSdrh
614be8b51eSdrh /*
624be8b51eSdrh ** A tclvar virtual-table object
634be8b51eSdrh */
644be8b51eSdrh struct tclvar_vtab {
654be8b51eSdrh sqlite3_vtab base;
664be8b51eSdrh Tcl_Interp *interp;
674be8b51eSdrh };
684be8b51eSdrh
694be8b51eSdrh /* A tclvar cursor object */
704be8b51eSdrh struct tclvar_cursor {
714be8b51eSdrh sqlite3_vtab_cursor base;
72169f8a0cSdanielk1977
73169f8a0cSdanielk1977 Tcl_Obj *pList1; /* Result of [info vars ?pattern?] */
74169f8a0cSdanielk1977 Tcl_Obj *pList2; /* Result of [array names [lindex $pList1 $i1]] */
75169f8a0cSdanielk1977 int i1; /* Current item in pList1 */
76169f8a0cSdanielk1977 int i2; /* Current item (if any) in pList2 */
774be8b51eSdrh };
784be8b51eSdrh
794be8b51eSdrh /* Methods for the tclvar module */
tclvarConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)804be8b51eSdrh static int tclvarConnect(
814be8b51eSdrh sqlite3 *db,
829da9d471Sdanielk1977 void *pAux,
83e4102960Sdrh int argc, const char *const*argv,
844ca8aac2Sdrh sqlite3_vtab **ppVtab,
854ca8aac2Sdrh char **pzErr
864be8b51eSdrh ){
874be8b51eSdrh tclvar_vtab *pVtab;
884be8b51eSdrh static const char zSchema[] =
89*4dd176eaSdrh "CREATE TABLE x("
90*4dd176eaSdrh " name TEXT," /* Base name */
91*4dd176eaSdrh " arrayname TEXT," /* Array index */
92*4dd176eaSdrh " value TEXT," /* Value */
93*4dd176eaSdrh " fullname TEXT PRIMARY KEY" /* base(index) name */
94*4dd176eaSdrh ") WITHOUT ROWID";
9531dad9daSdanielk1977 pVtab = sqlite3MallocZero( sizeof(*pVtab) );
964be8b51eSdrh if( pVtab==0 ) return SQLITE_NOMEM;
974be8b51eSdrh *ppVtab = &pVtab->base;
989da9d471Sdanielk1977 pVtab->interp = (Tcl_Interp *)pAux;
994be8b51eSdrh sqlite3_declare_vtab(db, zSchema);
1004be8b51eSdrh return SQLITE_OK;
1014be8b51eSdrh }
1024be8b51eSdrh /* Note that for this virtual table, the xCreate and xConnect
1034be8b51eSdrh ** methods are identical. */
104169f8a0cSdanielk1977
tclvarDisconnect(sqlite3_vtab * pVtab)1054be8b51eSdrh static int tclvarDisconnect(sqlite3_vtab *pVtab){
10617435752Sdrh sqlite3_free(pVtab);
107d1ab1ba5Sdanielk1977 return SQLITE_OK;
1084be8b51eSdrh }
1094be8b51eSdrh /* The xDisconnect and xDestroy methods are also the same */
1104be8b51eSdrh
111169f8a0cSdanielk1977 /*
112169f8a0cSdanielk1977 ** Open a new tclvar cursor.
113169f8a0cSdanielk1977 */
tclvarOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)1144be8b51eSdrh static int tclvarOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
1154be8b51eSdrh tclvar_cursor *pCur;
11631dad9daSdanielk1977 pCur = sqlite3MallocZero(sizeof(tclvar_cursor));
1174be8b51eSdrh *ppCursor = &pCur->base;
1184be8b51eSdrh return SQLITE_OK;
1194be8b51eSdrh }
1204be8b51eSdrh
121169f8a0cSdanielk1977 /*
122169f8a0cSdanielk1977 ** Close a tclvar cursor.
123169f8a0cSdanielk1977 */
tclvarClose(sqlite3_vtab_cursor * cur)1244be8b51eSdrh static int tclvarClose(sqlite3_vtab_cursor *cur){
1254be8b51eSdrh tclvar_cursor *pCur = (tclvar_cursor *)cur;
1264be8b51eSdrh if( pCur->pList1 ){
1274be8b51eSdrh Tcl_DecrRefCount(pCur->pList1);
1284be8b51eSdrh }
1294be8b51eSdrh if( pCur->pList2 ){
1304be8b51eSdrh Tcl_DecrRefCount(pCur->pList2);
1314be8b51eSdrh }
13217435752Sdrh sqlite3_free(pCur);
1334be8b51eSdrh return SQLITE_OK;
1344be8b51eSdrh }
1354be8b51eSdrh
136169f8a0cSdanielk1977 /*
137169f8a0cSdanielk1977 ** Returns 1 if data is ready, or 0 if not.
138169f8a0cSdanielk1977 */
next2(Tcl_Interp * interp,tclvar_cursor * pCur,Tcl_Obj * pObj)139169f8a0cSdanielk1977 static int next2(Tcl_Interp *interp, tclvar_cursor *pCur, Tcl_Obj *pObj){
140169f8a0cSdanielk1977 Tcl_Obj *p;
141169f8a0cSdanielk1977
142169f8a0cSdanielk1977 if( pObj ){
143169f8a0cSdanielk1977 if( !pCur->pList2 ){
144169f8a0cSdanielk1977 p = Tcl_NewStringObj("array names", -1);
145169f8a0cSdanielk1977 Tcl_IncrRefCount(p);
146169f8a0cSdanielk1977 Tcl_ListObjAppendElement(0, p, pObj);
147169f8a0cSdanielk1977 Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
148169f8a0cSdanielk1977 Tcl_DecrRefCount(p);
149169f8a0cSdanielk1977 pCur->pList2 = Tcl_GetObjResult(interp);
150169f8a0cSdanielk1977 Tcl_IncrRefCount(pCur->pList2);
151169f8a0cSdanielk1977 assert( pCur->i2==0 );
152169f8a0cSdanielk1977 }else{
153169f8a0cSdanielk1977 int n = 0;
154169f8a0cSdanielk1977 pCur->i2++;
155169f8a0cSdanielk1977 Tcl_ListObjLength(0, pCur->pList2, &n);
156169f8a0cSdanielk1977 if( pCur->i2>=n ){
157169f8a0cSdanielk1977 Tcl_DecrRefCount(pCur->pList2);
158169f8a0cSdanielk1977 pCur->pList2 = 0;
159169f8a0cSdanielk1977 pCur->i2 = 0;
1604be8b51eSdrh return 0;
1614be8b51eSdrh }
162169f8a0cSdanielk1977 }
1634be8b51eSdrh }
1644be8b51eSdrh
165169f8a0cSdanielk1977 return 1;
166169f8a0cSdanielk1977 }
167169f8a0cSdanielk1977
tclvarNext(sqlite3_vtab_cursor * cur)168169f8a0cSdanielk1977 static int tclvarNext(sqlite3_vtab_cursor *cur){
169169f8a0cSdanielk1977 Tcl_Obj *pObj;
170169f8a0cSdanielk1977 int n = 0;
171169f8a0cSdanielk1977 int ok = 0;
172169f8a0cSdanielk1977
1734be8b51eSdrh tclvar_cursor *pCur = (tclvar_cursor *)cur;
174169f8a0cSdanielk1977 Tcl_Interp *interp = ((tclvar_vtab *)(cur->pVtab))->interp;
175169f8a0cSdanielk1977
176169f8a0cSdanielk1977 Tcl_ListObjLength(0, pCur->pList1, &n);
177169f8a0cSdanielk1977 while( !ok && pCur->i1<n ){
178169f8a0cSdanielk1977 Tcl_ListObjIndex(0, pCur->pList1, pCur->i1, &pObj);
179169f8a0cSdanielk1977 ok = next2(interp, pCur, pObj);
180169f8a0cSdanielk1977 if( !ok ){
181169f8a0cSdanielk1977 pCur->i1++;
182169f8a0cSdanielk1977 }
183169f8a0cSdanielk1977 }
184169f8a0cSdanielk1977
185169f8a0cSdanielk1977 return 0;
1864be8b51eSdrh }
1874be8b51eSdrh
tclvarFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)1884be8b51eSdrh static int tclvarFilter(
1894be8b51eSdrh sqlite3_vtab_cursor *pVtabCursor,
1904be8b51eSdrh int idxNum, const char *idxStr,
1914be8b51eSdrh int argc, sqlite3_value **argv
1924be8b51eSdrh ){
1934be8b51eSdrh tclvar_cursor *pCur = (tclvar_cursor *)pVtabCursor;
194169f8a0cSdanielk1977 Tcl_Interp *interp = ((tclvar_vtab *)(pVtabCursor->pVtab))->interp;
19543970dd7Sdan Tcl_Obj *p = Tcl_NewStringObj("tclvar_filter_cmd", -1);
196169f8a0cSdanielk1977
19743970dd7Sdan const char *zEq = "";
19843970dd7Sdan const char *zMatch = "";
19943970dd7Sdan const char *zGlob = "";
20043970dd7Sdan const char *zRegexp = "";
20143970dd7Sdan const char *zLike = "";
20243970dd7Sdan int i;
203169f8a0cSdanielk1977
20443970dd7Sdan for(i=0; idxStr[i]; i++){
20543970dd7Sdan switch( idxStr[i] ){
20643970dd7Sdan case TCLVAR_NAME_EQ:
207060f9a4aSdrh zEq = (const char*)sqlite3_value_text(argv[i]);
20843970dd7Sdan break;
20943970dd7Sdan case TCLVAR_NAME_MATCH:
210060f9a4aSdrh zMatch = (const char*)sqlite3_value_text(argv[i]);
21143970dd7Sdan break;
21243970dd7Sdan case TCLVAR_VALUE_GLOB:
213060f9a4aSdrh zGlob = (const char*)sqlite3_value_text(argv[i]);
21443970dd7Sdan break;
21543970dd7Sdan case TCLVAR_VALUE_REGEXP:
216060f9a4aSdrh zRegexp = (const char*)sqlite3_value_text(argv[i]);
21743970dd7Sdan break;
21843970dd7Sdan case TCLVAR_VALUE_LIKE:
219060f9a4aSdrh zLike = (const char*)sqlite3_value_text(argv[i]);
22043970dd7Sdan break;
22143970dd7Sdan default:
22243970dd7Sdan assert( 0 );
223169f8a0cSdanielk1977 }
22443970dd7Sdan }
22543970dd7Sdan
22643970dd7Sdan Tcl_IncrRefCount(p);
22743970dd7Sdan Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zEq, -1));
22843970dd7Sdan Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zMatch, -1));
22943970dd7Sdan Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zGlob, -1));
23043970dd7Sdan Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zRegexp, -1));
23143970dd7Sdan Tcl_ListObjAppendElement(0, p, Tcl_NewStringObj(zLike, -1));
23243970dd7Sdan
233169f8a0cSdanielk1977 Tcl_EvalObjEx(interp, p, TCL_EVAL_GLOBAL);
234a62bb8d4Sdrh if( pCur->pList1 ){
235a62bb8d4Sdrh Tcl_DecrRefCount(pCur->pList1);
236a62bb8d4Sdrh }
237a62bb8d4Sdrh if( pCur->pList2 ){
238a62bb8d4Sdrh Tcl_DecrRefCount(pCur->pList2);
239a62bb8d4Sdrh pCur->pList2 = 0;
240a62bb8d4Sdrh }
241a62bb8d4Sdrh pCur->i1 = 0;
242a62bb8d4Sdrh pCur->i2 = 0;
243169f8a0cSdanielk1977 pCur->pList1 = Tcl_GetObjResult(interp);
244169f8a0cSdanielk1977 Tcl_IncrRefCount(pCur->pList1);
245169f8a0cSdanielk1977
246169f8a0cSdanielk1977 Tcl_DecrRefCount(p);
247169f8a0cSdanielk1977 return tclvarNext(pVtabCursor);
2484be8b51eSdrh }
2494be8b51eSdrh
tclvarColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)250169f8a0cSdanielk1977 static int tclvarColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
251169f8a0cSdanielk1977 Tcl_Obj *p1;
252169f8a0cSdanielk1977 Tcl_Obj *p2;
253169f8a0cSdanielk1977 const char *z1;
254169f8a0cSdanielk1977 const char *z2 = "";
255169f8a0cSdanielk1977 tclvar_cursor *pCur = (tclvar_cursor*)cur;
256169f8a0cSdanielk1977 Tcl_Interp *interp = ((tclvar_vtab *)cur->pVtab)->interp;
257169f8a0cSdanielk1977
258169f8a0cSdanielk1977 Tcl_ListObjIndex(interp, pCur->pList1, pCur->i1, &p1);
259169f8a0cSdanielk1977 Tcl_ListObjIndex(interp, pCur->pList2, pCur->i2, &p2);
260169f8a0cSdanielk1977 z1 = Tcl_GetString(p1);
261169f8a0cSdanielk1977 if( p2 ){
262169f8a0cSdanielk1977 z2 = Tcl_GetString(p2);
263169f8a0cSdanielk1977 }
264169f8a0cSdanielk1977 switch (i) {
265169f8a0cSdanielk1977 case 0: {
266169f8a0cSdanielk1977 sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
267169f8a0cSdanielk1977 break;
268169f8a0cSdanielk1977 }
269169f8a0cSdanielk1977 case 1: {
270169f8a0cSdanielk1977 sqlite3_result_text(ctx, z2, -1, SQLITE_TRANSIENT);
271169f8a0cSdanielk1977 break;
272169f8a0cSdanielk1977 }
273169f8a0cSdanielk1977 case 2: {
274169f8a0cSdanielk1977 Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY);
275169f8a0cSdanielk1977 sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT);
276169f8a0cSdanielk1977 break;
277169f8a0cSdanielk1977 }
278*4dd176eaSdrh case 3: {
279*4dd176eaSdrh char *z3;
280*4dd176eaSdrh if( p2 ){
281*4dd176eaSdrh z3 = sqlite3_mprintf("%s(%s)", z1, z2);
282*4dd176eaSdrh sqlite3_result_text(ctx, z3, -1, sqlite3_free);
283*4dd176eaSdrh }else{
284*4dd176eaSdrh sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT);
285*4dd176eaSdrh }
286*4dd176eaSdrh break;
287*4dd176eaSdrh }
288169f8a0cSdanielk1977 }
289169f8a0cSdanielk1977 return SQLITE_OK;
290169f8a0cSdanielk1977 }
291169f8a0cSdanielk1977
tclvarRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)292169f8a0cSdanielk1977 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
293169f8a0cSdanielk1977 *pRowid = 0;
294169f8a0cSdanielk1977 return SQLITE_OK;
295169f8a0cSdanielk1977 }
296169f8a0cSdanielk1977
tclvarEof(sqlite3_vtab_cursor * cur)297169f8a0cSdanielk1977 static int tclvarEof(sqlite3_vtab_cursor *cur){
298169f8a0cSdanielk1977 tclvar_cursor *pCur = (tclvar_cursor*)cur;
299169f8a0cSdanielk1977 return (pCur->pList2?0:1);
300169f8a0cSdanielk1977 }
301169f8a0cSdanielk1977
30243970dd7Sdan /*
30343970dd7Sdan ** If nul-terminated string zStr does not already contain the character
30443970dd7Sdan ** passed as the second argument, append it and return 0. Or, if there is
30543970dd7Sdan ** already an instance of x in zStr, do nothing return 1;
30643970dd7Sdan **
30743970dd7Sdan ** There is guaranteed to be enough room in the buffer pointed to by zStr
30843970dd7Sdan ** for the new character and nul-terminator.
30943970dd7Sdan */
tclvarAddToIdxstr(char * zStr,char x)31043970dd7Sdan static int tclvarAddToIdxstr(char *zStr, char x){
31143970dd7Sdan int i;
31243970dd7Sdan for(i=0; zStr[i]; i++){
31343970dd7Sdan if( zStr[i]==x ) return 1;
31443970dd7Sdan }
31543970dd7Sdan zStr[i] = x;
31643970dd7Sdan zStr[i+1] = '\0';
31743970dd7Sdan return 0;
31843970dd7Sdan }
31943970dd7Sdan
32043970dd7Sdan /*
32143970dd7Sdan ** Return true if variable $::tclvar_set_omit exists and is set to true.
32243970dd7Sdan ** False otherwise.
32343970dd7Sdan */
tclvarSetOmit(Tcl_Interp * interp)32443970dd7Sdan static int tclvarSetOmit(Tcl_Interp *interp){
32543970dd7Sdan int rc;
32643970dd7Sdan int res = 0;
32743970dd7Sdan Tcl_Obj *pRes;
32843970dd7Sdan rc = Tcl_Eval(interp,
32943970dd7Sdan "expr {[info exists ::tclvar_set_omit] && $::tclvar_set_omit}"
33043970dd7Sdan );
33143970dd7Sdan if( rc==TCL_OK ){
33243970dd7Sdan pRes = Tcl_GetObjResult(interp);
33343970dd7Sdan rc = Tcl_GetBooleanFromObj(0, pRes, &res);
33443970dd7Sdan }
33543970dd7Sdan return (rc==TCL_OK && res);
33643970dd7Sdan }
33743970dd7Sdan
33843970dd7Sdan /*
33943970dd7Sdan ** The xBestIndex() method. This virtual table supports the following
34043970dd7Sdan ** operators:
34143970dd7Sdan **
34243970dd7Sdan ** name = ? (omit flag clear)
34343970dd7Sdan ** name MATCH ? (omit flag set)
34443970dd7Sdan ** value GLOB ? (omit flag set iff $::tclvar_set_omit)
34543970dd7Sdan ** value REGEXP ? (omit flag set iff $::tclvar_set_omit)
34643970dd7Sdan ** value LIKE ? (omit flag set iff $::tclvar_set_omit)
34743970dd7Sdan **
34843970dd7Sdan ** For each constraint present, the corresponding TCLVAR_XXX character is
34943970dd7Sdan ** appended to the idxStr value.
35043970dd7Sdan */
tclvarBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)3514be8b51eSdrh static int tclvarBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
35243970dd7Sdan tclvar_vtab *pTab = (tclvar_vtab*)tab;
353169f8a0cSdanielk1977 int ii;
35443970dd7Sdan char *zStr = sqlite3_malloc(32);
35543970dd7Sdan int iStr = 0;
35643970dd7Sdan
35743970dd7Sdan if( zStr==0 ) return SQLITE_NOMEM;
35843970dd7Sdan zStr[0] = '\0';
359169f8a0cSdanielk1977
360169f8a0cSdanielk1977 for(ii=0; ii<pIdxInfo->nConstraint; ii++){
361169f8a0cSdanielk1977 struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
362169f8a0cSdanielk1977 struct sqlite3_index_constraint_usage *pUsage;
36343970dd7Sdan
364169f8a0cSdanielk1977 pUsage = &pIdxInfo->aConstraintUsage[ii];
36543970dd7Sdan if( pCons->usable ){
36643970dd7Sdan /* name = ? */
36743970dd7Sdan if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && pCons->iColumn==0 ){
36843970dd7Sdan if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_EQ) ){
36943970dd7Sdan pUsage->argvIndex = ++iStr;
370169f8a0cSdanielk1977 pUsage->omit = 0;
371169f8a0cSdanielk1977 }
372169f8a0cSdanielk1977 }
373169f8a0cSdanielk1977
37443970dd7Sdan /* name MATCH ? */
37543970dd7Sdan if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH && pCons->iColumn==0 ){
37643970dd7Sdan if( 0==tclvarAddToIdxstr(zStr, TCLVAR_NAME_MATCH) ){
37743970dd7Sdan pUsage->argvIndex = ++iStr;
378169f8a0cSdanielk1977 pUsage->omit = 1;
379169f8a0cSdanielk1977 }
380169f8a0cSdanielk1977 }
381169f8a0cSdanielk1977
38243970dd7Sdan /* value GLOB ? */
38343970dd7Sdan if( pCons->op==SQLITE_INDEX_CONSTRAINT_GLOB && pCons->iColumn==2 ){
38443970dd7Sdan if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_GLOB) ){
38543970dd7Sdan pUsage->argvIndex = ++iStr;
38643970dd7Sdan pUsage->omit = tclvarSetOmit(pTab->interp);
38743970dd7Sdan }
38843970dd7Sdan }
38943970dd7Sdan
39043970dd7Sdan /* value REGEXP ? */
39143970dd7Sdan if( pCons->op==SQLITE_INDEX_CONSTRAINT_REGEXP && pCons->iColumn==2 ){
39243970dd7Sdan if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_REGEXP) ){
39343970dd7Sdan pUsage->argvIndex = ++iStr;
39443970dd7Sdan pUsage->omit = tclvarSetOmit(pTab->interp);
39543970dd7Sdan }
39643970dd7Sdan }
39743970dd7Sdan
39843970dd7Sdan /* value LIKE ? */
39943970dd7Sdan if( pCons->op==SQLITE_INDEX_CONSTRAINT_LIKE && pCons->iColumn==2 ){
40043970dd7Sdan if( 0==tclvarAddToIdxstr(zStr, TCLVAR_VALUE_LIKE) ){
40143970dd7Sdan pUsage->argvIndex = ++iStr;
40243970dd7Sdan pUsage->omit = tclvarSetOmit(pTab->interp);
40343970dd7Sdan }
40443970dd7Sdan }
40543970dd7Sdan }
40643970dd7Sdan }
40743970dd7Sdan pIdxInfo->idxStr = zStr;
40843970dd7Sdan pIdxInfo->needToFreeIdxStr = 1;
40943970dd7Sdan
4104be8b51eSdrh return SQLITE_OK;
4114be8b51eSdrh }
4124be8b51eSdrh
4134be8b51eSdrh /*
414*4dd176eaSdrh ** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table
415*4dd176eaSdrh */
tclvarUpdate(sqlite3_vtab * tab,int argc,sqlite3_value ** argv,sqlite_int64 * pRowid)416*4dd176eaSdrh static int tclvarUpdate(
417*4dd176eaSdrh sqlite3_vtab *tab,
418*4dd176eaSdrh int argc,
419*4dd176eaSdrh sqlite3_value **argv,
420*4dd176eaSdrh sqlite_int64 *pRowid
421*4dd176eaSdrh ){
422*4dd176eaSdrh tclvar_vtab *pTab = (tclvar_vtab*)tab;
423*4dd176eaSdrh if( argc==1 ){
424*4dd176eaSdrh /* A DELETE operation. The variable to be deleted is stored in argv[0] */
425*4dd176eaSdrh const char *zVar = (const char*)sqlite3_value_text(argv[0]);
426*4dd176eaSdrh Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY);
427*4dd176eaSdrh return SQLITE_OK;
428*4dd176eaSdrh }
429*4dd176eaSdrh if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
430*4dd176eaSdrh /* An INSERT operation */
431*4dd176eaSdrh const char *zValue = (const char*)sqlite3_value_text(argv[4]);
432*4dd176eaSdrh const char *zName;
433*4dd176eaSdrh if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){
434*4dd176eaSdrh tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT");
435*4dd176eaSdrh return SQLITE_ERROR;
436*4dd176eaSdrh }
437*4dd176eaSdrh zName = (const char*)sqlite3_value_text(argv[5]);
438*4dd176eaSdrh if( zValue ){
439*4dd176eaSdrh Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY);
440*4dd176eaSdrh }else{
441*4dd176eaSdrh Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY);
442*4dd176eaSdrh }
443*4dd176eaSdrh return SQLITE_OK;
444*4dd176eaSdrh }
445*4dd176eaSdrh if( sqlite3_value_type(argv[0])==SQLITE_TEXT
446*4dd176eaSdrh && sqlite3_value_type(argv[1])==SQLITE_TEXT
447*4dd176eaSdrh ){
448*4dd176eaSdrh /* An UPDATE operation */
449*4dd176eaSdrh const char *zOldName = (const char*)sqlite3_value_text(argv[0]);
450*4dd176eaSdrh const char *zNewName = (const char*)sqlite3_value_text(argv[1]);
451*4dd176eaSdrh const char *zValue = (const char*)sqlite3_value_text(argv[4]);
452*4dd176eaSdrh
453*4dd176eaSdrh if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){
454*4dd176eaSdrh Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY);
455*4dd176eaSdrh }
456*4dd176eaSdrh if( zValue!=0 ){
457*4dd176eaSdrh Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY);
458*4dd176eaSdrh }
459*4dd176eaSdrh return SQLITE_OK;
460*4dd176eaSdrh }
461*4dd176eaSdrh tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change");
462*4dd176eaSdrh return SQLITE_ERROR;
463*4dd176eaSdrh }
464*4dd176eaSdrh
465*4dd176eaSdrh /*
466169f8a0cSdanielk1977 ** A virtual table module that provides read-only access to a
467169f8a0cSdanielk1977 ** Tcl global variable namespace.
4684be8b51eSdrh */
4694be8b51eSdrh static sqlite3_module tclvarModule = {
4704be8b51eSdrh 0, /* iVersion */
4714be8b51eSdrh tclvarConnect,
4724be8b51eSdrh tclvarConnect,
4734be8b51eSdrh tclvarBestIndex,
4744be8b51eSdrh tclvarDisconnect,
4754be8b51eSdrh tclvarDisconnect,
4764be8b51eSdrh tclvarOpen, /* xOpen - open a cursor */
4774be8b51eSdrh tclvarClose, /* xClose - close a cursor */
4784be8b51eSdrh tclvarFilter, /* xFilter - configure scan constraints */
4794be8b51eSdrh tclvarNext, /* xNext - advance a cursor */
480169f8a0cSdanielk1977 tclvarEof, /* xEof - check for end of scan */
4814be8b51eSdrh tclvarColumn, /* xColumn - read data */
482169f8a0cSdanielk1977 tclvarRowid, /* xRowid - read data */
483*4dd176eaSdrh tclvarUpdate, /* xUpdate */
484169f8a0cSdanielk1977 0, /* xBegin */
485169f8a0cSdanielk1977 0, /* xSync */
486169f8a0cSdanielk1977 0, /* xCommit */
487b7f6f68fSdrh 0, /* xRollback */
488b7f6f68fSdrh 0, /* xFindMethod */
489c033b642Sdanielk1977 0, /* xRename */
4904be8b51eSdrh };
4914be8b51eSdrh
4924be8b51eSdrh /*
4934be8b51eSdrh ** Decode a pointer to an sqlite3 object.
4944be8b51eSdrh */
49524b58dd7Sdrh extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
4964be8b51eSdrh
4974be8b51eSdrh /*
4984be8b51eSdrh ** Register the echo virtual table module.
4994be8b51eSdrh */
register_tclvar_module(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5007617e4a8Smistachkin static int SQLITE_TCLAPI register_tclvar_module(
5014be8b51eSdrh ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
5024be8b51eSdrh Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
5034be8b51eSdrh int objc, /* Number of arguments */
5044be8b51eSdrh Tcl_Obj *CONST objv[] /* Command arguments */
5054be8b51eSdrh ){
50643970dd7Sdan int rc = TCL_OK;
5074be8b51eSdrh sqlite3 *db;
5084be8b51eSdrh if( objc!=2 ){
5094be8b51eSdrh Tcl_WrongNumArgs(interp, 1, objv, "DB");
5104be8b51eSdrh return TCL_ERROR;
5114be8b51eSdrh }
5124be8b51eSdrh if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
5134be8b51eSdrh #ifndef SQLITE_OMIT_VIRTUALTABLE
514d1ab1ba5Sdanielk1977 sqlite3_create_module(db, "tclvar", &tclvarModule, (void*)interp);
51543970dd7Sdan rc = Tcl_Eval(interp,
51643970dd7Sdan "proc like {pattern str} {\n"
51743970dd7Sdan " set p [string map {% * _ ?} $pattern]\n"
51843970dd7Sdan " string match $p $str\n"
51943970dd7Sdan "}\n"
52043970dd7Sdan "proc tclvar_filter_cmd {eq match glob regexp like} {\n"
52143970dd7Sdan " set res {}\n"
52243970dd7Sdan " set pattern $eq\n"
52343970dd7Sdan " if {$pattern=={}} { set pattern $match }\n"
52443970dd7Sdan " if {$pattern=={}} { set pattern * }\n"
52543970dd7Sdan " foreach v [uplevel #0 info vars $pattern] {\n"
52643970dd7Sdan " if {($glob=={} || [string match $glob [uplevel #0 set $v]])\n"
52743970dd7Sdan " && ($like=={} || [like $like [uplevel #0 set $v]])\n"
52843970dd7Sdan " && ($regexp=={} || [regexp $regexp [uplevel #0 set $v]])\n"
52943970dd7Sdan " } {\n"
53043970dd7Sdan " lappend res $v\n"
53143970dd7Sdan " }\n"
53243970dd7Sdan " }\n"
53343970dd7Sdan " set res\n"
53443970dd7Sdan "}\n"
53543970dd7Sdan );
5364be8b51eSdrh #endif
53743970dd7Sdan return rc;
5384be8b51eSdrh }
5394be8b51eSdrh
540169f8a0cSdanielk1977 #endif
541169f8a0cSdanielk1977
5424be8b51eSdrh
5434be8b51eSdrh /*
5444be8b51eSdrh ** Register commands with the TCL interpreter.
5454be8b51eSdrh */
Sqlitetesttclvar_Init(Tcl_Interp * interp)5464be8b51eSdrh int Sqlitetesttclvar_Init(Tcl_Interp *interp){
5476e89162dSdanielk1977 #ifndef SQLITE_OMIT_VIRTUALTABLE
5484be8b51eSdrh static struct {
5494be8b51eSdrh char *zName;
5504be8b51eSdrh Tcl_ObjCmdProc *xProc;
5514be8b51eSdrh void *clientData;
5524be8b51eSdrh } aObjCmd[] = {
5534be8b51eSdrh { "register_tclvar_module", register_tclvar_module, 0 },
5544be8b51eSdrh };
5554be8b51eSdrh int i;
5564be8b51eSdrh for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
5574be8b51eSdrh Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
5584be8b51eSdrh aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
5594be8b51eSdrh }
5606e89162dSdanielk1977 #endif
5614be8b51eSdrh return TCL_OK;
5624be8b51eSdrh }
563