wasCSharpSQLite – Blame information for rev
?pathlinks?
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 | } |