wasCSharpSQLite – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Diagnostics;
3 using System.Text;
4  
5 using sqlite_int64 = System.Int64;
6  
7 namespace Community.CsharpSqlite
8 {
9 #if TCLSH
10 using tcl.lang;
11 using sqlite3_stmt = Sqlite3.Vdbe;
12 using sqlite3_value = Sqlite3.Mem;
13 using Tcl_CmdInfo = tcl.lang.WrappedCommand;
14 using Tcl_DString = tcl.lang.TclString;
15 using Tcl_Interp = tcl.lang.Interp;
16 using Tcl_Obj = tcl.lang.TclObject;
17 using ClientData = System.Object;
18  
19  
20 public partial class Sqlite3
21 {
22 /*
23 ** 2006 June 10
24 **
25 ** The author disclaims copyright to this source code. In place of
26 ** a legal notice, here is a blessing:
27 **
28 ** May you do good and not evil.
29 ** May you find forgiveness for yourself and forgive others.
30 ** May you share freely, never taking more than you give.
31 **
32 *************************************************************************
33 ** Code for testing the virtual table interfaces. This code
34 ** is not included in the SQLite library. It is used for automated
35 ** testing of the SQLite library.
36 *************************************************************************
37 ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
38 ** C#-SQLite is an independent reimplementation of the SQLite software library
39 **
40 ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
41 **
42 *************************************************************************
43 */
44 //#include "sqliteInt.h"
45 //#include "tcl.h"
46 //#include <stdlib.h>
47 //#include <string.h>
48  
49 #if !SQLITE_OMIT_VIRTUALTABLE
50  
51 //typedef struct echo_vtab echo_vtab;
52 //typedef struct echo_cursor echo_cursor;
53  
54 /*
55 ** The test module defined in this file uses four global Tcl variables to
56 ** commicate with test-scripts:
57 **
58 ** $::echo_module
59 ** $::echo_module_sync_fail
60 ** $::echo_module_begin_fail
61 ** $::echo_module_cost
62 **
63 ** The variable ::echo_module is a list. Each time one of the following
64 ** methods is called, one or more elements are appended to the list.
65 ** This is used for automated testing of virtual table modules.
66 **
67 ** The ::echo_module_sync_fail variable is set by test scripts and read
68 ** by code in this file. If it is set to the name of a real table in the
69 ** the database, then all xSync operations on echo virtual tables that
70 ** use the named table as a backing store will fail.
71 */
72  
73 /*
74 ** Errors can be provoked within the following echo virtual table methods:
75 **
76 ** xBestIndex xOpen xFilter xNext
77 ** xColumn xRowid xUpdate xSync
78 ** xBegin xRename
79 **
80 ** This is done by setting the global tcl variable:
81 **
82 ** echo_module_fail($method,$tbl)
83 **
84 ** where $method is set to the name of the virtual table method to fail
85 ** (i.e. "xBestIndex") and $tbl is the name of the table being echoed (not
86 ** the name of the virtual table, the name of the underlying real table).
87 */
88  
89 /*
90 ** An echo virtual-table object.
91 **
92 ** echo.vtab.aIndex is an array of booleans. The nth entry is true if
93 ** the nth column of the real table is the left-most column of an index
94 ** (implicit or otherwise). In other words, if SQLite can optimize
95 ** a query like "SELECT * FROM real_table WHERE col = ?".
96 **
97 ** Member variable aCol[] contains copies of the column names of the real
98 ** table.
99 */
100 class echo_vtab : sqlite3_vtab
101 {
102 //public sqlite3_vtab base;
103 public Tcl_Interp interp; /* Tcl interpreter containing debug variables */
104 public sqlite3 db; /* Database connection */
105  
106 public int isPattern;
107 public int inTransaction; /* True if within a transaction */
108 public string zThis; /* Name of the echo table */
109 public string zTableName; /* Name of the real table */
110 public string zLogName; /* Name of the log table */
111 public int nCol; /* Number of columns in the real table */
112 public int[] aIndex; /* Array of size nCol. True if column has an index */
113 public string[] aCol; /* Array of size nCol. Column names */
114 };
115  
116 /* An echo cursor object */
117 class echo_cursor : sqlite3_vtab_cursor
118 {
119 //public sqlite3_vtab_cursor base;
120 public sqlite3_stmt pStmt;
121 };
122  
123 static int simulateVtabError( echo_vtab p, string zMethod )
124 {
125 string zErr;
126 StringBuilder zVarname = new StringBuilder( 128 );
127 //zVarname[127] = '\0';
128 sqlite3_snprintf( 127, zVarname, "echo_module_fail(%s,%s)", zMethod, p.zTableName );
129 zErr = TCL.Tcl_GetVar( p.interp, zVarname.ToString(), (TCL.VarFlag)TCL.TCL_GLOBAL_ONLY ).ToString();
130 if ( zErr != "" )
131 {
132 p.zErrMsg = sqlite3_mprintf( "echo-vtab-error: %s", zErr );
133 }
134 return ( zErr != "" ? 1 : 0 );
135 }
136  
137 /*
138 ** Convert an SQL-style quoted string into a normal string by removing
139 ** the quote characters. The conversion is done in-place. If the
140 ** input does not begin with a quote character, then this routine
141 ** is a no-op.
142 **
143 ** Examples:
144 **
145 ** "abc" becomes abc
146 ** 'xyz' becomes xyz
147 ** [pqr] becomes pqr
148 ** `mno` becomes mno
149 */
150 static void dequoteString( ref string z )
151 {
152 //int quote;
153 //int i, j;
154 if ( String.IsNullOrEmpty( z ) )
155 return;
156 sqlite3Dequote( ref z );
157 //quote = z[0];
158 //switch( quote ){
159 // case '\'': break;
160 // case '"': break;
161 // case '`': break; /* For MySQL compatibility */
162 // case '[': quote = ']'; break; /* For MS SqlServer compatibility */
163 // default: return;
164 //}
165 //for(i=1, j=0; z[i]; i++){
166 // if( z[i]==quote ){
167 // if( z[i+1]==quote ){
168 // z[j++] = quote;
169 // i++;
170 // }else{
171 // z[j++] = 0;
172 // break;
173 // }
174 // }else{
175 // z[j++] = z[i];
176 // }
177 //}
178 }
179  
180 /*
181 ** Retrieve the column names for the table named zTab via database
182 ** connection db. SQLITE_OK is returned on success, or an sqlite error
183 ** code otherwise.
184 **
185 ** If successful, the number of columns is written to pnCol. paCol is
186 ** set to point at sqlite3_malloc()'d space containing the array of
187 ** nCol column names. The caller is responsible for calling sqlite3_free
188 ** on paCol.
189 */
190 static int getColumnNames(
191 sqlite3 db,
192 string zTab,
193 out string[] paCol,
194 out int pnCol
195 )
196 {
197 string[] aCol = null;
198 string zSql;
199 sqlite3_stmt pStmt = null;
200 int rc = SQLITE_OK;
201 int nCol = 0;
202  
203 /* Prepare the statement "SELECT * FROM <tbl>". The column names
204 ** of the result set of the compiled SELECT will be the same as
205 ** the column names of table <tbl>.
206 */
207 zSql = sqlite3_mprintf( "SELECT * FROM %Q", zTab );
208 //if( null==zSql ){
209 // rc = SQLITE_NOMEM;
210 // goto _out;
211 //}
212 rc = sqlite3_prepare( db, zSql, -1, ref pStmt, 0 );
213 //sqlite3_free(zSql);
214  
215 if ( rc == SQLITE_OK )
216 {
217 int ii;
218 int nBytes;
219 string zSpace;
220 nCol = sqlite3_column_count( pStmt );
221  
222 /* Figure out how much space to allocate for the array of column names
223 ** (including space for the strings themselves). Then allocate it.
224 */
225 nBytes = sizeof( char ) * nCol;
226 for ( ii = 0; ii < nCol; ii++ )
227 {
228 string zName = sqlite3_column_name( pStmt, ii );
229 //if( null==zName ){
230 // rc = SQLITE_NOMEM;
231 // goto _out;
232 //}
233 nBytes += zName.Length + 1;//strlen( zName ) + 1;
234 }
235 aCol = new string[nCol];//(char )sqlite3MallocZero(nBytes);
236 //if( null==aCol ){
237 // rc = SQLITE_NOMEM;
238 // goto out_;
239 //}
240  
241 /* Copy the column names into the allocated space and set up the
242 ** pointers in the aCol[] array.
243 */
244 //zSpace = (char)( &aCol[nCol] );
245 for ( ii = 0; ii < nCol; ii++ )
246 {
247 //aCol[ii] = zSpace;
248 //zSpace += sprintf( zSpace, "%s", sqlite3_column_name( pStmt, ii ) );
249 //zSpace++;
250 aCol[ii] = sqlite3_column_name( pStmt, ii );
251 }
252 //Debug.Assert( (zSpace-nBytes)==(char )aCol );
253 }
254  
255 paCol = aCol;
256 pnCol = nCol;
257  
258 //_out:
259 sqlite3_finalize( pStmt );
260 return rc;
261 }
262  
263 /*
264 ** Parameter zTab is the name of a table in database db with nCol
265 ** columns. This function allocates an array of integers nCol in
266 ** size and populates it according to any implicit or explicit
267 ** indices on table zTab.
268 **
269 ** If successful, SQLITE_OK is returned and paIndex set to point
270 ** at the allocated array. Otherwise, an error code is returned.
271 **
272 ** See comments associated with the member variable aIndex above
273 ** "struct echo_vtab" for details of the contents of the array.
274 */
275 static int getIndexArray(
276 sqlite3 db, /* Database connection */
277 string zTab, /* Name of table in database db */
278 int nCol,
279 ref int[] paIndex
280 )
281 {
282 sqlite3_stmt pStmt = null;
283 int[] aIndex = null;
284 int rc = 0;
285 string zSql;
286  
287 /* Allocate space for the index array */
288 aIndex = new int[nCol];//(int )sqlite3MallocZero(sizeof(int) * nCol);
289 // if( null==aIndex ){
290 // rc = SQLITE_NOMEM;
291 // goto get_index_array_out;
292 // }
293  
294 /* Compile an sqlite pragma to loop through all indices on table zTab */
295 zSql = sqlite3_mprintf( "PRAGMA index_list(%s)", zTab );
296 // if( null==zSql ){
297 // rc = SQLITE_NOMEM;
298 // goto get_index_array_out;
299 // }
300 rc = sqlite3_prepare( db, zSql, -1, ref pStmt, 0 );
301 // //sqlite3_free(zSql);
302  
303 /* For each index, figure out the left-most column and set the
304 ** corresponding entry in aIndex[] to 1.
305 */
306 while ( pStmt != null && sqlite3_step( pStmt ) == SQLITE_ROW )
307 {
308 string zIdx = (string)sqlite3_column_text( pStmt, 1 );
309 sqlite3_stmt pStmt2 = null;
310 zSql = sqlite3_mprintf( "PRAGMA index_info(%s)", zIdx );
311 //if ( null == zSql )
312 //{
313 // rc = SQLITE_NOMEM;
314 // goto get_index_array_out;
315 //}
316 rc = sqlite3_prepare( db, zSql, -1, ref pStmt2, 0 );
317 //sqlite3_free(zSql);
318 if ( pStmt2 != null && sqlite3_step( pStmt2 ) == SQLITE_ROW )
319 {
320 int cid = sqlite3_column_int( pStmt2, 1 );
321 Debug.Assert( cid >= 0 && cid < nCol );
322 aIndex[cid] = 1;
323 }
324 if ( pStmt2 != null )
325 {
326 rc = sqlite3_finalize( pStmt2 );
327 }
328 if ( rc != SQLITE_OK )
329 {
330 goto get_index_array_out;
331 }
332 }
333  
334  
335 get_index_array_out:
336 if ( pStmt != null )
337 {
338 int rc2 = sqlite3_finalize( pStmt );
339 if ( rc == SQLITE_OK )
340 {
341 rc = rc2;
342 }
343 }
344 if ( rc != SQLITE_OK )
345 {
346 //sqlite3_free(aIndex);
347 aIndex = null;
348 }
349 paIndex = aIndex;
350 return rc;
351 }
352  
353 /*
354 ** Global Tcl variable $echo_module is a list. This routine appends
355 ** the string element zArg to that list in interpreter interp.
356 */
357 static void appendToEchoModule( Tcl_Interp interp, string zArg )
358 {
359 int flags = ( TCL.TCL_APPEND_VALUE | TCL.TCL_LIST_ELEMENT | TCL.TCL_GLOBAL_ONLY );
360 TCL.Tcl_SetVar( interp, "echo_module", ( zArg != null ? zArg : "" ), flags );
361 }
362  
363 /*
364 ** This function is called from within the echo-modules xCreate and
365 ** xConnect methods. The argc and argv arguments are copies of those
366 ** passed to the calling method. This function is responsible for
367 ** calling sqlite3_declare_vtab() to declare the schema of the virtual
368 ** table being created or connected.
369 **
370 ** If the constructor was passed just one argument, i.e.:
371 **
372 ** CREATE TABLE t1 AS echo(t2);
373 **
374 ** Then t2 is assumed to be the name of a *real* database table. The
375 ** schema of the virtual table is declared by passing a copy of the
376 ** CREATE TABLE statement for the real table to sqlite3_declare_vtab().
377 ** Hence, the virtual table should have exactly the same column names and
378 ** types as the real table.
379 */
380 static int echoDeclareVtab(
381 echo_vtab pVtab,
382 sqlite3 db
383 )
384 {
385 int rc = SQLITE_OK;
386  
387 if ( pVtab.zTableName != null )
388 {
389 sqlite3_stmt pStmt = null;
390 rc = sqlite3_prepare( db,
391 "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?",
392 -1, ref pStmt, 0 );
393 if ( rc == SQLITE_OK )
394 {
395 sqlite3_bind_text( pStmt, 1, pVtab.zTableName, -1, null );
396 if ( sqlite3_step( pStmt ) == SQLITE_ROW )
397 {
398 int rc2;
399 string zCreateTable = (string)sqlite3_column_text( pStmt, 0 );
400 rc = sqlite3_declare_vtab( db, zCreateTable );
401 rc2 = sqlite3_finalize( pStmt );
402 if ( rc == SQLITE_OK )
403 {
404 rc = rc2;
405 }
406 }
407 else
408 {
409 rc = sqlite3_finalize( pStmt );
410 if ( rc == SQLITE_OK )
411 {
412 rc = SQLITE_ERROR;
413 }
414 }
415 if ( rc == SQLITE_OK )
416 {
417 rc = getColumnNames( db, pVtab.zTableName, out pVtab.aCol, out pVtab.nCol );
418 }
419 if ( rc == SQLITE_OK )
420 {
421 rc = getIndexArray( db, pVtab.zTableName, pVtab.nCol, ref pVtab.aIndex );
422 }
423 }
424 }
425  
426 return rc;
427 }
428  
429 /*
430 ** This function frees all runtime structures associated with the virtual
431 ** table pVtab.
432 */
433 static int echoDestructor( ref object pVtab )
434 {
435 //echo_vtab p = (echo_vtab)pVtab;
436 //sqlite3_free(p.aIndex);
437 //sqlite3_free(p.aCol);
438 //sqlite3_free(p.zThis);
439 //sqlite3_free(p.zTableName);
440 //sqlite3_free(p.zLogName);
441 //sqlite3_free(p);
442 pVtab = null;
443 return 0;
444 }
445  
446 //typedef struct EchoModule EchoModule;
447 class EchoModule : sqlite3_vtab
448 {
449 public Tcl_Interp interp;
450 };
451  
452 /*
453 ** This function is called to do the work of the xConnect() method -
454 ** to allocate the required in-memory structures for a newly connected
455 ** virtual table.
456 */
457 static int echoConstructor(
458 sqlite3 db,
459 object pAux,
460 int argc, string[] argv,
461 out sqlite3_vtab ppVtab,
462 out string pzErr
463 )
464 {
465 int rc;
466 int i;
467 echo_vtab pVtab;
468 pzErr = "";
469  
470 /* Allocate the sqlite3_vtab/echo_vtab structure itself */
471 pVtab = new echo_vtab();//sqlite3MallocZero( sizeof(*pVtab) );
472 //if( null==pVtab ){
473 // return SQLITE_NOMEM;
474 //}
475 pVtab.interp = ( (EchoModule)pAux ).interp;
476 pVtab.db = db;
477  
478 /* Allocate echo_vtab.zThis */
479 pVtab.zThis = sqlite3_mprintf( "%s", argv[2] );
480 //if( null==pVtab.zThis ){
481 // object obj = ppVtab;
482 // echoDestructor( ref obj );
483 // obj = null;
484 // return SQLITE_NOMEM;
485 //}
486  
487 /* Allocate echo_vtab.zTableName */
488 if ( argc > 3 )
489 {
490 pVtab.zTableName = sqlite3_mprintf( "%s", argv[3] );
491 dequoteString( ref pVtab.zTableName );
492 if ( !String.IsNullOrEmpty( pVtab.zTableName ) && pVtab.zTableName[0] == '*' )
493 {
494 string z = sqlite3_mprintf( "%s%s", argv[2], ( pVtab.zTableName.Substring(1) ) );
495 //sqlite3_free(pVtab.zTableName);
496 pVtab.zTableName = z;
497 pVtab.isPattern = 1;
498 }
499 //if( null==pVtab.zTableName ){
500 // object obj = ppVtab;
501 // echoDestructor( ref obj );
502 // obj = null;
503 // return SQLITE_NOMEM;
504 //}
505 }
506  
507 /* Log the arguments to this function to Tcl var ::echo_module */
508 for ( i = 0; i < argc; i++ )
509 {
510 appendToEchoModule( pVtab.interp, argv[i] );
511 }
512  
513 /* Invoke sqlite3_declare_vtab and set up other members of the echo_vtab
514 ** structure. If an error occurs, delete the sqlite3_vtab structure and
515 ** return an error code.
516 */
517 rc = echoDeclareVtab( pVtab, db );
518 if ( rc != SQLITE_OK )
519 {
520 //object obj = ppVtab;
521 //echoDestructor( ref obj );
522 //obj = null;
523 ppVtab = null;
524 return rc;
525 }
526  
527 /* Success. Set ppVtab and return */
528 ppVtab = pVtab;//.base;
529 return SQLITE_OK;
530 }
531  
532 /*
533 ** Echo virtual table module xCreate method.
534 */
535 static int echoCreate(
536 sqlite3 db,
537 object pAux,
538 int argc,
539 string[] argv,
540 out sqlite3_vtab ppVtab,
541 out string pzErr
542 )
543 {
544 int rc = SQLITE_OK;
545 appendToEchoModule( ( (EchoModule)pAux ).interp, "xCreate" );
546 rc = echoConstructor( db, pAux, argc, argv, out ppVtab, out pzErr );
547  
548 /* If there were two arguments passed to the module at the SQL level
549 ** (i.e. "CREATE VIRTUAL TABLE tbl USING echo(arg1, arg2)"), then
550 ** the second argument is used as a table name. Attempt to create
551 ** such a table with a single column, "logmsg". This table will
552 ** be used to log calls to the xUpdate method. It will be deleted
553 ** when the virtual table is DROPed.
554 **
555 ** Note: The main point of this is to test that we can drop tables
556 ** from within an xDestroy method call.
557 */
558 if ( rc == SQLITE_OK && argc == 5 )
559 {
560 string zSql;
561 echo_vtab pVtab = (echo_vtab)ppVtab;
562 pVtab.zLogName = sqlite3_mprintf( "%s", argv[4] );
563 zSql = sqlite3_mprintf( "CREATE TABLE %Q(logmsg)", pVtab.zLogName );
564 rc = sqlite3_exec( db, zSql, 0, 0, 0 );
565 //sqlite3_free(zSql);
566 if ( rc != SQLITE_OK )
567 {
568 pzErr = sqlite3_mprintf( "%s", sqlite3_errmsg( db ) );
569 }
570 }
571  
572 if ( ppVtab != null && rc != SQLITE_OK )
573 {
574 //object obj = ppVtab;
575 //echoDestructor( ref obj );
576 //obj = null;
577 ppVtab = null;
578 }
579  
580 if ( rc == SQLITE_OK )
581 {
582 ( (echo_vtab)ppVtab ).inTransaction = 1;
583 }
584  
585 return rc;
586 }
587  
588 /*
589 ** Echo virtual table module xConnect method.
590 */
591 static int echoConnect(
592 sqlite3 db,
593 object pAux,
594 int argc,
595 string[] argv,
596 out sqlite3_vtab ppVtab,
597 out string pzErr
598 )
599 {
600 appendToEchoModule( ( (EchoModule)pAux ).interp, "xConnect" );
601 return echoConstructor( db, pAux, argc, argv, out ppVtab, out pzErr );
602 }
603  
604 /*
605 ** Echo virtual table module xDisconnect method.
606 */
607 static int echoDisconnect( ref object pVtab )
608 {
609 appendToEchoModule( ( (echo_vtab)pVtab ).interp, "xDisconnect" );
610 return echoDestructor( ref pVtab );
611 }
612  
613 /*
614 ** Echo virtual table module xDestroy method.
615 */
616 static int echoDestroy( ref object pVtab )
617 {
618 int rc = SQLITE_OK;
619 echo_vtab p = (echo_vtab)pVtab;
620 appendToEchoModule( ( (echo_vtab)pVtab ).interp, "xDestroy" );
621  
622 /* Drop the "log" table, if one exists (see echoCreate() for details) */
623 if ( p != null && !String.IsNullOrEmpty( p.zLogName ) )
624 {
625 string zSql;
626 zSql = sqlite3_mprintf( "DROP TABLE %Q", p.zLogName );
627 rc = sqlite3_exec( p.db, zSql, 0, 0, 0 );
628 //sqlite3_free(zSql);
629 }
630  
631 if ( rc == SQLITE_OK )
632 {
633 rc = echoDestructor( ref pVtab );
634 }
635 return rc;
636 }
637  
638 /*
639 ** Echo virtual table module xOpen method.
640 */
641 static int echoOpen( sqlite3_vtab pVTab, out sqlite3_vtab_cursor ppCursor )
642 {
643 echo_cursor pCur;
644 if ( simulateVtabError( (echo_vtab)pVTab, "xOpen" ) != 0 )
645 {
646 ppCursor = null;
647 return SQLITE_ERROR;
648 }
649 pCur = new echo_cursor();//sqlite3MallocZero( sizeof( echo_cursor ) );
650 ppCursor = (sqlite3_vtab_cursor)pCur;
651 return ( pCur != null ? SQLITE_OK : SQLITE_NOMEM );
652 }
653  
654 /*
655 ** Echo virtual table module xClose method.
656 */
657 static int echoClose( ref sqlite3_vtab_cursor cur )
658 {
659 int rc = 0;
660 echo_cursor pCur = (echo_cursor)cur;
661 sqlite3_stmt pStmt = pCur.pStmt;
662 pCur.pStmt = null;
663 //sqlite3_free(pCur);
664 pCur = null;
665 rc = sqlite3_finalize( pStmt );
666 return rc;
667 }
668  
669 /*
670 ** Return non-zero if the cursor does not currently point to a valid record
671 ** (i.e if the scan has finished), or zero otherwise.
672 */
673 static int echoEof( sqlite3_vtab_cursor cur )
674 {
675 return ( ( (echo_cursor)cur ).pStmt != null ? 0 : 1 );
676 }
677  
678 /*
679 ** Echo virtual table module xNext method.
680 */
681 static int echoNext( sqlite3_vtab_cursor cur )
682 {
683 int rc = SQLITE_OK;
684 echo_cursor pCur = (echo_cursor)cur;
685  
686 if ( simulateVtabError( (echo_vtab)( cur.pVtab ), "xNext" ) != 0 )
687 {
688 return SQLITE_ERROR;
689 }
690  
691 if ( pCur.pStmt != null )
692 {
693 rc = sqlite3_step( pCur.pStmt );
694 if ( rc == SQLITE_ROW )
695 {
696 rc = SQLITE_OK;
697 }
698 else
699 {
700 rc = sqlite3_finalize( pCur.pStmt );
701 pCur.pStmt = null;
702 }
703 }
704  
705 return rc;
706 }
707  
708 /*
709 ** Echo virtual table module xColumn method.
710 */
711 static int echoColumn( sqlite3_vtab_cursor cur, sqlite3_context ctx, int i )
712 {
713 int iCol = i + 1;
714 sqlite3_stmt pStmt = ( (echo_cursor)cur ).pStmt;
715  
716 if ( simulateVtabError( (echo_vtab)( cur.pVtab ), "xColumn" ) != 0 )
717 {
718 return SQLITE_ERROR;
719 }
720  
721 if ( null == pStmt )
722 {
723 sqlite3_result_null( ctx );
724 }
725 else
726 {
727 Debug.Assert( sqlite3_data_count( pStmt ) > iCol );
728 sqlite3_result_value( ctx, sqlite3_column_value( pStmt, iCol ) );
729 }
730 return SQLITE_OK;
731 }
732  
733 /*
734 ** Echo virtual table module xRowid method.
735 */
736 static int echoRowid( sqlite3_vtab_cursor cur, out sqlite_int64 pRowid )
737 {
738 sqlite3_stmt pStmt = ( (echo_cursor)cur ).pStmt;
739  
740 if ( simulateVtabError( (echo_vtab)( cur.pVtab ), "xRowid" ) != 0 )
741 {
742 pRowid = 0;
743 return SQLITE_ERROR;
744 }
745  
746 pRowid = sqlite3_column_int64( pStmt, 0 );
747 return SQLITE_OK;
748 }
749  
750 /*
751 ** Compute a simple hash of the null terminated string zString.
752 **
753 ** This module uses only sqlite3_index_info.idxStr, not
754 ** sqlite3_index_info.idxNum. So to test idxNum, when idxStr is set
755 ** in echoBestIndex(), idxNum is set to the corresponding hash value.
756 ** In echoFilter(), code Debug.Assert()s that the supplied idxNum value is
757 ** indeed the hash of the supplied idxStr.
758 */
759 static int hashString( string zString )
760 {
761 int val = 0;
762 int ii;
763 for ( ii = 0; ii < zString.Length; ii++ )
764 {
765 val = ( val << 3 ) + (int)zString[ii];
766 }
767 return val;
768 }
769  
770 /*
771 ** Echo virtual table module xFilter method.
772 */
773 static int echoFilter(
774 sqlite3_vtab_cursor pVtabCursor,
775 int idxNum, string idxStr,
776 int argc, sqlite3_value[] argv
777 )
778 {
779 int rc;
780 int i;
781  
782 echo_cursor pCur = (echo_cursor)pVtabCursor;
783 echo_vtab pVtab = (echo_vtab)pVtabCursor.pVtab;
784 sqlite3 db = pVtab.db;
785  
786 if ( simulateVtabError( pVtab, "xFilter" ) != 0 )
787 {
788 return SQLITE_ERROR;
789 }
790  
791 /* Check that idxNum matches idxStr */
792 Debug.Assert( idxNum == hashString( idxStr ) );
793  
794 /* Log arguments to the ::echo_module Tcl variable */
795 appendToEchoModule( pVtab.interp, "xFilter" );
796 appendToEchoModule( pVtab.interp, idxStr );
797 for ( i = 0; i < argc; i++ )
798 {
799 appendToEchoModule( pVtab.interp, sqlite3_value_text( argv[i] ) );
800 }
801  
802 sqlite3_finalize( pCur.pStmt );
803 pCur.pStmt = null;
804  
805 /* Prepare the SQL statement created by echoBestIndex and bind the
806 ** runtime parameters passed to this function to it.
807 */
808 rc = sqlite3_prepare( db, idxStr, -1, ref pCur.pStmt, 0 );
809 Debug.Assert( pCur.pStmt != null || rc != SQLITE_OK );
810 for ( i = 0; rc == SQLITE_OK && i < argc; i++ )
811 {
812 rc = sqlite3_bind_value( pCur.pStmt, i + 1, argv[i] );
813 }
814  
815 /* If everything was successful, advance to the first row of the scan */
816 if ( rc == SQLITE_OK )
817 {
818 rc = echoNext( pVtabCursor );
819 }
820  
821 return rc;
822 }
823  
824  
825 /*
826 ** A helper function used by echoUpdate() and echoBestIndex() for
827 ** manipulating strings in concert with the sqlite3_mprintf() function.
828 **
829 ** Parameter pzStr points to a pointer to a string allocated with
830 ** sqlite3_mprintf. The second parameter, zAppend, points to another
831 ** string. The two strings are concatenated together and pzStr
832 ** set to point at the result. The initial buffer pointed to by pzStr
833 ** is deallocated via sqlite3_free().
834 **
835 ** If the third argument, doFree, is true, then sqlite3_free() is
836 ** also called to free the buffer pointed to by zAppend.
837 */
838 static void string_concat( ref string pzStr, string zAppend, int doFree, ref int pRc )
839 {
840 string zIn = pzStr;
841 if ( null == zAppend && doFree != 0 && pRc == SQLITE_OK )
842 {
843 pRc = SQLITE_NOMEM;
844 }
845 if ( pRc != SQLITE_OK )
846 {
847 //sqlite3_free(zIn);
848 zIn = null;
849 }
850 else
851 {
852 if ( zIn != null )
853 {
854 string zTemp = zIn;
855 zIn = sqlite3_mprintf( "%s%s", zIn, zAppend );
856 //sqlite3_free(zTemp);
857 }
858 else
859 {
860 zIn = sqlite3_mprintf( "%s", zAppend );
861 }
862 //if ( null == zIn )
863 //{
864 // pRc = SQLITE_NOMEM;
865 //}
866 }
867 pzStr = zIn;
868 //if( doFree ){
869 // sqlite3_free(zAppend);
870 //}
871 }
872  
873 /*
874 ** The echo module implements the subset of query constraints and sort
875 ** orders that may take advantage of SQLite indices on the underlying
876 ** real table. For example, if the real table is declared as:
877 **
878 ** CREATE TABLE real(a, b, c);
879 ** CREATE INDEX real_index ON real(b);
880 **
881 ** then the echo module handles WHERE or ORDER BY clauses that refer
882 ** to the column "b", but not "a" or "c". If a multi-column index is
883 ** present, only its left most column is considered.
884 **
885 ** This xBestIndex method encodes the proposed search strategy as
886 ** an SQL query on the real table underlying the virtual echo module
887 ** table and stores the query in sqlite3_index_info.idxStr. The SQL
888 ** statement is of the form:
889 **
890 ** SELECT rowid, * FROM <real-table> ?<where-clause>? ?<order-by-clause>?
891 **
892 ** where the <where-clause> and <order-by-clause> are determined
893 ** by the contents of the structure pointed to by the pIdxInfo argument.
894 */
895 static int echoBestIndex( sqlite3_vtab vtab, ref sqlite3_index_info pIdxInfo )
896 {
897 int ii;
898 string zQuery = "";
899 string zNew;
900 int nArg = 0;
901 string zSep = "WHERE";
902 echo_vtab pVtab = (echo_vtab)vtab;
903 sqlite3_stmt pStmt = null;
904 Tcl_Interp interp = pVtab.interp;
905  
906 int nRow = 0;
907 int useIdx = 0;
908 int rc = SQLITE_OK;
909 int useCost = 0;
910 double cost = 0;
911 int isIgnoreUsable = 0;
912 if ( TCL.Tcl_GetVar( interp, "echo_module_ignore_usable", (TCL.VarFlag)TCL.TCL_GLOBAL_ONLY ).ToString() != "" )
913 {
914 isIgnoreUsable = 1;
915 }
916  
917 if ( simulateVtabError( pVtab, "xBestIndex" ) != 0 )
918 {
919 return SQLITE_ERROR;
920 }
921  
922 /* Determine the number of rows in the table and store this value in local
923 ** variable nRow. The 'estimated-cost' of the scan will be the number of
924 ** rows in the table for a linear scan, or the log (base 2) of the
925 ** number of rows if the proposed scan uses an index.
926 */
927 if ( TCL.Tcl_GetVar( interp, "echo_module_cost", (TCL.VarFlag)TCL.TCL_GLOBAL_ONLY ).ToString() != "" )
928 {
929 Double.TryParse( TCL.Tcl_GetVar( interp, "echo_module_cost", (TCL.VarFlag)TCL.TCL_GLOBAL_ONLY ).ToString(), out cost );
930 useCost = 1;
931 }
932 else
933 {
934 zQuery = sqlite3_mprintf( "SELECT count() FROM %Q", pVtab.zTableName );
935 //if ( null == zQuery )
936 //{
937 // return SQLITE_NOMEM;
938 //}
939 rc = sqlite3_prepare( pVtab.db, zQuery, -1, ref pStmt, 0 );
940 //sqlite3_free(zQuery);
941 if ( rc != SQLITE_OK )
942 {
943 return rc;
944 }
945 sqlite3_step( pStmt );
946 nRow = sqlite3_column_int( pStmt, 0 );
947 rc = sqlite3_finalize( pStmt );
948 if ( rc != SQLITE_OK )
949 {
950 return rc;
951 }
952 }
953  
954 zQuery = sqlite3_mprintf( "SELECT rowid, * FROM %Q", pVtab.zTableName );
955 //if ( null == zQuery )
956 //{
957 // return SQLITE_NOMEM;
958 //}
959 for ( ii = 0; ii < pIdxInfo.nConstraint; ii++ )
960 {
961 sqlite3_index_constraint pConstraint;
962 sqlite3_index_constraint_usage pUsage;
963 int iCol;
964  
965 pConstraint = pIdxInfo.aConstraint[ii];
966 pUsage = pIdxInfo.aConstraintUsage[ii];
967  
968 if ( 0 == isIgnoreUsable && !pConstraint.usable )
969 continue;
970  
971 iCol = pConstraint.iColumn;
972 if ( iCol < 0 || pVtab.aIndex[iCol] != 0 )
973 {
974 string zCol;
975 string zOp = "";
976 useIdx = 1;
977 if ( iCol >= 0 )
978 {
979 zCol = pVtab.aCol[iCol];
980 }
981 else
982 {
983 zCol = "rowid";
984 }
985 switch ( pConstraint.op )
986 {
987 case SQLITE_INDEX_CONSTRAINT_EQ:
988 zOp = "=";
989 break;
990 case SQLITE_INDEX_CONSTRAINT_LT:
991 zOp = "<";
992 break;
993 case SQLITE_INDEX_CONSTRAINT_GT:
994 zOp = ">";
995 break;
996 case SQLITE_INDEX_CONSTRAINT_LE:
997 zOp = "<=";
998 break;
999 case SQLITE_INDEX_CONSTRAINT_GE:
1000 zOp = ">=";
1001 break;
1002 case SQLITE_INDEX_CONSTRAINT_MATCH:
1003 zOp = "LIKE";
1004 break;
1005 }
1006 if ( zOp[0] == 'L' )
1007 {
1008 zNew = sqlite3_mprintf( " %s %s LIKE (SELECT '%%'||?||'%%')",
1009 zSep, zCol );
1010 }
1011 else
1012 {
1013 zNew = sqlite3_mprintf( " %s %s %s ?", zSep, zCol, zOp );
1014 }
1015 string_concat( ref zQuery, zNew, 1, ref rc );
1016  
1017 zSep = "AND";
1018 pUsage.argvIndex = ++nArg;
1019 pUsage.omit = true;
1020 }
1021 }
1022  
1023 /* If there is only one term in the ORDER BY clause, and it is
1024 ** on a column that this virtual table has an index for, then consume
1025 ** the ORDER BY clause.
1026 */
1027 if ( pIdxInfo.nOrderBy == 1 && ( pIdxInfo.aOrderBy[0].iColumn < 0 || pVtab.aIndex[pIdxInfo.aOrderBy[0].iColumn] != 0 ) )
1028 {
1029 int iCol = pIdxInfo.aOrderBy[0].iColumn;
1030 string zCol;
1031 string zDir = pIdxInfo.aOrderBy[0].desc ? "DESC" : "ASC";
1032 if ( iCol >= 0 )
1033 {
1034 zCol = pVtab.aCol[iCol];
1035 }
1036 else
1037 {
1038 zCol = "rowid";
1039 }
1040 zNew = sqlite3_mprintf( " ORDER BY %s %s", zCol, zDir );
1041 string_concat( ref zQuery, zNew, 1, ref rc );
1042 pIdxInfo.orderByConsumed = true;
1043 }
1044  
1045 appendToEchoModule( pVtab.interp, "xBestIndex" );
1046 ;
1047 appendToEchoModule( pVtab.interp, zQuery );
1048  
1049 if ( null == zQuery )
1050 {
1051 return rc;
1052 }
1053 pIdxInfo.idxNum = hashString( zQuery );
1054 pIdxInfo.idxStr = zQuery;
1055 pIdxInfo.needToFreeIdxStr = 1;
1056 if ( useCost != 0 )
1057 {
1058 pIdxInfo.estimatedCost = cost;
1059 }
1060 else if ( useIdx != 0 )
1061 {
1062 /* Approximation of log2(nRow). */
1063 for ( ii = 0; ii < ( sizeof( int ) * 8 ); ii++ )
1064 {
1065 if ( ( nRow & ( 1 << ii ) ) != 0 )
1066 {
1067 pIdxInfo.estimatedCost = (double)ii;
1068 }
1069 }
1070 }
1071 else
1072 {
1073 pIdxInfo.estimatedCost = (double)nRow;
1074 }
1075 return rc;
1076 }
1077  
1078 /*
1079 ** The xUpdate method for echo module virtual tables.
1080 **
1081 ** apData[0] apData[1] apData[2..]
1082 **
1083 ** INTEGER DELETE
1084 **
1085 ** INTEGER NULL (nCol args) UPDATE (do not set rowid)
1086 ** INTEGER INTEGER (nCol args) UPDATE (with SET rowid = <arg1>)
1087 **
1088 ** NULL NULL (nCol args) INSERT INTO (automatic rowid value)
1089 ** NULL INTEGER (nCol args) INSERT (incl. rowid value)
1090 **
1091 */
1092 static int echoUpdate(
1093 sqlite3_vtab vtab,
1094 int nData,
1095 sqlite3_value[] apData,
1096 out sqlite_int64 pRowid
1097 )
1098 {
1099 echo_vtab pVtab = (echo_vtab)vtab;
1100 sqlite3 db = pVtab.db;
1101 int rc = SQLITE_OK;
1102  
1103 pRowid = 0;
1104 sqlite3_stmt pStmt = null;
1105 string z = ""; /* SQL statement to execute */
1106 int bindArgZero = 0; /* True to bind apData[0] to sql var no. nData */
1107 int bindArgOne = 0; /* True to bind apData[1] to sql var no. 1 */
1108 int i; /* Counter variable used by for loops */
1109  
1110 Debug.Assert( nData == pVtab.nCol + 2 || nData == 1 );
1111  
1112 /* Ticket #3083 - make sure we always start a transaction prior to
1113 ** making any changes to a virtual table */
1114 Debug.Assert( pVtab.inTransaction != 0 );
1115  
1116 if ( simulateVtabError( pVtab, "xUpdate" ) != 0 )
1117 {
1118 return SQLITE_ERROR;
1119 }
1120  
1121 /* If apData[0] is an integer and nData>1 then do an UPDATE */
1122 if ( nData > 1 && sqlite3_value_type( apData[0] ) == SQLITE_INTEGER )
1123 {
1124 string zSep = " SET";
1125 z = sqlite3_mprintf( "UPDATE %Q", pVtab.zTableName );
1126 //if ( null == z )
1127 //{
1128 // rc = SQLITE_NOMEM;
1129 //}
1130  
1131 bindArgOne = ( apData[1] != null && sqlite3_value_type( apData[1] ) == SQLITE_INTEGER )?1:0;
1132 bindArgZero = 1;
1133  
1134 if ( bindArgOne != 0 )
1135 {
1136 string_concat( ref z, " SET rowid=?1 ", 0, ref rc );
1137 zSep = ",";
1138 }
1139 for ( i = 2; i < nData; i++ )
1140 {
1141 if ( apData[i] == null )
1142 continue;
1143 string_concat( ref z, sqlite3_mprintf(
1144 "%s %Q=?%d", zSep, pVtab.aCol[i - 2], i ), 1, ref rc );
1145 zSep = ",";
1146 }
1147 string_concat( ref z, sqlite3_mprintf( " WHERE rowid=?%d", nData ), 1, ref rc );
1148 }
1149  
1150 /* If apData[0] is an integer and nData==1 then do a DELETE */
1151 else if ( nData == 1 && sqlite3_value_type( apData[0] ) == SQLITE_INTEGER )
1152 {
1153 z = sqlite3_mprintf( "DELETE FROM %Q WHERE rowid = ?1", pVtab.zTableName );
1154 //if ( null == z )
1155 //{
1156 // rc = SQLITE_NOMEM;
1157 //}
1158 bindArgZero = 1;
1159 }
1160  
1161 /* If the first argument is NULL and there are more than two args, INSERT */
1162 else if ( nData > 2 && sqlite3_value_type( apData[0] ) == SQLITE_NULL )
1163 {
1164 int ii;
1165 string zInsert = "";
1166 string zValues = "";
1167  
1168 zInsert = sqlite3_mprintf( "INSERT INTO %Q (", pVtab.zTableName );
1169 //if ( null == zInsert )
1170 //{
1171 // rc = SQLITE_NOMEM;
1172 //}
1173 if ( sqlite3_value_type( apData[1] ) == SQLITE_INTEGER )
1174 {
1175 bindArgOne = 1;
1176 zValues = sqlite3_mprintf( "?" );
1177 string_concat( ref zInsert, "rowid", 0, ref rc );
1178 }
1179  
1180 Debug.Assert( ( pVtab.nCol + 2 ) == nData );
1181 for ( ii = 2; ii < nData; ii++ )
1182 {
1183 string_concat( ref zInsert,
1184 sqlite3_mprintf( "%s%Q", !String.IsNullOrEmpty(zValues) ? ", " : "", pVtab.aCol[ii - 2] ), 1, ref rc );
1185 string_concat( ref zValues,
1186 sqlite3_mprintf( "%s?%d", !String.IsNullOrEmpty( zValues ) ? ", " : "", ii ), 1, ref rc );
1187 }
1188  
1189 string_concat( ref z, zInsert, 1, ref rc );
1190 string_concat( ref z, ") VALUES(", 0, ref rc );
1191 string_concat( ref z, zValues, 1, ref rc );
1192 string_concat( ref z, ")", 0, ref rc );
1193 }
1194  
1195 /* Anything else is an error */
1196 else
1197 {
1198 Debug.Assert( false );
1199 return SQLITE_ERROR;
1200 }
1201  
1202 if ( rc == SQLITE_OK )
1203 {
1204 rc = sqlite3_prepare( db, z, -1, ref pStmt, 0 );
1205 }
1206 Debug.Assert( rc != SQLITE_OK || pStmt != null );
1207 //sqlite3_free(z);
1208 if ( rc == SQLITE_OK )
1209 {
1210 if ( bindArgZero != 0 )
1211 {
1212 sqlite3_bind_value( pStmt, nData, apData[0] );
1213 }
1214 if ( bindArgOne != 0 )
1215 {
1216 sqlite3_bind_value( pStmt, 1, apData[1] );
1217 }
1218 for ( i = 2; i < nData && rc == SQLITE_OK; i++ )
1219 {
1220 if ( apData[i] != null )
1221 rc = sqlite3_bind_value( pStmt, i, apData[i] );
1222 }
1223 if ( rc == SQLITE_OK )
1224 {
1225 sqlite3_step( pStmt );
1226 rc = sqlite3_finalize( pStmt );
1227 }
1228 else
1229 {
1230 sqlite3_finalize( pStmt );
1231 }
1232 }
1233  
1234 if (/* pRowid != 0 && */ rc == SQLITE_OK )
1235 {
1236 pRowid = sqlite3_last_insert_rowid( db );
1237 }
1238 if ( rc != SQLITE_OK )
1239 {
1240 vtab.zErrMsg = sqlite3_mprintf( "echo-vtab-error: %s", sqlite3_errmsg( db ) );
1241 }
1242  
1243 return rc;
1244 }
1245  
1246 /*
1247 ** xBegin, xSync, xCommit and xRollback callbacks for echo module
1248 ** virtual tables. Do nothing other than add the name of the callback
1249 ** to the $::echo_module Tcl variable.
1250 */
1251 static int echoTransactionCall( sqlite3_vtab vtab, string zCall )
1252 {
1253 string z;
1254 echo_vtab pVtab = (echo_vtab)vtab;
1255 z = sqlite3_mprintf( "echo(%s)", pVtab.zTableName );
1256 //if( z==null ) return SQLITE_NOMEM;
1257 appendToEchoModule( pVtab.interp, zCall );
1258 appendToEchoModule( pVtab.interp, z );
1259 //sqlite3_free(z);
1260 return SQLITE_OK;
1261 }
1262 static int echoBegin( sqlite3_vtab vtab )
1263 {
1264 int rc;
1265 echo_vtab pVtab = (echo_vtab)vtab;
1266 Tcl_Interp interp = pVtab.interp;
1267 string zVal;
1268  
1269 /* Ticket #3083 - do not start a transaction if we are already in
1270 ** a transaction */
1271 Debug.Assert( 0 == pVtab.inTransaction );
1272  
1273 if ( simulateVtabError( pVtab, "xBegin" ) != 0 )
1274 {
1275 return SQLITE_ERROR;
1276 }
1277  
1278 rc = echoTransactionCall( vtab, "xBegin" );
1279  
1280 if ( rc == SQLITE_OK )
1281 {
1282 /* Check if the $::echo_module_begin_fail variable is defined. If it is,
1283 ** and it is set to the name of the real table underlying this virtual
1284 ** echo module table, then cause this xSync operation to fail.
1285 */
1286 zVal = TCL.Tcl_GetVar( interp, "echo_module_begin_fail", TCL.VarFlag.GLOBAL_ONLY ).ToString();
1287 if ( zVal != null && 0 == zVal.CompareTo( pVtab.zTableName ) )
1288 {
1289 rc = SQLITE_ERROR;
1290 }
1291 }
1292 if ( rc == SQLITE_OK )
1293 {
1294 pVtab.inTransaction = 1;
1295 }
1296 return rc;
1297 }
1298 static int echoSync( sqlite3_vtab vtab )
1299 {
1300 int rc;
1301 echo_vtab pVtab = (echo_vtab)vtab;
1302 Tcl_Interp interp = pVtab.interp;
1303 string zVal;
1304  
1305 /* Ticket #3083 - Only call xSync if we have previously started a
1306 ** transaction */
1307 Debug.Assert( pVtab.inTransaction != 0 );
1308  
1309 if ( simulateVtabError( pVtab, "xSync" ) != 0 )
1310 {
1311 return SQLITE_ERROR;
1312 }
1313  
1314 rc = echoTransactionCall( vtab, "xSync" );
1315  
1316 if ( rc == SQLITE_OK )
1317 {
1318 /* Check if the $::echo_module_sync_fail variable is defined. If it is,
1319 ** and it is set to the name of the real table underlying this virtual
1320 ** echo module table, then cause this xSync operation to fail.
1321 */
1322 zVal = TCL.Tcl_GetVar( interp, "echo_module_sync_fail", TCL.VarFlag.GLOBAL_ONLY ).ToString();
1323 if ( zVal != "" && 0 == zVal.CompareTo( pVtab.zTableName ) )
1324 {
1325 rc = -1;
1326 }
1327 }
1328 return rc;
1329 }
1330 static int echoCommit( sqlite3_vtab vtab )
1331 {
1332 echo_vtab pVtab = (echo_vtab)vtab;
1333 int rc;
1334  
1335 /* Ticket #3083 - Only call xCommit if we have previously started
1336 ** a transaction */
1337 Debug.Assert( pVtab.inTransaction != 0 );
1338  
1339 if ( simulateVtabError( pVtab, "xCommit" ) != 0 )
1340 {
1341 return SQLITE_ERROR;
1342 }
1343  
1344 sqlite3BeginBenignMalloc();
1345 rc = echoTransactionCall( vtab, "xCommit" );
1346 sqlite3EndBenignMalloc();
1347 pVtab.inTransaction = 0;
1348 return rc;
1349 }
1350 static int echoRollback( sqlite3_vtab vtab )
1351 {
1352 int rc;
1353 echo_vtab pVtab = (echo_vtab)vtab;
1354  
1355 /* Ticket #3083 - Only call xRollback if we have previously started
1356 ** a transaction */
1357 Debug.Assert( pVtab.inTransaction != 0 );
1358  
1359 rc = echoTransactionCall( vtab, "xRollback" );
1360 pVtab.inTransaction = 0;
1361 return rc;
1362 }
1363  
1364 /*
1365 ** Implementation of "GLOB" function on the echo module. Pass
1366 ** all arguments to the ::echo_glob_overload procedure of TCL
1367 ** and return the result of that procedure as a string.
1368 */
1369 static void overloadedGlobFunction(
1370 sqlite3_context pContext,
1371 int nArg,
1372 sqlite3_value[] apArg
1373 )
1374 {
1375 Tcl_Interp interp = (Interp)sqlite3_user_data( pContext );
1376 TclObject str = null;
1377 int i;
1378 int rc;
1379 TCL.Tcl_DStringInit( out str );
1380 TCL.Tcl_DStringAppendElement( str, "::echo_glob_overload" );
1381 for ( i = 0; i < nArg; i++ )
1382 {
1383 TCL.Tcl_DStringAppendElement( str, " " + sqlite3_value_text( apArg[i] ) );
1384 }
1385  
1386 rc = TCL.Tcl_EvalObjEx( interp, str, 0 );// rc = TCL.Tcl_Eval( interp, TCL.Tcl_DStringValue( ref str ) );
1387 TCL.Tcl_DStringFree( ref str );
1388 if ( rc != 0 )
1389 {
1390 sqlite3_result_error( pContext, TCL.Tcl_GetStringResult( interp ), -1 );
1391 }
1392 else
1393 {
1394 sqlite3_result_text( pContext, TCL.Tcl_GetStringResult( interp ),
1395 -1, SQLITE_TRANSIENT );
1396 }
1397 TCL.Tcl_ResetResult( interp );
1398 }
1399  
1400 /*
1401 ** This is the xFindFunction implementation for the echo module.
1402 ** SQLite calls this routine when the first argument of a function
1403 ** is a column of an echo virtual table. This routine can optionally
1404 ** override the implementation of that function. It will choose to
1405 ** do so if the function is named "glob", and a TCL command named
1406 ** ::echo_glob_overload exists.
1407 */
1408 static int echoFindFunction(
1409 sqlite3_vtab vtab,
1410 int nArg,
1411 string zFuncName,
1412 ref dxFunc pxFunc, //void (**pxFunc)(sqlite3_context*,int,sqlite3_value),
1413 ref object ppArg
1414 )
1415 {
1416 echo_vtab pVtab = (echo_vtab)vtab;
1417 Tcl_Interp interp = pVtab.interp;
1418 Tcl_CmdInfo info;
1419 if ( !zFuncName.StartsWith( "glob", StringComparison.InvariantCultureIgnoreCase ) )
1420 {
1421 return 0;
1422 }
1423 TCL.Tcl_GetCommandInfo( interp, "::echo_glob_overload", out info );
1424 if ( info ==null)
1425 {
1426 return 0;
1427 }
1428 pxFunc = overloadedGlobFunction;
1429 ppArg = interp;
1430 return 1;
1431 }
1432  
1433 static int echoRename( sqlite3_vtab vtab, string zNewName )
1434 {
1435 int rc = SQLITE_OK;
1436 echo_vtab p = (echo_vtab)vtab;
1437  
1438 if ( simulateVtabError( p, "xRename" ) != 0 )
1439 {
1440 return SQLITE_ERROR;
1441 }
1442  
1443 if ( p.isPattern != 0 )
1444 {
1445 int nThis = p.zThis.Length;
1446 string zSql = sqlite3_mprintf( "ALTER TABLE %s RENAME TO %s%s",
1447 p.zTableName, zNewName, p.zTableName.Substring(nThis)
1448 );
1449 rc = sqlite3_exec( p.db, zSql, 0, 0, 0 );
1450 //sqlite3_free( zSql );
1451 }
1452  
1453 return rc;
1454 }
1455  
1456 /*
1457 ** A virtual table module that merely "echos" the contents of another
1458 ** table (like an SQL VIEW).
1459 */
1460 static sqlite3_module echoModule = new sqlite3_module(
1461 0, /* iVersion */
1462 echoCreate,
1463 echoConnect,
1464 echoBestIndex,
1465 echoDisconnect,
1466 echoDestroy,
1467 echoOpen, /* xOpen - open a cursor */
1468 echoClose, /* xClose - close a cursor */
1469 echoFilter, /* xFilter - configure scan constraints */
1470 echoNext, /* xNext - advance a cursor */
1471 echoEof, /* xEof */
1472 echoColumn, /* xColumn - read data */
1473 echoRowid, /* xRowid - read data */
1474 echoUpdate, /* xUpdate - write data */
1475 echoBegin, /* xBegin - begin transaction */
1476 echoSync, /* xSync - sync transaction */
1477 echoCommit, /* xCommit - commit transaction */
1478 echoRollback, /* xRollback - rollback transaction */
1479 echoFindFunction, /* xFindFunction - function overloading */
1480 echoRename /* xRename - rename the table */
1481 );
1482  
1483 /*
1484 ** Decode a pointer to an sqlite3 object.
1485 */
1486 //extern int getDbPointer(Tcl_Interp interp, string zA, sqlite3 ppDb);
1487  
1488 static int moduleDestroy( ref object p )
1489 {
1490 p = null;// sqlite3_free( p );
1491 return SQLITE_OK;
1492 }
1493  
1494 /*
1495 ** Register the echo virtual table module.
1496 */
1497 static int register_echo_module(
1498 ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
1499 Tcl_Interp interp, /* The TCL interpreter that invoked this command */
1500 int objc, /* Number of arguments */
1501 Tcl_Obj[] objv /* Command arguments */
1502 )
1503 {
1504 sqlite3 db = null;
1505 ;
1506 ;
1507 EchoModule pMod;
1508 if ( objc != 2 )
1509 {
1510 TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB" );
1511 return TCL.TCL_ERROR;
1512 }
1513 if ( getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ) != 0 )
1514 return TCL.TCL_ERROR;
1515 pMod = new EchoModule();//sqlite3_malloc(sizeof(EchoModule));
1516 pMod.interp = interp;
1517 sqlite3_create_module_v2( db, "echo", echoModule, pMod, moduleDestroy );
1518 return TCL.TCL_OK;
1519 }
1520  
1521 /*
1522 ** Tcl interface to sqlite3_declare_vtab, invoked as follows from Tcl:
1523 **
1524 ** sqlite3_declare_vtab DB SQL
1525 */
1526 static int declare_vtab(
1527 ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
1528 Tcl_Interp interp, /* The TCL interpreter that invoked this command */
1529 int objc, /* Number of arguments */
1530 Tcl_Obj[] objv /* Command arguments */
1531 )
1532 {
1533 sqlite3 db = null;
1534 int rc;
1535 if ( objc != 3 )
1536 {
1537 TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB SQL" );
1538 return TCL.TCL_ERROR;
1539 }
1540 if ( getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ) != 0 )
1541 return TCL.TCL_ERROR;
1542 rc = sqlite3_declare_vtab( db, TCL.Tcl_GetString( objv[2] ) );
1543 if ( rc != SQLITE_OK )
1544 {
1545 TCL.Tcl_SetResult( interp, sqlite3_errmsg( db ), TCL.TCL_VOLATILE );
1546 return TCL.TCL_ERROR;
1547 }
1548 return TCL.TCL_OK;
1549 }
1550  
1551 #endif //* ifndef SQLITE_OMIT_VIRTUALTABLE */
1552  
1553 /*
1554 ** Register commands with the TCL interpreter.
1555 */
1556 static public int Sqlitetest8_Init( Tcl_Interp interp )
1557 {
1558 #if !SQLITE_OMIT_VIRTUALTABLE
1559 //static struct {
1560 // string zName;
1561 // Tcl_ObjCmdProc *xProc;
1562 // void *clientData;
1563 //}
1564 _aObjCmd[] aObjCmd = new _aObjCmd[]{
1565 new _aObjCmd( "register_echo_module", register_echo_module, 0 ),
1566 new _aObjCmd( "sqlite3_declare_vtab", declare_vtab, 0 ),
1567 };
1568 int i;
1569 for ( i = 0; i < aObjCmd.Length; i++ )//sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++)
1570 {
1571 TCL.Tcl_CreateObjCommand( interp, aObjCmd[i].zName,
1572 aObjCmd[i].xProc, aObjCmd[i].clientData, null );
1573 }
1574 #endif
1575 return TCL.TCL_OK;
1576 }
1577 }
1578 #endif
1579 }