wasCSharpSQLite – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System.Diagnostics;
2  
3 using u8 = System.Byte;
4 using u32 = System.UInt32;
5  
6 namespace Community.CsharpSqlite
7 {
8 #if TCLSH
9 using tcl.lang;
10 using DbPage = Sqlite3.PgHdr;
11 using sqlite_int64 = System.Int64;
12 using sqlite3_stmt = Sqlite3.Vdbe;
13 using sqlite3_value = Sqlite3.Mem;
14 using Tcl_CmdInfo = tcl.lang.WrappedCommand;
15 using Tcl_Interp = tcl.lang.Interp;
16 using Tcl_Obj = tcl.lang.TclObject;
17 using ClientData = System.Object;
18 using System;
19 #endif
20  
21 public partial class Sqlite3
22 {
23 /*
24 ** 2010 July 12
25 **
26 ** The author disclaims copyright to this source code. In place of
27 ** a legal notice, here is a blessing:
28 **
29 ** May you do good and not evil.
30 ** May you find forgiveness for yourself and forgive others.
31 ** May you share freely, never taking more than you give.
32 **
33 ******************************************************************************
34 **
35 ** This file contains an implementation of the "dbstat" virtual table.
36 **
37 ** The dbstat virtual table is used to extract low-level formatting
38 ** information from an SQLite database in order to implement the
39 ** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
40 ** for an example implementation.
41 *************************************************************************
42 ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
43 ** C#-SQLite is an independent reimplementation of the SQLite software library
44 **
45 ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
46 **
47 **************************************************************************/
48  
49 //#include "sqliteInt.h"
50  
51 #if !SQLITE_OMIT_VIRTUALTABLE
52  
53 /*
54 ** Page paths:
55 **
56 ** The value of the 'path' column describes the path taken from the
57 ** root-node of the b-tree structure to each page. The value of the
58 ** root-node path is '/'.
59 **
60 ** The value of the path for the left-most child page of the root of
61 ** a b-tree is '/000/'. (Btrees store content ordered from left to right
62 ** so the pages to the left have smaller keys than the pages to the right.)
63 ** The next to left-most child of the root page is
64 ** '/001', and so on, each sibling page identified by a 3-digit hex
65 ** value. The children of the 451st left-most sibling have paths such
66 ** as '/1c2/000/, '/1c2/001/' etc.
67 **
68 ** Overflow pages are specified by appending a '+' character and a
69 ** six-digit hexadecimal value to the path to the cell they are linked
70 ** from. For example, the three overflow pages in a chain linked from
71 ** the left-most cell of the 450th child of the root page are identified
72 ** by the paths:
73 **
74 ** '/1c2/000+000000' // First page in overflow chain
75 ** '/1c2/000+000001' // Second page in overflow chain
76 ** '/1c2/000+000002' // Third page in overflow chain
77 **
78 ** If the paths are sorted using the BINARY collation sequence, then
79 ** the overflow pages associated with a cell will appear earlier in the
80 ** sort-order than its child page:
81 **
82 ** '/1c2/000/' // Left-most child of 451st child of root
83 */
84 const string VTAB_SCHEMA =
85 "CREATE TABLE xx( " +
86 " name STRING, /* Name of table or index */" +
87 " path INTEGER, /* Path to page from root */" +
88 " pageno INTEGER, /* Page number */" +
89 " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" +
90 " ncell INTEGER, /* Cells on page (0 for overflow) */" +
91 " payload INTEGER, /* Bytes of payload on this page */" +
92 " unused INTEGER, /* Bytes of unused space on this page */" +
93 " mx_payload INTEGER /* Largest payload size of all cells */" +
94 ");";
95  
96 #if FALSE
97 //#define VTAB_SCHEMA2 \
98 "CREATE TABLE yy( " \
99 " pageno INTEGER, /* B-tree page number */" \
100 " cellno INTEGER, /* Cell number within page */" \
101 " local INTEGER, /* Bytes of content stored locally */" \
102 " payload INTEGER, /* Total cell payload size */" \
103 " novfl INTEGER /* Number of overflow pages */" \
104 ");"
105 #endif
106  
107  
108 //typedef struct StatTable StatTable;
109 //typedef struct StatCursor StatCursor;
110 //typedef struct StatPage StatPage;
111 //typedef struct StatCell StatCell;
112  
113 class StatCell
114 {
115 public int nLocal; /* Bytes of local payload */
116 public u32 iChildPg; /* Child node (or 0 if this is a leaf) */
117 public int nOvfl; /* Entries in aOvfl[] */
118 public u32[] aOvfl; /* Array of overflow page numbers */
119 public int nLastOvfl; /* Bytes of payload on final overflow page */
120 public int iOvfl; /* Iterates through aOvfl[] */
121 };
122  
123 class StatPage
124 {
125 public u32 iPgno;
126 public DbPage pPg;
127 public int iCell;
128  
129 public string zPath; /* Path to this page */
130  
131 /* Variables populated by statDecodePage(): */
132 public u8 flags; /* Copy of flags byte */
133 public int nCell; /* Number of cells on page */
134 public int nUnused; /* Number of unused bytes on page */
135 public StatCell[] aCell; /* Array of parsed cells */
136 public u32 iRightChildPg; /* Right-child page number (or 0) */
137 public int nMxPayload; /* Largest payload of any cell on this page */
138 };
139  
140 class StatCursor : sqlite3_vtab_cursor
141 {
142 //sqlite3_vtab_cursor base;
143 public sqlite3_stmt pStmt; /* Iterates through set of root pages */
144 public int isEof; /* After pStmt has returned SQLITE_DONE */
145  
146 public StatPage[] aPage = new StatPage[32];
147 public int iPage; /* Current entry in aPage[] */
148  
149 /* Values to return. */
150 public string zName; /* Value of 'name' column */
151 public string zPath; /* Value of 'path' column */
152 public u32 iPageno; /* Value of 'pageno' column */
153 public string zPagetype; /* Value of 'pagetype' column */
154 public int nCell; /* Value of 'ncell' column */
155 public int nPayload; /* Value of 'payload' column */
156 public int nUnused; /* Value of 'unused' column */
157 public int nMxPayload; /* Value of 'mx_payload' column */
158 };
159  
160 class StatTable : sqlite3_vtab
161 {
162 //sqlite3_vtab base;
163 public sqlite3 db;
164 };
165  
166 //#if !get2byte
167 //# define get2byte(x) ((x)[0]<<8 | (x)[1])
168 //#endif
169  
170 /*
171 ** Connect to or create a statvfs virtual table.
172 */
173 static int statConnect(
174 sqlite3 db,
175 object pAux,
176 int argc,
177 string[] argv,
178 out sqlite3_vtab ppVtab,
179 out string pzErr
180 )
181 {
182 StatTable pTab;
183  
184 pTab = new StatTable();//(StatTable )sqlite3_malloc(sizeof(StatTable));
185 //memset(pTab, 0, sizeof(StatTable));
186 pTab.db = db;
187  
188 sqlite3_declare_vtab( db, VTAB_SCHEMA );
189 ppVtab = pTab;
190 pzErr = "";
191 return SQLITE_OK;
192 }
193  
194 /*
195 ** Disconnect from or destroy a statvfs virtual table.
196 */
197 static int statDisconnect( ref object pVtab )
198 {
199 pVtab = null;//sqlite3_free( pVtab );
200 return SQLITE_OK;
201 }
202  
203 /*
204 ** There is no "best-index". This virtual table always does a linear
205 ** scan of the binary VFS log file.
206 */
207 static int statBestIndex( sqlite3_vtab tab, ref sqlite3_index_info pIdxInfo )
208 {
209  
210 /* Records are always returned in ascending order of (name, path).
211 ** If this will satisfy the client, set the orderByConsumed flag so that
212 ** SQLite does not do an external sort.
213 */
214 if ( ( pIdxInfo.nOrderBy == 1
215 && pIdxInfo.aOrderBy[0].iColumn == 0
216 && pIdxInfo.aOrderBy[0].desc == false
217 ) ||
218 ( pIdxInfo.nOrderBy == 2
219 && pIdxInfo.aOrderBy[0].iColumn == 0
220 && pIdxInfo.aOrderBy[0].desc == false
221 && pIdxInfo.aOrderBy[1].iColumn == 1
222 && pIdxInfo.aOrderBy[1].desc == false
223 )
224 )
225 {
226 pIdxInfo.orderByConsumed = true;
227 }
228  
229 pIdxInfo.estimatedCost = 10.0;
230 return SQLITE_OK;
231 }
232  
233 /*
234 ** Open a new statvfs cursor.
235 */
236 static int statOpen( sqlite3_vtab pVTab, out sqlite3_vtab_cursor ppCursor )
237 {
238 StatTable pTab = (StatTable)pVTab;
239 StatCursor pCsr;
240 int rc;
241  
242 pCsr = new StatCursor();//(StatCursor )sqlite3_malloc(sizeof(StatCursor));
243 //memset(pCsr, 0, sizeof(StatCursor));
244 pCsr.pVtab = pVTab;
245  
246 rc = sqlite3_prepare_v2( pTab.db,
247 "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" +
248 " UNION ALL " +
249 "SELECT name, rootpage, type FROM sqlite_master WHERE rootpage!=0" +
250 " ORDER BY name", -1,
251 ref pCsr.pStmt, 0
252 );
253 if ( rc != SQLITE_OK )
254 {
255 pCsr = null;//sqlite3_free( pCsr );
256 ppCursor = null;
257 return rc;
258 }
259  
260 ppCursor = (sqlite3_vtab_cursor)pCsr;
261 return SQLITE_OK;
262 }
263  
264 static void statClearPage( ref StatPage p )
265 {
266 int i;
267 if ( p != null && p.aCell != null )
268 {
269 for ( i = 0; i < p.nCell; i++ )
270 {
271 p.aCell[i].aOvfl = null;//sqlite3_free( p.aCell[i].aOvfl );
272 }
273 sqlite3PagerUnref( p.pPg );
274 // sqlite3_free( p.aCell );
275 // sqlite3_free( p.zPath );
276 }
277 p = new StatPage();//memset( p, 0, sizeof( StatPage ) );
278 }
279  
280 static void statResetCsr( StatCursor pCsr )
281 {
282 int i;
283 sqlite3_reset( pCsr.pStmt );
284 for ( i = 0; i < ArraySize( pCsr.aPage ); i++ )
285 {
286 statClearPage( ref pCsr.aPage[i] );
287 }
288 pCsr.iPage = 0;
289 //sqlite3_free(pCsr.zPath);
290 pCsr.zPath = null;
291 }
292  
293 /*
294 ** Close a statvfs cursor.
295 */
296 static int statClose( ref sqlite3_vtab_cursor pCursor )
297 {
298 StatCursor pCsr = (StatCursor)pCursor;
299 statResetCsr( pCsr );
300 sqlite3_finalize( pCsr.pStmt );
301 pCsr = null;//sqlite3_free( pCsr );
302 return SQLITE_OK;
303 }
304  
305 static void getLocalPayload(
306 int nUsable, /* Usable bytes per page */
307 u8 flags, /* Page flags */
308 int nTotal, /* Total record (payload) size */
309 out int pnLocal /* OUT: Bytes stored locally */
310 )
311 {
312 int nLocal;
313 int nMinLocal;
314 int nMaxLocal;
315  
316 if ( flags == 0x0D )
317 { /* Table leaf node */
318 nMinLocal = ( nUsable - 12 ) * 32 / 255 - 23;
319 nMaxLocal = nUsable - 35;
320 }
321 else
322 { /* Index interior and leaf nodes */
323 nMinLocal = ( nUsable - 12 ) * 32 / 255 - 23;
324 nMaxLocal = ( nUsable - 12 ) * 64 / 255 - 23;
325 }
326  
327 nLocal = nMinLocal + ( nTotal - nMinLocal ) % ( nUsable - 4 );
328 if ( nLocal > nMaxLocal )
329 nLocal = nMinLocal;
330 pnLocal = nLocal;
331 }
332  
333 static int statDecodePage( Btree pBt, StatPage p )
334 {
335 int nUnused;
336 int iOff;
337 int nHdr;
338 int isLeaf;
339  
340 u8[] aData = sqlite3PagerGetData( p.pPg );
341 u8[] aHdr = new byte[p.iPgno == 1 ? aData.Length - 100 : aData.Length];
342 Buffer.BlockCopy( aData, p.iPgno == 1 ? 100 : 0, aHdr, 0, aHdr.Length );
343  
344 p.flags = aHdr[0];
345 p.nCell = get2byte( aHdr, 3 );
346 p.nMxPayload = 0;
347  
348 isLeaf = ( p.flags == 0x0A || p.flags == 0x0D ) ? 1 : 0;
349 nHdr = 12 - isLeaf * 4 + ( ( p.iPgno == 1 ) ? 1 : 0 ) * 100;
350  
351 nUnused = get2byte( aHdr, 5 ) - nHdr - 2 * p.nCell;
352 nUnused += (int)aHdr[7];
353 iOff = get2byte( aHdr, 1 );
354 while ( iOff != 0 )
355 {
356 nUnused += get2byte( aData, iOff + 2 );
357 iOff = get2byte( aData, iOff );
358 }
359 p.nUnused = nUnused;
360 p.iRightChildPg = isLeaf != 0 ? 0 : sqlite3Get4byte( aHdr, 8 );
361  
362 if ( p.nCell != 0 )
363 {
364 int i; /* Used to iterate through cells */
365 int nUsable = sqlite3BtreeGetPageSize( pBt ) - sqlite3BtreeGetReserve( pBt );
366  
367 p.aCell = new StatCell[p.nCell + 1];// sqlite3_malloc( ( p.nCell + 1 ) * sizeof( StatCell ) );
368 //memset(p.aCell, 0, (p.nCell+1) * sizeof(StatCell));
369  
370 for ( i = 0; i < p.nCell; i++ )
371 {
372 p.aCell[i] = new StatCell();
373 StatCell pCell = p.aCell[i];
374  
375 iOff = get2byte( aData, nHdr + i * 2 );
376 if ( 0 == isLeaf )
377 {
378 pCell.iChildPg = sqlite3Get4byte( aData, iOff );
379 iOff += 4;
380 }
381 if ( p.flags == 0x05 )
382 {
383 /* A table interior node. nPayload==0. */
384 }
385 else
386 {
387 u32 nPayload; /* Bytes of payload total (local+overflow) */
388 int nLocal; /* Bytes of payload stored locally */
389 iOff += getVarint32( aData, iOff, out nPayload );
390 if ( p.flags == 0x0D )
391 {
392 ulong dummy;
393 iOff += sqlite3GetVarint( aData, iOff, out dummy );
394 }
395 if ( nPayload > p.nMxPayload )
396 p.nMxPayload = (int)nPayload;
397 getLocalPayload( nUsable, p.flags, (int)nPayload, out nLocal );
398 pCell.nLocal = nLocal;
399 Debug.Assert( nPayload >= nLocal );
400 Debug.Assert( nLocal <= ( nUsable - 35 ) );
401 if ( nPayload > nLocal )
402 {
403 int j;
404 int nOvfl = (int)( ( nPayload - nLocal ) + nUsable - 4 - 1 ) / ( nUsable - 4 );
405 pCell.nLastOvfl = (int)( nPayload - nLocal ) - ( nOvfl - 1 ) * ( nUsable - 4 );
406 pCell.nOvfl = nOvfl;
407 pCell.aOvfl = new uint[nOvfl];//sqlite3_malloc(sizeof(u32)*nOvfl);
408 pCell.aOvfl[0] = sqlite3Get4byte( aData, iOff + nLocal );
409 for ( j = 1; j < nOvfl; j++ )
410 {
411 int rc;
412 u32 iPrev = pCell.aOvfl[j - 1];
413 DbPage pPg = null;
414 rc = sqlite3PagerGet( sqlite3BtreePager( pBt ), iPrev, ref pPg );
415 if ( rc != SQLITE_OK )
416 {
417 Debug.Assert( pPg == null );
418 return rc;
419 }
420 pCell.aOvfl[j] = sqlite3Get4byte( sqlite3PagerGetData( pPg ) );
421 sqlite3PagerUnref( pPg );
422 }
423 }
424 }
425 }
426 }
427  
428 return SQLITE_OK;
429 }
430  
431 /*
432 ** Move a statvfs cursor to the next entry in the file.
433 */
434 static int statNext( sqlite3_vtab_cursor pCursor )
435 {
436 int rc = 0;
437 int nPayload;
438 StatCursor pCsr = (StatCursor)pCursor;
439 StatTable pTab = (StatTable)pCursor.pVtab;
440 Btree pBt = pTab.db.aDb[0].pBt;
441 Pager pPager = sqlite3BtreePager( pBt );
442  
443 //sqlite3_free(pCsr.zPath);
444 pCsr.zPath = null;
445  
446 if ( pCsr.aPage[0].pPg == null )
447 {
448 rc = sqlite3_step( pCsr.pStmt );
449 if ( rc == SQLITE_ROW )
450 {
451 u32 nPage;
452 u32 iRoot = (u32)sqlite3_column_int64( pCsr.pStmt, 1 );
453 sqlite3PagerPagecount( pPager, out nPage );
454 if ( nPage == 0 )
455 {
456 pCsr.isEof = 1;
457 return sqlite3_reset( pCsr.pStmt );
458 }
459 rc = sqlite3PagerGet( pPager, iRoot, ref pCsr.aPage[0].pPg );
460 pCsr.aPage[0].iPgno = iRoot;
461 pCsr.aPage[0].iCell = 0;
462 pCsr.aPage[0].zPath = sqlite3_mprintf( "/" );
463 pCsr.iPage = 0;
464 }
465 else
466 {
467 pCsr.isEof = 1;
468 return sqlite3_reset( pCsr.pStmt );
469 }
470 }
471 else
472 {
473  
474 /* Page p itself has already been visited. */
475 StatPage p = pCsr.aPage[pCsr.iPage];
476 StatPage p1 = pCsr.aPage[pCsr.iPage + 1];
477  
478 while ( p.iCell < p.nCell )
479 {
480 StatCell pCell = p.aCell[p.iCell];
481 if ( pCell.iOvfl < pCell.nOvfl )
482 {
483 int nUsable = sqlite3BtreeGetPageSize( pBt ) - sqlite3BtreeGetReserve( pBt );
484 pCsr.zName = sqlite3_column_text( pCsr.pStmt, 0 );
485 pCsr.iPageno = pCell.aOvfl[pCell.iOvfl];
486 pCsr.zPagetype = "overflow";
487 pCsr.nCell = 0;
488 pCsr.nMxPayload = 0;
489 pCsr.zPath = sqlite3_mprintf(
490 "%s%.3x+%.6x", p.zPath, p.iCell, pCell.iOvfl
491 );
492 if ( pCell.iOvfl < pCell.nOvfl - 1 )
493 {
494 pCsr.nUnused = 0;
495 pCsr.nPayload = nUsable - 4;
496 }
497 else
498 {
499 pCsr.nPayload = pCell.nLastOvfl;
500 pCsr.nUnused = nUsable - 4 - pCsr.nPayload;
501 }
502 pCell.iOvfl++;
503 return SQLITE_OK;
504 }
505 if ( p.iRightChildPg != 0 )
506 break;
507 p.iCell++;
508 }
509  
510 while ( 0 == p.iRightChildPg || p.iCell > p.nCell )
511 {
512 statClearPage( ref p );
513 pCsr.aPage[pCsr.iPage] = p;
514 if ( pCsr.iPage == 0 )
515 return statNext( pCursor );
516 pCsr.iPage--;
517 p = pCsr.aPage[pCsr.iPage];
518 if ( pCsr.aPage[pCsr.iPage + 1] == null )
519 pCsr.aPage[pCsr.iPage + 1] = new StatPage();
520 p1 = pCsr.aPage[pCsr.iPage + 1];
521 }
522 pCsr.iPage++;
523 Debug.Assert( p == pCsr.aPage[pCsr.iPage - 1] );
524  
525 if ( p.iCell == p.nCell )
526 {
527 p1.iPgno = p.iRightChildPg;
528 }
529 else
530 {
531 p1.iPgno = p.aCell[p.iCell].iChildPg;
532 }
533 rc = sqlite3PagerGet( pPager, p1.iPgno, ref p1.pPg );
534 p1.iCell = 0;
535 p1.zPath = sqlite3_mprintf( "%s%.3x/", p.zPath, p.iCell );
536 p.iCell++;
537 }
538  
539  
540 /* Populate the StatCursor fields with the values to be returned
541 ** by the xColumn() and xRowid() methods.
542 */
543 if ( rc == SQLITE_OK )
544 {
545 int i;
546 StatPage p = pCsr.aPage[pCsr.iPage];
547 pCsr.zName = sqlite3_column_text( pCsr.pStmt, 0 );
548 pCsr.iPageno = p.iPgno;
549  
550 statDecodePage( pBt, p );
551  
552 switch ( p.flags )
553 {
554 case 0x05: /* table internal */
555 case 0x02: /* index internal */
556 pCsr.zPagetype = "internal";
557 break;
558 case 0x0D: /* table leaf */
559 case 0x0A: /* index leaf */
560 pCsr.zPagetype = "leaf";
561 break;
562 default:
563 pCsr.zPagetype = "corrupted";
564 break;
565 }
566 pCsr.nCell = p.nCell;
567 pCsr.nUnused = p.nUnused;
568 pCsr.nMxPayload = p.nMxPayload;
569 pCsr.zPath = sqlite3_mprintf( "%s", p.zPath );
570 nPayload = 0;
571 for ( i = 0; i < p.nCell; i++ )
572 {
573 nPayload += p.aCell[i].nLocal;
574 }
575 pCsr.nPayload = nPayload;
576 }
577  
578 return rc;
579 }
580  
581 static int statEof( sqlite3_vtab_cursor pCursor )
582 {
583 StatCursor pCsr = (StatCursor)pCursor;
584 return pCsr.isEof;
585 }
586  
587 static int statFilter(
588 sqlite3_vtab_cursor pCursor,
589 int idxNum, string idxStr,
590 int argc, sqlite3_value[] argv
591 )
592 {
593 StatCursor pCsr = (StatCursor)pCursor;
594  
595 statResetCsr( pCsr );
596 return statNext( pCursor );
597 }
598  
599 static int statColumn(
600 sqlite3_vtab_cursor pCursor,
601 sqlite3_context ctx,
602 int i
603 )
604 {
605 StatCursor pCsr = (StatCursor)pCursor;
606 switch ( i )
607 {
608 case 0: /* name */
609 sqlite3_result_text( ctx, pCsr.zName, -1, SQLITE_STATIC );
610 break;
611 case 1: /* path */
612 sqlite3_result_text( ctx, pCsr.zPath, -1, SQLITE_TRANSIENT );
613 break;
614 case 2: /* pageno */
615 sqlite3_result_int64( ctx, pCsr.iPageno );
616 break;
617 case 3: /* pagetype */
618 sqlite3_result_text( ctx, pCsr.zPagetype, -1, SQLITE_STATIC );
619 break;
620 case 4: /* ncell */
621 sqlite3_result_int( ctx, pCsr.nCell );
622 break;
623 case 5: /* payload */
624 sqlite3_result_int( ctx, pCsr.nPayload );
625 break;
626 case 6: /* unused */
627 sqlite3_result_int( ctx, pCsr.nUnused );
628 break;
629 case 7: /* mx_payload */
630 sqlite3_result_int( ctx, pCsr.nMxPayload );
631 break;
632 }
633 return SQLITE_OK;
634 }
635  
636 static int statRowid( sqlite3_vtab_cursor pCursor, out sqlite_int64 pRowid )
637 {
638 StatCursor pCsr = (StatCursor)pCursor;
639 pRowid = pCsr.iPageno;
640 return SQLITE_OK;
641 }
642  
643 static sqlite3_module dbstat_module = new sqlite3_module(
644 0, /* iVersion */
645 statConnect, /* xCreate */
646 statConnect, /* xConnect */
647 statBestIndex, /* xBestIndex */
648 statDisconnect, /* xDisconnect */
649 statDisconnect, /* xDestroy */
650 statOpen, /* xOpen - open a cursor */
651 statClose, /* xClose - close a cursor */
652 statFilter, /* xFilter - configure scan constraints */
653 statNext, /* xNext - advance a cursor */
654 statEof, /* xEof - check for end of scan */
655 statColumn, /* xColumn - read data */
656 statRowid, /* xRowid - read data */
657 null, /* xUpdate */
658 null, /* xBegin */
659 null, /* xSync */
660 null, /* xCommit */
661 null, /* xRollback */
662 null, /* xFindMethod */
663 null /* xRename */
664 );
665  
666 static int sqlite3_dbstat_register( sqlite3 db )
667 {
668 sqlite3_create_module( db, "dbstat", dbstat_module, 0 );
669 return SQLITE_OK;
670 }
671  
672 #endif
673  
674 #if SQLITE_TEST
675 //#include <tcl.h>
676  
677 static int test_dbstat(
678 object clientData,
679 Tcl_Interp interp,
680 int objc,
681 Tcl_Obj[] objv
682 )
683 {
684 #if SQLITE_OMIT_VIRTUALTABLE
685 Tcl_AppendResult(interp, "dbstat not available because of "
686 "SQLITE_OMIT_VIRTUALTABLE", (void*)0);
687 return TCL.TCL_ERROR;
688 #else
689 //sqlite3 db;
690 string zDb;
691 Tcl_CmdInfo cmdInfo;
692  
693 if ( objc != 2 )
694 {
695 TCL.Tcl_WrongNumArgs( interp, 1, objv, "DB" );
696 return TCL.TCL_ERROR;
697 }
698  
699 zDb = TCL.Tcl_GetString( objv[1] );
700 if ( !TCL.Tcl_GetCommandInfo( interp, zDb, out cmdInfo ) )
701 {
702 sqlite3 db = ( (SqliteDb)cmdInfo.objClientData ).db;
703 sqlite3_dbstat_register( db );
704 }
705 return TCL.TCL_OK;
706 #endif
707 }
708  
709 public static int SqlitetestStat_Init( Tcl_Interp interp )
710 {
711 TCL.Tcl_CreateObjCommand( interp, "register_dbstat_vtab", test_dbstat, null, null );
712 return TCL.TCL_OK;
713 }
714 #endif
715 }
716 }