diff options
author | dan miller | 2007-10-20 02:49:29 +0000 |
---|---|---|
committer | dan miller | 2007-10-20 02:49:29 +0000 |
commit | e36d23a85ebff914d74bb541558c2b6082b78edb (patch) | |
tree | 54b58fdf162e78af64055282a6035c8d2443389d /libraries/sqlite/unix/sqlite-3.5.1/src/test8.c | |
parent | * Fixed an issue whereby avatar chat distances were being calculated against ... (diff) | |
download | opensim-SC-e36d23a85ebff914d74bb541558c2b6082b78edb.zip opensim-SC-e36d23a85ebff914d74bb541558c2b6082b78edb.tar.gz opensim-SC-e36d23a85ebff914d74bb541558c2b6082b78edb.tar.bz2 opensim-SC-e36d23a85ebff914d74bb541558c2b6082b78edb.tar.xz |
sqlite source (unix build) added to libraries
Diffstat (limited to 'libraries/sqlite/unix/sqlite-3.5.1/src/test8.c')
-rw-r--r-- | libraries/sqlite/unix/sqlite-3.5.1/src/test8.c | 1215 |
1 files changed, 1215 insertions, 0 deletions
diff --git a/libraries/sqlite/unix/sqlite-3.5.1/src/test8.c b/libraries/sqlite/unix/sqlite-3.5.1/src/test8.c new file mode 100644 index 0000000..1a6baea --- /dev/null +++ b/libraries/sqlite/unix/sqlite-3.5.1/src/test8.c | |||
@@ -0,0 +1,1215 @@ | |||
1 | /* | ||
2 | ** 2006 June 10 | ||
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 | ** Code for testing the virtual table interfaces. This code | ||
13 | ** is not included in the SQLite library. It is used for automated | ||
14 | ** testing of the SQLite library. | ||
15 | ** | ||
16 | ** $Id: test8.c,v 1.57 2007/09/03 15:03:21 danielk1977 Exp $ | ||
17 | */ | ||
18 | #include "sqliteInt.h" | ||
19 | #include "tcl.h" | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | |||
23 | #ifndef SQLITE_OMIT_VIRTUALTABLE | ||
24 | |||
25 | typedef struct echo_vtab echo_vtab; | ||
26 | typedef struct echo_cursor echo_cursor; | ||
27 | |||
28 | /* | ||
29 | ** The test module defined in this file uses four global Tcl variables to | ||
30 | ** commicate with test-scripts: | ||
31 | ** | ||
32 | ** $::echo_module | ||
33 | ** $::echo_module_sync_fail | ||
34 | ** $::echo_module_begin_fail | ||
35 | ** $::echo_module_cost | ||
36 | ** | ||
37 | ** The variable ::echo_module is a list. Each time one of the following | ||
38 | ** methods is called, one or more elements are appended to the list. | ||
39 | ** This is used for automated testing of virtual table modules. | ||
40 | ** | ||
41 | ** The ::echo_module_sync_fail variable is set by test scripts and read | ||
42 | ** by code in this file. If it is set to the name of a real table in the | ||
43 | ** the database, then all xSync operations on echo virtual tables that | ||
44 | ** use the named table as a backing store will fail. | ||
45 | */ | ||
46 | |||
47 | /* | ||
48 | ** An echo virtual-table object. | ||
49 | ** | ||
50 | ** echo.vtab.aIndex is an array of booleans. The nth entry is true if | ||
51 | ** the nth column of the real table is the left-most column of an index | ||
52 | ** (implicit or otherwise). In other words, if SQLite can optimize | ||
53 | ** a query like "SELECT * FROM real_table WHERE col = ?". | ||
54 | ** | ||
55 | ** Member variable aCol[] contains copies of the column names of the real | ||
56 | ** table. | ||
57 | */ | ||
58 | struct echo_vtab { | ||
59 | sqlite3_vtab base; | ||
60 | Tcl_Interp *interp; /* Tcl interpreter containing debug variables */ | ||
61 | sqlite3 *db; /* Database connection */ | ||
62 | |||
63 | int isPattern; | ||
64 | char *zThis; /* Name of the echo table */ | ||
65 | char *zTableName; /* Name of the real table */ | ||
66 | char *zLogName; /* Name of the log table */ | ||
67 | int nCol; /* Number of columns in the real table */ | ||
68 | int *aIndex; /* Array of size nCol. True if column has an index */ | ||
69 | char **aCol; /* Array of size nCol. Column names */ | ||
70 | }; | ||
71 | |||
72 | /* An echo cursor object */ | ||
73 | struct echo_cursor { | ||
74 | sqlite3_vtab_cursor base; | ||
75 | sqlite3_stmt *pStmt; | ||
76 | }; | ||
77 | |||
78 | /* | ||
79 | ** Convert an SQL-style quoted string into a normal string by removing | ||
80 | ** the quote characters. The conversion is done in-place. If the | ||
81 | ** input does not begin with a quote character, then this routine | ||
82 | ** is a no-op. | ||
83 | ** | ||
84 | ** Examples: | ||
85 | ** | ||
86 | ** "abc" becomes abc | ||
87 | ** 'xyz' becomes xyz | ||
88 | ** [pqr] becomes pqr | ||
89 | ** `mno` becomes mno | ||
90 | */ | ||
91 | static void dequoteString(char *z){ | ||
92 | int quote; | ||
93 | int i, j; | ||
94 | if( z==0 ) return; | ||
95 | quote = z[0]; | ||
96 | switch( quote ){ | ||
97 | case '\'': break; | ||
98 | case '"': break; | ||
99 | case '`': break; /* For MySQL compatibility */ | ||
100 | case '[': quote = ']'; break; /* For MS SqlServer compatibility */ | ||
101 | default: return; | ||
102 | } | ||
103 | for(i=1, j=0; z[i]; i++){ | ||
104 | if( z[i]==quote ){ | ||
105 | if( z[i+1]==quote ){ | ||
106 | z[j++] = quote; | ||
107 | i++; | ||
108 | }else{ | ||
109 | z[j++] = 0; | ||
110 | break; | ||
111 | } | ||
112 | }else{ | ||
113 | z[j++] = z[i]; | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | ** Retrieve the column names for the table named zTab via database | ||
120 | ** connection db. SQLITE_OK is returned on success, or an sqlite error | ||
121 | ** code otherwise. | ||
122 | ** | ||
123 | ** If successful, the number of columns is written to *pnCol. *paCol is | ||
124 | ** set to point at sqlite3_malloc()'d space containing the array of | ||
125 | ** nCol column names. The caller is responsible for calling sqlite3_free | ||
126 | ** on *paCol. | ||
127 | */ | ||
128 | static int getColumnNames( | ||
129 | sqlite3 *db, | ||
130 | const char *zTab, | ||
131 | char ***paCol, | ||
132 | int *pnCol | ||
133 | ){ | ||
134 | char **aCol = 0; | ||
135 | char *zSql; | ||
136 | sqlite3_stmt *pStmt = 0; | ||
137 | int rc = SQLITE_OK; | ||
138 | int nCol = 0; | ||
139 | |||
140 | /* Prepare the statement "SELECT * FROM <tbl>". The column names | ||
141 | ** of the result set of the compiled SELECT will be the same as | ||
142 | ** the column names of table <tbl>. | ||
143 | */ | ||
144 | zSql = sqlite3_mprintf("SELECT * FROM %Q", zTab); | ||
145 | if( !zSql ){ | ||
146 | rc = SQLITE_NOMEM; | ||
147 | goto out; | ||
148 | } | ||
149 | rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); | ||
150 | sqlite3_free(zSql); | ||
151 | |||
152 | if( rc==SQLITE_OK ){ | ||
153 | int ii; | ||
154 | int nBytes; | ||
155 | char *zSpace; | ||
156 | nCol = sqlite3_column_count(pStmt); | ||
157 | |||
158 | /* Figure out how much space to allocate for the array of column names | ||
159 | ** (including space for the strings themselves). Then allocate it. | ||
160 | */ | ||
161 | nBytes = sizeof(char *) * nCol; | ||
162 | for(ii=0; ii<nCol; ii++){ | ||
163 | nBytes += (strlen(sqlite3_column_name(pStmt, ii)) + 1); | ||
164 | } | ||
165 | aCol = (char **)sqlite3MallocZero(nBytes); | ||
166 | if( !aCol ){ | ||
167 | rc = SQLITE_NOMEM; | ||
168 | goto out; | ||
169 | } | ||
170 | |||
171 | /* Copy the column names into the allocated space and set up the | ||
172 | ** pointers in the aCol[] array. | ||
173 | */ | ||
174 | zSpace = (char *)(&aCol[nCol]); | ||
175 | for(ii=0; ii<nCol; ii++){ | ||
176 | aCol[ii] = zSpace; | ||
177 | zSpace += sprintf(zSpace, "%s", sqlite3_column_name(pStmt, ii)); | ||
178 | zSpace++; | ||
179 | } | ||
180 | assert( (zSpace-nBytes)==(char *)aCol ); | ||
181 | } | ||
182 | |||
183 | *paCol = aCol; | ||
184 | *pnCol = nCol; | ||
185 | |||
186 | out: | ||
187 | sqlite3_finalize(pStmt); | ||
188 | return rc; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | ** Parameter zTab is the name of a table in database db with nCol | ||
193 | ** columns. This function allocates an array of integers nCol in | ||
194 | ** size and populates it according to any implicit or explicit | ||
195 | ** indices on table zTab. | ||
196 | ** | ||
197 | ** If successful, SQLITE_OK is returned and *paIndex set to point | ||
198 | ** at the allocated array. Otherwise, an error code is returned. | ||
199 | ** | ||
200 | ** See comments associated with the member variable aIndex above | ||
201 | ** "struct echo_vtab" for details of the contents of the array. | ||
202 | */ | ||
203 | static int getIndexArray( | ||
204 | sqlite3 *db, /* Database connection */ | ||
205 | const char *zTab, /* Name of table in database db */ | ||
206 | int nCol, | ||
207 | int **paIndex | ||
208 | ){ | ||
209 | sqlite3_stmt *pStmt = 0; | ||
210 | int *aIndex = 0; | ||
211 | int rc; | ||
212 | char *zSql; | ||
213 | |||
214 | /* Allocate space for the index array */ | ||
215 | aIndex = (int *)sqlite3MallocZero(sizeof(int) * nCol); | ||
216 | if( !aIndex ){ | ||
217 | rc = SQLITE_NOMEM; | ||
218 | goto get_index_array_out; | ||
219 | } | ||
220 | |||
221 | /* Compile an sqlite pragma to loop through all indices on table zTab */ | ||
222 | zSql = sqlite3MPrintf(0, "PRAGMA index_list(%s)", zTab); | ||
223 | if( !zSql ){ | ||
224 | rc = SQLITE_NOMEM; | ||
225 | goto get_index_array_out; | ||
226 | } | ||
227 | rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); | ||
228 | sqlite3_free(zSql); | ||
229 | |||
230 | /* For each index, figure out the left-most column and set the | ||
231 | ** corresponding entry in aIndex[] to 1. | ||
232 | */ | ||
233 | while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ | ||
234 | const char *zIdx = (const char *)sqlite3_column_text(pStmt, 1); | ||
235 | sqlite3_stmt *pStmt2 = 0; | ||
236 | zSql = sqlite3MPrintf(0, "PRAGMA index_info(%s)", zIdx); | ||
237 | if( !zSql ){ | ||
238 | rc = SQLITE_NOMEM; | ||
239 | goto get_index_array_out; | ||
240 | } | ||
241 | rc = sqlite3_prepare(db, zSql, -1, &pStmt2, 0); | ||
242 | sqlite3_free(zSql); | ||
243 | if( pStmt2 && sqlite3_step(pStmt2)==SQLITE_ROW ){ | ||
244 | int cid = sqlite3_column_int(pStmt2, 1); | ||
245 | assert( cid>=0 && cid<nCol ); | ||
246 | aIndex[cid] = 1; | ||
247 | } | ||
248 | if( pStmt2 ){ | ||
249 | rc = sqlite3_finalize(pStmt2); | ||
250 | } | ||
251 | if( rc!=SQLITE_OK ){ | ||
252 | goto get_index_array_out; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | |||
257 | get_index_array_out: | ||
258 | if( pStmt ){ | ||
259 | int rc2 = sqlite3_finalize(pStmt); | ||
260 | if( rc==SQLITE_OK ){ | ||
261 | rc = rc2; | ||
262 | } | ||
263 | } | ||
264 | if( rc!=SQLITE_OK ){ | ||
265 | sqlite3_free(aIndex); | ||
266 | aIndex = 0; | ||
267 | } | ||
268 | *paIndex = aIndex; | ||
269 | return rc; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | ** Global Tcl variable $echo_module is a list. This routine appends | ||
274 | ** the string element zArg to that list in interpreter interp. | ||
275 | */ | ||
276 | static void appendToEchoModule(Tcl_Interp *interp, const char *zArg){ | ||
277 | int flags = (TCL_APPEND_VALUE | TCL_LIST_ELEMENT | TCL_GLOBAL_ONLY); | ||
278 | Tcl_SetVar(interp, "echo_module", (zArg?zArg:""), flags); | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | ** This function is called from within the echo-modules xCreate and | ||
283 | ** xConnect methods. The argc and argv arguments are copies of those | ||
284 | ** passed to the calling method. This function is responsible for | ||
285 | ** calling sqlite3_declare_vtab() to declare the schema of the virtual | ||
286 | ** table being created or connected. | ||
287 | ** | ||
288 | ** If the constructor was passed just one argument, i.e.: | ||
289 | ** | ||
290 | ** CREATE TABLE t1 AS echo(t2); | ||
291 | ** | ||
292 | ** Then t2 is assumed to be the name of a *real* database table. The | ||
293 | ** schema of the virtual table is declared by passing a copy of the | ||
294 | ** CREATE TABLE statement for the real table to sqlite3_declare_vtab(). | ||
295 | ** Hence, the virtual table should have exactly the same column names and | ||
296 | ** types as the real table. | ||
297 | */ | ||
298 | static int echoDeclareVtab( | ||
299 | echo_vtab *pVtab, | ||
300 | sqlite3 *db | ||
301 | ){ | ||
302 | int rc = SQLITE_OK; | ||
303 | |||
304 | if( pVtab->zTableName ){ | ||
305 | sqlite3_stmt *pStmt = 0; | ||
306 | rc = sqlite3_prepare(db, | ||
307 | "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?", | ||
308 | -1, &pStmt, 0); | ||
309 | if( rc==SQLITE_OK ){ | ||
310 | sqlite3_bind_text(pStmt, 1, pVtab->zTableName, -1, 0); | ||
311 | if( sqlite3_step(pStmt)==SQLITE_ROW ){ | ||
312 | int rc2; | ||
313 | const char *zCreateTable = (const char *)sqlite3_column_text(pStmt, 0); | ||
314 | rc = sqlite3_declare_vtab(db, zCreateTable); | ||
315 | rc2 = sqlite3_finalize(pStmt); | ||
316 | if( rc==SQLITE_OK ){ | ||
317 | rc = rc2; | ||
318 | } | ||
319 | } else { | ||
320 | rc = sqlite3_finalize(pStmt); | ||
321 | if( rc==SQLITE_OK ){ | ||
322 | rc = SQLITE_ERROR; | ||
323 | } | ||
324 | } | ||
325 | if( rc==SQLITE_OK ){ | ||
326 | rc = getColumnNames(db, pVtab->zTableName, &pVtab->aCol, &pVtab->nCol); | ||
327 | } | ||
328 | if( rc==SQLITE_OK ){ | ||
329 | rc = getIndexArray(db, pVtab->zTableName, pVtab->nCol, &pVtab->aIndex); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | |||
334 | return rc; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | ** This function frees all runtime structures associated with the virtual | ||
339 | ** table pVtab. | ||
340 | */ | ||
341 | static int echoDestructor(sqlite3_vtab *pVtab){ | ||
342 | echo_vtab *p = (echo_vtab*)pVtab; | ||
343 | sqlite3_free(p->aIndex); | ||
344 | sqlite3_free(p->aCol); | ||
345 | sqlite3_free(p->zThis); | ||
346 | sqlite3_free(p->zTableName); | ||
347 | sqlite3_free(p->zLogName); | ||
348 | sqlite3_free(p); | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | typedef struct EchoModule EchoModule; | ||
353 | struct EchoModule { | ||
354 | Tcl_Interp *interp; | ||
355 | }; | ||
356 | |||
357 | /* | ||
358 | ** This function is called to do the work of the xConnect() method - | ||
359 | ** to allocate the required in-memory structures for a newly connected | ||
360 | ** virtual table. | ||
361 | */ | ||
362 | static int echoConstructor( | ||
363 | sqlite3 *db, | ||
364 | void *pAux, | ||
365 | int argc, const char *const*argv, | ||
366 | sqlite3_vtab **ppVtab, | ||
367 | char **pzErr | ||
368 | ){ | ||
369 | int rc; | ||
370 | int i; | ||
371 | echo_vtab *pVtab; | ||
372 | |||
373 | /* Allocate the sqlite3_vtab/echo_vtab structure itself */ | ||
374 | pVtab = sqlite3MallocZero( sizeof(*pVtab) ); | ||
375 | if( !pVtab ){ | ||
376 | return SQLITE_NOMEM; | ||
377 | } | ||
378 | pVtab->interp = ((EchoModule *)pAux)->interp; | ||
379 | pVtab->db = db; | ||
380 | |||
381 | /* Allocate echo_vtab.zThis */ | ||
382 | pVtab->zThis = sqlite3MPrintf(0, "%s", argv[2]); | ||
383 | if( !pVtab->zThis ){ | ||
384 | echoDestructor((sqlite3_vtab *)pVtab); | ||
385 | return SQLITE_NOMEM; | ||
386 | } | ||
387 | |||
388 | /* Allocate echo_vtab.zTableName */ | ||
389 | if( argc>3 ){ | ||
390 | pVtab->zTableName = sqlite3MPrintf(0, "%s", argv[3]); | ||
391 | dequoteString(pVtab->zTableName); | ||
392 | if( pVtab->zTableName && pVtab->zTableName[0]=='*' ){ | ||
393 | char *z = sqlite3MPrintf(0, "%s%s", argv[2], &(pVtab->zTableName[1])); | ||
394 | sqlite3_free(pVtab->zTableName); | ||
395 | pVtab->zTableName = z; | ||
396 | pVtab->isPattern = 1; | ||
397 | } | ||
398 | if( !pVtab->zTableName ){ | ||
399 | echoDestructor((sqlite3_vtab *)pVtab); | ||
400 | return SQLITE_NOMEM; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | /* Log the arguments to this function to Tcl var ::echo_module */ | ||
405 | for(i=0; i<argc; i++){ | ||
406 | appendToEchoModule(pVtab->interp, argv[i]); | ||
407 | } | ||
408 | |||
409 | /* Invoke sqlite3_declare_vtab and set up other members of the echo_vtab | ||
410 | ** structure. If an error occurs, delete the sqlite3_vtab structure and | ||
411 | ** return an error code. | ||
412 | */ | ||
413 | rc = echoDeclareVtab(pVtab, db); | ||
414 | if( rc!=SQLITE_OK ){ | ||
415 | echoDestructor((sqlite3_vtab *)pVtab); | ||
416 | return rc; | ||
417 | } | ||
418 | |||
419 | /* Success. Set *ppVtab and return */ | ||
420 | *ppVtab = &pVtab->base; | ||
421 | return SQLITE_OK; | ||
422 | } | ||
423 | |||
424 | /* | ||
425 | ** Echo virtual table module xCreate method. | ||
426 | */ | ||
427 | static int echoCreate( | ||
428 | sqlite3 *db, | ||
429 | void *pAux, | ||
430 | int argc, const char *const*argv, | ||
431 | sqlite3_vtab **ppVtab, | ||
432 | char **pzErr | ||
433 | ){ | ||
434 | int rc = SQLITE_OK; | ||
435 | appendToEchoModule(((EchoModule *)pAux)->interp, "xCreate"); | ||
436 | rc = echoConstructor(db, pAux, argc, argv, ppVtab, pzErr); | ||
437 | |||
438 | /* If there were two arguments passed to the module at the SQL level | ||
439 | ** (i.e. "CREATE VIRTUAL TABLE tbl USING echo(arg1, arg2)"), then | ||
440 | ** the second argument is used as a table name. Attempt to create | ||
441 | ** such a table with a single column, "logmsg". This table will | ||
442 | ** be used to log calls to the xUpdate method. It will be deleted | ||
443 | ** when the virtual table is DROPed. | ||
444 | ** | ||
445 | ** Note: The main point of this is to test that we can drop tables | ||
446 | ** from within an xDestroy method call. | ||
447 | */ | ||
448 | if( rc==SQLITE_OK && argc==5 ){ | ||
449 | char *zSql; | ||
450 | echo_vtab *pVtab = *(echo_vtab **)ppVtab; | ||
451 | pVtab->zLogName = sqlite3MPrintf(0, "%s", argv[4]); | ||
452 | zSql = sqlite3MPrintf(0, "CREATE TABLE %Q(logmsg)", pVtab->zLogName); | ||
453 | rc = sqlite3_exec(db, zSql, 0, 0, 0); | ||
454 | sqlite3_free(zSql); | ||
455 | if( rc!=SQLITE_OK ){ | ||
456 | *pzErr = sqlite3StrDup(sqlite3_errmsg(db)); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | if( *ppVtab && rc!=SQLITE_OK ){ | ||
461 | echoDestructor(*ppVtab); | ||
462 | *ppVtab = 0; | ||
463 | } | ||
464 | |||
465 | return rc; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | ** Echo virtual table module xConnect method. | ||
470 | */ | ||
471 | static int echoConnect( | ||
472 | sqlite3 *db, | ||
473 | void *pAux, | ||
474 | int argc, const char *const*argv, | ||
475 | sqlite3_vtab **ppVtab, | ||
476 | char **pzErr | ||
477 | ){ | ||
478 | appendToEchoModule(((EchoModule *)pAux)->interp, "xConnect"); | ||
479 | return echoConstructor(db, pAux, argc, argv, ppVtab, pzErr); | ||
480 | } | ||
481 | |||
482 | /* | ||
483 | ** Echo virtual table module xDisconnect method. | ||
484 | */ | ||
485 | static int echoDisconnect(sqlite3_vtab *pVtab){ | ||
486 | appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect"); | ||
487 | return echoDestructor(pVtab); | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | ** Echo virtual table module xDestroy method. | ||
492 | */ | ||
493 | static int echoDestroy(sqlite3_vtab *pVtab){ | ||
494 | int rc = SQLITE_OK; | ||
495 | echo_vtab *p = (echo_vtab *)pVtab; | ||
496 | appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy"); | ||
497 | |||
498 | /* Drop the "log" table, if one exists (see echoCreate() for details) */ | ||
499 | if( p && p->zLogName ){ | ||
500 | char *zSql; | ||
501 | zSql = sqlite3MPrintf(0, "DROP TABLE %Q", p->zLogName); | ||
502 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); | ||
503 | sqlite3_free(zSql); | ||
504 | } | ||
505 | |||
506 | if( rc==SQLITE_OK ){ | ||
507 | rc = echoDestructor(pVtab); | ||
508 | } | ||
509 | return rc; | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | ** Echo virtual table module xOpen method. | ||
514 | */ | ||
515 | static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ | ||
516 | echo_cursor *pCur; | ||
517 | pCur = sqlite3MallocZero(sizeof(echo_cursor)); | ||
518 | *ppCursor = (sqlite3_vtab_cursor *)pCur; | ||
519 | return (pCur ? SQLITE_OK : SQLITE_NOMEM); | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | ** Echo virtual table module xClose method. | ||
524 | */ | ||
525 | static int echoClose(sqlite3_vtab_cursor *cur){ | ||
526 | int rc; | ||
527 | echo_cursor *pCur = (echo_cursor *)cur; | ||
528 | sqlite3_stmt *pStmt = pCur->pStmt; | ||
529 | pCur->pStmt = 0; | ||
530 | sqlite3_free(pCur); | ||
531 | rc = sqlite3_finalize(pStmt); | ||
532 | return rc; | ||
533 | } | ||
534 | |||
535 | /* | ||
536 | ** Return non-zero if the cursor does not currently point to a valid record | ||
537 | ** (i.e if the scan has finished), or zero otherwise. | ||
538 | */ | ||
539 | static int echoEof(sqlite3_vtab_cursor *cur){ | ||
540 | return (((echo_cursor *)cur)->pStmt ? 0 : 1); | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | ** Echo virtual table module xNext method. | ||
545 | */ | ||
546 | static int echoNext(sqlite3_vtab_cursor *cur){ | ||
547 | int rc = SQLITE_OK; | ||
548 | echo_cursor *pCur = (echo_cursor *)cur; | ||
549 | |||
550 | if( pCur->pStmt ){ | ||
551 | rc = sqlite3_step(pCur->pStmt); | ||
552 | if( rc==SQLITE_ROW ){ | ||
553 | rc = SQLITE_OK; | ||
554 | }else{ | ||
555 | rc = sqlite3_finalize(pCur->pStmt); | ||
556 | pCur->pStmt = 0; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | return rc; | ||
561 | } | ||
562 | |||
563 | /* | ||
564 | ** Echo virtual table module xColumn method. | ||
565 | */ | ||
566 | static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ | ||
567 | int iCol = i + 1; | ||
568 | sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; | ||
569 | if( !pStmt ){ | ||
570 | sqlite3_result_null(ctx); | ||
571 | }else{ | ||
572 | assert( sqlite3_data_count(pStmt)>iCol ); | ||
573 | sqlite3_result_value(ctx, sqlite3_column_value(pStmt, iCol)); | ||
574 | } | ||
575 | return SQLITE_OK; | ||
576 | } | ||
577 | |||
578 | /* | ||
579 | ** Echo virtual table module xRowid method. | ||
580 | */ | ||
581 | static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ | ||
582 | sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; | ||
583 | *pRowid = sqlite3_column_int64(pStmt, 0); | ||
584 | return SQLITE_OK; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | ** Compute a simple hash of the null terminated string zString. | ||
589 | ** | ||
590 | ** This module uses only sqlite3_index_info.idxStr, not | ||
591 | ** sqlite3_index_info.idxNum. So to test idxNum, when idxStr is set | ||
592 | ** in echoBestIndex(), idxNum is set to the corresponding hash value. | ||
593 | ** In echoFilter(), code assert()s that the supplied idxNum value is | ||
594 | ** indeed the hash of the supplied idxStr. | ||
595 | */ | ||
596 | static int hashString(const char *zString){ | ||
597 | int val = 0; | ||
598 | int ii; | ||
599 | for(ii=0; zString[ii]; ii++){ | ||
600 | val = (val << 3) + (int)zString[ii]; | ||
601 | } | ||
602 | return val; | ||
603 | } | ||
604 | |||
605 | /* | ||
606 | ** Echo virtual table module xFilter method. | ||
607 | */ | ||
608 | static int echoFilter( | ||
609 | sqlite3_vtab_cursor *pVtabCursor, | ||
610 | int idxNum, const char *idxStr, | ||
611 | int argc, sqlite3_value **argv | ||
612 | ){ | ||
613 | int rc; | ||
614 | int i; | ||
615 | |||
616 | echo_cursor *pCur = (echo_cursor *)pVtabCursor; | ||
617 | echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; | ||
618 | sqlite3 *db = pVtab->db; | ||
619 | |||
620 | /* Check that idxNum matches idxStr */ | ||
621 | assert( idxNum==hashString(idxStr) ); | ||
622 | |||
623 | /* Log arguments to the ::echo_module Tcl variable */ | ||
624 | appendToEchoModule(pVtab->interp, "xFilter"); | ||
625 | appendToEchoModule(pVtab->interp, idxStr); | ||
626 | for(i=0; i<argc; i++){ | ||
627 | appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i])); | ||
628 | } | ||
629 | |||
630 | sqlite3_finalize(pCur->pStmt); | ||
631 | pCur->pStmt = 0; | ||
632 | |||
633 | /* Prepare the SQL statement created by echoBestIndex and bind the | ||
634 | ** runtime parameters passed to this function to it. | ||
635 | */ | ||
636 | rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0); | ||
637 | assert( pCur->pStmt || rc!=SQLITE_OK ); | ||
638 | for(i=0; rc==SQLITE_OK && i<argc; i++){ | ||
639 | sqlite3_bind_value(pCur->pStmt, i+1, argv[i]); | ||
640 | } | ||
641 | |||
642 | /* If everything was successful, advance to the first row of the scan */ | ||
643 | if( rc==SQLITE_OK ){ | ||
644 | rc = echoNext(pVtabCursor); | ||
645 | } | ||
646 | |||
647 | return rc; | ||
648 | } | ||
649 | |||
650 | |||
651 | /* | ||
652 | ** A helper function used by echoUpdate() and echoBestIndex() for | ||
653 | ** manipulating strings in concert with the sqlite3_mprintf() function. | ||
654 | ** | ||
655 | ** Parameter pzStr points to a pointer to a string allocated with | ||
656 | ** sqlite3_mprintf. The second parameter, zAppend, points to another | ||
657 | ** string. The two strings are concatenated together and *pzStr | ||
658 | ** set to point at the result. The initial buffer pointed to by *pzStr | ||
659 | ** is deallocated via sqlite3_free(). | ||
660 | ** | ||
661 | ** If the third argument, doFree, is true, then sqlite3_free() is | ||
662 | ** also called to free the buffer pointed to by zAppend. | ||
663 | */ | ||
664 | static void string_concat(char **pzStr, char *zAppend, int doFree, int *pRc){ | ||
665 | char *zIn = *pzStr; | ||
666 | if( !zAppend && doFree && *pRc==SQLITE_OK ){ | ||
667 | *pRc = SQLITE_NOMEM; | ||
668 | } | ||
669 | if( *pRc!=SQLITE_OK ){ | ||
670 | sqlite3_free(zIn); | ||
671 | zIn = 0; | ||
672 | }else{ | ||
673 | if( zIn ){ | ||
674 | char *zTemp = zIn; | ||
675 | zIn = sqlite3_mprintf("%s%s", zIn, zAppend); | ||
676 | sqlite3_free(zTemp); | ||
677 | }else{ | ||
678 | zIn = sqlite3_mprintf("%s", zAppend); | ||
679 | } | ||
680 | if( !zIn ){ | ||
681 | *pRc = SQLITE_NOMEM; | ||
682 | } | ||
683 | } | ||
684 | *pzStr = zIn; | ||
685 | if( doFree ){ | ||
686 | sqlite3_free(zAppend); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | ** The echo module implements the subset of query constraints and sort | ||
692 | ** orders that may take advantage of SQLite indices on the underlying | ||
693 | ** real table. For example, if the real table is declared as: | ||
694 | ** | ||
695 | ** CREATE TABLE real(a, b, c); | ||
696 | ** CREATE INDEX real_index ON real(b); | ||
697 | ** | ||
698 | ** then the echo module handles WHERE or ORDER BY clauses that refer | ||
699 | ** to the column "b", but not "a" or "c". If a multi-column index is | ||
700 | ** present, only it's left most column is considered. | ||
701 | ** | ||
702 | ** This xBestIndex method encodes the proposed search strategy as | ||
703 | ** an SQL query on the real table underlying the virtual echo module | ||
704 | ** table and stores the query in sqlite3_index_info.idxStr. The SQL | ||
705 | ** statement is of the form: | ||
706 | ** | ||
707 | ** SELECT rowid, * FROM <real-table> ?<where-clause>? ?<order-by-clause>? | ||
708 | ** | ||
709 | ** where the <where-clause> and <order-by-clause> are determined | ||
710 | ** by the contents of the structure pointed to by the pIdxInfo argument. | ||
711 | */ | ||
712 | static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | ||
713 | int ii; | ||
714 | char *zQuery = 0; | ||
715 | char *zNew; | ||
716 | int nArg = 0; | ||
717 | const char *zSep = "WHERE"; | ||
718 | echo_vtab *pVtab = (echo_vtab *)tab; | ||
719 | sqlite3_stmt *pStmt = 0; | ||
720 | Tcl_Interp *interp = pVtab->interp; | ||
721 | |||
722 | int nRow; | ||
723 | int useIdx = 0; | ||
724 | int rc = SQLITE_OK; | ||
725 | int useCost = 0; | ||
726 | double cost; | ||
727 | |||
728 | /* Determine the number of rows in the table and store this value in local | ||
729 | ** variable nRow. The 'estimated-cost' of the scan will be the number of | ||
730 | ** rows in the table for a linear scan, or the log (base 2) of the | ||
731 | ** number of rows if the proposed scan uses an index. | ||
732 | */ | ||
733 | if( Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY) ){ | ||
734 | cost = atof(Tcl_GetVar(interp, "echo_module_cost", TCL_GLOBAL_ONLY)); | ||
735 | useCost = 1; | ||
736 | } else { | ||
737 | zQuery = sqlite3_mprintf("SELECT count(*) FROM %Q", pVtab->zTableName); | ||
738 | if( !zQuery ){ | ||
739 | return SQLITE_NOMEM; | ||
740 | } | ||
741 | rc = sqlite3_prepare(pVtab->db, zQuery, -1, &pStmt, 0); | ||
742 | sqlite3_free(zQuery); | ||
743 | if( rc!=SQLITE_OK ){ | ||
744 | return rc; | ||
745 | } | ||
746 | sqlite3_step(pStmt); | ||
747 | nRow = sqlite3_column_int(pStmt, 0); | ||
748 | rc = sqlite3_finalize(pStmt); | ||
749 | if( rc!=SQLITE_OK ){ | ||
750 | return rc; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | zQuery = sqlite3_mprintf("SELECT rowid, * FROM %Q", pVtab->zTableName); | ||
755 | if( !zQuery ){ | ||
756 | return SQLITE_NOMEM; | ||
757 | } | ||
758 | for(ii=0; ii<pIdxInfo->nConstraint; ii++){ | ||
759 | const struct sqlite3_index_constraint *pConstraint; | ||
760 | struct sqlite3_index_constraint_usage *pUsage; | ||
761 | int iCol; | ||
762 | |||
763 | pConstraint = &pIdxInfo->aConstraint[ii]; | ||
764 | pUsage = &pIdxInfo->aConstraintUsage[ii]; | ||
765 | |||
766 | iCol = pConstraint->iColumn; | ||
767 | if( pVtab->aIndex[iCol] ){ | ||
768 | char *zCol = pVtab->aCol[iCol]; | ||
769 | char *zOp = 0; | ||
770 | useIdx = 1; | ||
771 | if( iCol<0 ){ | ||
772 | zCol = "rowid"; | ||
773 | } | ||
774 | switch( pConstraint->op ){ | ||
775 | case SQLITE_INDEX_CONSTRAINT_EQ: | ||
776 | zOp = "="; break; | ||
777 | case SQLITE_INDEX_CONSTRAINT_LT: | ||
778 | zOp = "<"; break; | ||
779 | case SQLITE_INDEX_CONSTRAINT_GT: | ||
780 | zOp = ">"; break; | ||
781 | case SQLITE_INDEX_CONSTRAINT_LE: | ||
782 | zOp = "<="; break; | ||
783 | case SQLITE_INDEX_CONSTRAINT_GE: | ||
784 | zOp = ">="; break; | ||
785 | case SQLITE_INDEX_CONSTRAINT_MATCH: | ||
786 | zOp = "LIKE"; break; | ||
787 | } | ||
788 | if( zOp[0]=='L' ){ | ||
789 | zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')", | ||
790 | zSep, zCol); | ||
791 | } else { | ||
792 | zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp); | ||
793 | } | ||
794 | string_concat(&zQuery, zNew, 1, &rc); | ||
795 | |||
796 | zSep = "AND"; | ||
797 | pUsage->argvIndex = ++nArg; | ||
798 | pUsage->omit = 1; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | /* If there is only one term in the ORDER BY clause, and it is | ||
803 | ** on a column that this virtual table has an index for, then consume | ||
804 | ** the ORDER BY clause. | ||
805 | */ | ||
806 | if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ | ||
807 | int iCol = pIdxInfo->aOrderBy->iColumn; | ||
808 | char *zCol = pVtab->aCol[iCol]; | ||
809 | char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; | ||
810 | if( iCol<0 ){ | ||
811 | zCol = "rowid"; | ||
812 | } | ||
813 | zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); | ||
814 | string_concat(&zQuery, zNew, 1, &rc); | ||
815 | pIdxInfo->orderByConsumed = 1; | ||
816 | } | ||
817 | |||
818 | appendToEchoModule(pVtab->interp, "xBestIndex");; | ||
819 | appendToEchoModule(pVtab->interp, zQuery); | ||
820 | |||
821 | if( !zQuery ){ | ||
822 | return rc; | ||
823 | } | ||
824 | pIdxInfo->idxNum = hashString(zQuery); | ||
825 | pIdxInfo->idxStr = zQuery; | ||
826 | pIdxInfo->needToFreeIdxStr = 1; | ||
827 | if (useCost) { | ||
828 | pIdxInfo->estimatedCost = cost; | ||
829 | } else if( useIdx ){ | ||
830 | /* Approximation of log2(nRow). */ | ||
831 | for( ii=0; ii<(sizeof(int)*8); ii++ ){ | ||
832 | if( nRow & (1<<ii) ){ | ||
833 | pIdxInfo->estimatedCost = (double)ii; | ||
834 | } | ||
835 | } | ||
836 | } else { | ||
837 | pIdxInfo->estimatedCost = (double)nRow; | ||
838 | } | ||
839 | return rc; | ||
840 | } | ||
841 | |||
842 | /* | ||
843 | ** The xUpdate method for echo module virtual tables. | ||
844 | ** | ||
845 | ** apData[0] apData[1] apData[2..] | ||
846 | ** | ||
847 | ** INTEGER DELETE | ||
848 | ** | ||
849 | ** INTEGER NULL (nCol args) UPDATE (do not set rowid) | ||
850 | ** INTEGER INTEGER (nCol args) UPDATE (with SET rowid = <arg1>) | ||
851 | ** | ||
852 | ** NULL NULL (nCol args) INSERT INTO (automatic rowid value) | ||
853 | ** NULL INTEGER (nCol args) INSERT (incl. rowid value) | ||
854 | ** | ||
855 | */ | ||
856 | int echoUpdate( | ||
857 | sqlite3_vtab *tab, | ||
858 | int nData, | ||
859 | sqlite3_value **apData, | ||
860 | sqlite_int64 *pRowid | ||
861 | ){ | ||
862 | echo_vtab *pVtab = (echo_vtab *)tab; | ||
863 | sqlite3 *db = pVtab->db; | ||
864 | int rc = SQLITE_OK; | ||
865 | |||
866 | sqlite3_stmt *pStmt; | ||
867 | char *z = 0; /* SQL statement to execute */ | ||
868 | int bindArgZero = 0; /* True to bind apData[0] to sql var no. nData */ | ||
869 | int bindArgOne = 0; /* True to bind apData[1] to sql var no. 1 */ | ||
870 | int i; /* Counter variable used by for loops */ | ||
871 | |||
872 | assert( nData==pVtab->nCol+2 || nData==1 ); | ||
873 | |||
874 | /* If apData[0] is an integer and nData>1 then do an UPDATE */ | ||
875 | if( nData>1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){ | ||
876 | char *zSep = " SET"; | ||
877 | z = sqlite3_mprintf("UPDATE %Q", pVtab->zTableName); | ||
878 | if( !z ){ | ||
879 | rc = SQLITE_NOMEM; | ||
880 | } | ||
881 | |||
882 | bindArgOne = (apData[1] && sqlite3_value_type(apData[1])==SQLITE_INTEGER); | ||
883 | bindArgZero = 1; | ||
884 | |||
885 | if( bindArgOne ){ | ||
886 | string_concat(&z, " SET rowid=?1 ", 0, &rc); | ||
887 | zSep = ","; | ||
888 | } | ||
889 | for(i=2; i<nData; i++){ | ||
890 | if( apData[i]==0 ) continue; | ||
891 | string_concat(&z, sqlite3_mprintf( | ||
892 | "%s %Q=?%d", zSep, pVtab->aCol[i-2], i), 1, &rc); | ||
893 | zSep = ","; | ||
894 | } | ||
895 | string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 1, &rc); | ||
896 | } | ||
897 | |||
898 | /* If apData[0] is an integer and nData==1 then do a DELETE */ | ||
899 | else if( nData==1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){ | ||
900 | z = sqlite3_mprintf("DELETE FROM %Q WHERE rowid = ?1", pVtab->zTableName); | ||
901 | if( !z ){ | ||
902 | rc = SQLITE_NOMEM; | ||
903 | } | ||
904 | bindArgZero = 1; | ||
905 | } | ||
906 | |||
907 | /* If the first argument is NULL and there are more than two args, INSERT */ | ||
908 | else if( nData>2 && sqlite3_value_type(apData[0])==SQLITE_NULL ){ | ||
909 | int ii; | ||
910 | char *zInsert = 0; | ||
911 | char *zValues = 0; | ||
912 | |||
913 | zInsert = sqlite3_mprintf("INSERT INTO %Q (", pVtab->zTableName); | ||
914 | if( !zInsert ){ | ||
915 | rc = SQLITE_NOMEM; | ||
916 | } | ||
917 | if( sqlite3_value_type(apData[1])==SQLITE_INTEGER ){ | ||
918 | bindArgOne = 1; | ||
919 | zValues = sqlite3_mprintf("?"); | ||
920 | string_concat(&zInsert, "rowid", 0, &rc); | ||
921 | } | ||
922 | |||
923 | assert((pVtab->nCol+2)==nData); | ||
924 | for(ii=2; ii<nData; ii++){ | ||
925 | string_concat(&zInsert, | ||
926 | sqlite3_mprintf("%s%Q", zValues?", ":"", pVtab->aCol[ii-2]), 1, &rc); | ||
927 | string_concat(&zValues, | ||
928 | sqlite3_mprintf("%s?%d", zValues?", ":"", ii), 1, &rc); | ||
929 | } | ||
930 | |||
931 | string_concat(&z, zInsert, 1, &rc); | ||
932 | string_concat(&z, ") VALUES(", 0, &rc); | ||
933 | string_concat(&z, zValues, 1, &rc); | ||
934 | string_concat(&z, ")", 0, &rc); | ||
935 | } | ||
936 | |||
937 | /* Anything else is an error */ | ||
938 | else{ | ||
939 | assert(0); | ||
940 | return SQLITE_ERROR; | ||
941 | } | ||
942 | |||
943 | if( rc==SQLITE_OK ){ | ||
944 | rc = sqlite3_prepare(db, z, -1, &pStmt, 0); | ||
945 | } | ||
946 | assert( rc!=SQLITE_OK || pStmt ); | ||
947 | sqlite3_free(z); | ||
948 | if( rc==SQLITE_OK ) { | ||
949 | if( bindArgZero ){ | ||
950 | sqlite3_bind_value(pStmt, nData, apData[0]); | ||
951 | } | ||
952 | if( bindArgOne ){ | ||
953 | sqlite3_bind_value(pStmt, 1, apData[1]); | ||
954 | } | ||
955 | for(i=2; i<nData; i++){ | ||
956 | if( apData[i] ) sqlite3_bind_value(pStmt, i, apData[i]); | ||
957 | } | ||
958 | sqlite3_step(pStmt); | ||
959 | rc = sqlite3_finalize(pStmt); | ||
960 | } | ||
961 | |||
962 | if( pRowid && rc==SQLITE_OK ){ | ||
963 | *pRowid = sqlite3_last_insert_rowid(db); | ||
964 | } | ||
965 | |||
966 | return rc; | ||
967 | } | ||
968 | |||
969 | /* | ||
970 | ** xBegin, xSync, xCommit and xRollback callbacks for echo module | ||
971 | ** virtual tables. Do nothing other than add the name of the callback | ||
972 | ** to the $::echo_module Tcl variable. | ||
973 | */ | ||
974 | static int echoTransactionCall(sqlite3_vtab *tab, const char *zCall){ | ||
975 | char *z; | ||
976 | echo_vtab *pVtab = (echo_vtab *)tab; | ||
977 | z = sqlite3_mprintf("echo(%s)", pVtab->zTableName); | ||
978 | appendToEchoModule(pVtab->interp, zCall); | ||
979 | appendToEchoModule(pVtab->interp, z); | ||
980 | sqlite3_free(z); | ||
981 | return (z?SQLITE_OK:SQLITE_NOMEM); | ||
982 | } | ||
983 | static int echoBegin(sqlite3_vtab *tab){ | ||
984 | int rc; | ||
985 | echo_vtab *pVtab = (echo_vtab *)tab; | ||
986 | Tcl_Interp *interp = pVtab->interp; | ||
987 | const char *zVal; | ||
988 | |||
989 | rc = echoTransactionCall(tab, "xBegin"); | ||
990 | |||
991 | if( rc==SQLITE_OK ){ | ||
992 | /* Check if the $::echo_module_begin_fail variable is defined. If it is, | ||
993 | ** and it is set to the name of the real table underlying this virtual | ||
994 | ** echo module table, then cause this xSync operation to fail. | ||
995 | */ | ||
996 | zVal = Tcl_GetVar(interp, "echo_module_begin_fail", TCL_GLOBAL_ONLY); | ||
997 | if( zVal && 0==strcmp(zVal, pVtab->zTableName) ){ | ||
998 | rc = SQLITE_ERROR; | ||
999 | } | ||
1000 | } | ||
1001 | return rc; | ||
1002 | } | ||
1003 | static int echoSync(sqlite3_vtab *tab){ | ||
1004 | int rc; | ||
1005 | echo_vtab *pVtab = (echo_vtab *)tab; | ||
1006 | Tcl_Interp *interp = pVtab->interp; | ||
1007 | const char *zVal; | ||
1008 | |||
1009 | rc = echoTransactionCall(tab, "xSync"); | ||
1010 | |||
1011 | if( rc==SQLITE_OK ){ | ||
1012 | /* Check if the $::echo_module_sync_fail variable is defined. If it is, | ||
1013 | ** and it is set to the name of the real table underlying this virtual | ||
1014 | ** echo module table, then cause this xSync operation to fail. | ||
1015 | */ | ||
1016 | zVal = Tcl_GetVar(interp, "echo_module_sync_fail", TCL_GLOBAL_ONLY); | ||
1017 | if( zVal && 0==strcmp(zVal, pVtab->zTableName) ){ | ||
1018 | rc = -1; | ||
1019 | } | ||
1020 | } | ||
1021 | return rc; | ||
1022 | } | ||
1023 | static int echoCommit(sqlite3_vtab *tab){ | ||
1024 | sqlite3MallocBenignFailure(1); | ||
1025 | return echoTransactionCall(tab, "xCommit"); | ||
1026 | } | ||
1027 | static int echoRollback(sqlite3_vtab *tab){ | ||
1028 | return echoTransactionCall(tab, "xRollback"); | ||
1029 | } | ||
1030 | |||
1031 | /* | ||
1032 | ** Implementation of "GLOB" function on the echo module. Pass | ||
1033 | ** all arguments to the ::echo_glob_overload procedure of TCL | ||
1034 | ** and return the result of that procedure as a string. | ||
1035 | */ | ||
1036 | static void overloadedGlobFunction( | ||
1037 | sqlite3_context *pContext, | ||
1038 | int nArg, | ||
1039 | sqlite3_value **apArg | ||
1040 | ){ | ||
1041 | Tcl_Interp *interp = sqlite3_user_data(pContext); | ||
1042 | Tcl_DString str; | ||
1043 | int i; | ||
1044 | int rc; | ||
1045 | Tcl_DStringInit(&str); | ||
1046 | Tcl_DStringAppendElement(&str, "::echo_glob_overload"); | ||
1047 | for(i=0; i<nArg; i++){ | ||
1048 | Tcl_DStringAppendElement(&str, (char*)sqlite3_value_text(apArg[i])); | ||
1049 | } | ||
1050 | rc = Tcl_Eval(interp, Tcl_DStringValue(&str)); | ||
1051 | Tcl_DStringFree(&str); | ||
1052 | if( rc ){ | ||
1053 | sqlite3_result_error(pContext, Tcl_GetStringResult(interp), -1); | ||
1054 | }else{ | ||
1055 | sqlite3_result_text(pContext, Tcl_GetStringResult(interp), | ||
1056 | -1, SQLITE_TRANSIENT); | ||
1057 | } | ||
1058 | Tcl_ResetResult(interp); | ||
1059 | } | ||
1060 | |||
1061 | /* | ||
1062 | ** This is the xFindFunction implementation for the echo module. | ||
1063 | ** SQLite calls this routine when the first argument of a function | ||
1064 | ** is a column of an echo virtual table. This routine can optionally | ||
1065 | ** override the implementation of that function. It will choose to | ||
1066 | ** do so if the function is named "glob", and a TCL command named | ||
1067 | ** ::echo_glob_overload exists. | ||
1068 | */ | ||
1069 | static int echoFindFunction( | ||
1070 | sqlite3_vtab *vtab, | ||
1071 | int nArg, | ||
1072 | const char *zFuncName, | ||
1073 | void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), | ||
1074 | void **ppArg | ||
1075 | ){ | ||
1076 | echo_vtab *pVtab = (echo_vtab *)vtab; | ||
1077 | Tcl_Interp *interp = pVtab->interp; | ||
1078 | Tcl_CmdInfo info; | ||
1079 | if( strcmp(zFuncName,"glob")!=0 ){ | ||
1080 | return 0; | ||
1081 | } | ||
1082 | if( Tcl_GetCommandInfo(interp, "::echo_glob_overload", &info)==0 ){ | ||
1083 | return 0; | ||
1084 | } | ||
1085 | *pxFunc = overloadedGlobFunction; | ||
1086 | *ppArg = interp; | ||
1087 | return 1; | ||
1088 | } | ||
1089 | |||
1090 | static int echoRename(sqlite3_vtab *vtab, const char *zNewName){ | ||
1091 | int rc = SQLITE_OK; | ||
1092 | echo_vtab *p = (echo_vtab *)vtab; | ||
1093 | |||
1094 | if( p->isPattern ){ | ||
1095 | int nThis = strlen(p->zThis); | ||
1096 | char *zSql = sqlite3MPrintf(0, "ALTER TABLE %s RENAME TO %s%s", | ||
1097 | p->zTableName, zNewName, &p->zTableName[nThis] | ||
1098 | ); | ||
1099 | rc = sqlite3_exec(p->db, zSql, 0, 0, 0); | ||
1100 | sqlite3_free(zSql); | ||
1101 | } | ||
1102 | |||
1103 | return rc; | ||
1104 | } | ||
1105 | |||
1106 | /* | ||
1107 | ** A virtual table module that merely "echos" the contents of another | ||
1108 | ** table (like an SQL VIEW). | ||
1109 | */ | ||
1110 | static sqlite3_module echoModule = { | ||
1111 | 0, /* iVersion */ | ||
1112 | echoCreate, | ||
1113 | echoConnect, | ||
1114 | echoBestIndex, | ||
1115 | echoDisconnect, | ||
1116 | echoDestroy, | ||
1117 | echoOpen, /* xOpen - open a cursor */ | ||
1118 | echoClose, /* xClose - close a cursor */ | ||
1119 | echoFilter, /* xFilter - configure scan constraints */ | ||
1120 | echoNext, /* xNext - advance a cursor */ | ||
1121 | echoEof, /* xEof */ | ||
1122 | echoColumn, /* xColumn - read data */ | ||
1123 | echoRowid, /* xRowid - read data */ | ||
1124 | echoUpdate, /* xUpdate - write data */ | ||
1125 | echoBegin, /* xBegin - begin transaction */ | ||
1126 | echoSync, /* xSync - sync transaction */ | ||
1127 | echoCommit, /* xCommit - commit transaction */ | ||
1128 | echoRollback, /* xRollback - rollback transaction */ | ||
1129 | echoFindFunction, /* xFindFunction - function overloading */ | ||
1130 | echoRename, /* xRename - rename the table */ | ||
1131 | }; | ||
1132 | |||
1133 | /* | ||
1134 | ** Decode a pointer to an sqlite3 object. | ||
1135 | */ | ||
1136 | static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){ | ||
1137 | *ppDb = (sqlite3*)sqlite3TextToPtr(zA); | ||
1138 | return TCL_OK; | ||
1139 | } | ||
1140 | |||
1141 | static void moduleDestroy(void *p){ | ||
1142 | sqlite3_free(p); | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | ** Register the echo virtual table module. | ||
1147 | */ | ||
1148 | static int register_echo_module( | ||
1149 | ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ | ||
1150 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | ||
1151 | int objc, /* Number of arguments */ | ||
1152 | Tcl_Obj *CONST objv[] /* Command arguments */ | ||
1153 | ){ | ||
1154 | sqlite3 *db; | ||
1155 | EchoModule *pMod; | ||
1156 | if( objc!=2 ){ | ||
1157 | Tcl_WrongNumArgs(interp, 1, objv, "DB"); | ||
1158 | return TCL_ERROR; | ||
1159 | } | ||
1160 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | ||
1161 | pMod = sqlite3_malloc(sizeof(EchoModule)); | ||
1162 | pMod->interp = interp; | ||
1163 | sqlite3_create_module_v2(db, "echo", &echoModule, (void*)pMod, moduleDestroy); | ||
1164 | return TCL_OK; | ||
1165 | } | ||
1166 | |||
1167 | /* | ||
1168 | ** Tcl interface to sqlite3_declare_vtab, invoked as follows from Tcl: | ||
1169 | ** | ||
1170 | ** sqlite3_declare_vtab DB SQL | ||
1171 | */ | ||
1172 | static int declare_vtab( | ||
1173 | ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ | ||
1174 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | ||
1175 | int objc, /* Number of arguments */ | ||
1176 | Tcl_Obj *CONST objv[] /* Command arguments */ | ||
1177 | ){ | ||
1178 | sqlite3 *db; | ||
1179 | int rc; | ||
1180 | if( objc!=3 ){ | ||
1181 | Tcl_WrongNumArgs(interp, 1, objv, "DB SQL"); | ||
1182 | return TCL_ERROR; | ||
1183 | } | ||
1184 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; | ||
1185 | rc = sqlite3_declare_vtab(db, Tcl_GetString(objv[2])); | ||
1186 | if( rc!=SQLITE_OK ){ | ||
1187 | Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE); | ||
1188 | return TCL_ERROR; | ||
1189 | } | ||
1190 | return TCL_OK; | ||
1191 | } | ||
1192 | |||
1193 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ | ||
1194 | |||
1195 | /* | ||
1196 | ** Register commands with the TCL interpreter. | ||
1197 | */ | ||
1198 | int Sqlitetest8_Init(Tcl_Interp *interp){ | ||
1199 | static struct { | ||
1200 | char *zName; | ||
1201 | Tcl_ObjCmdProc *xProc; | ||
1202 | void *clientData; | ||
1203 | } aObjCmd[] = { | ||
1204 | #ifndef SQLITE_OMIT_VIRTUALTABLE | ||
1205 | { "register_echo_module", register_echo_module, 0 }, | ||
1206 | { "sqlite3_declare_vtab", declare_vtab, 0 }, | ||
1207 | #endif | ||
1208 | }; | ||
1209 | int i; | ||
1210 | for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ | ||
1211 | Tcl_CreateObjCommand(interp, aObjCmd[i].zName, | ||
1212 | aObjCmd[i].xProc, aObjCmd[i].clientData, 0); | ||
1213 | } | ||
1214 | return TCL_OK; | ||
1215 | } | ||