wasCSharpSQLite – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using System.Text; |
||
4 | |||
5 | using u8 = System.Byte; |
||
6 | |||
7 | namespace Community.CsharpSqlite |
||
8 | { |
||
9 | using sqlite3_int64 = System.Int64; |
||
10 | using sqlite3_stmt = Sqlite3.Vdbe; |
||
11 | |||
12 | public partial class Sqlite3 |
||
13 | { |
||
14 | /* |
||
15 | ** 2005 July 8 |
||
16 | ** |
||
17 | ** The author disclaims copyright to this source code. In place of |
||
18 | ** a legal notice, here is a blessing: |
||
19 | ** |
||
20 | ** May you do good and not evil. |
||
21 | ** May you find forgiveness for yourself and forgive others. |
||
22 | ** May you share freely, never taking more than you give. |
||
23 | ** |
||
24 | ************************************************************************* |
||
25 | ** This file contains code associated with the ANALYZE command. |
||
26 | ************************************************************************* |
||
27 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
28 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
29 | ** |
||
30 | ** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e |
||
31 | ** |
||
32 | ************************************************************************* |
||
33 | */ |
||
34 | #if !SQLITE_OMIT_ANALYZE |
||
35 | //#include "sqliteInt.h" |
||
36 | |||
37 | /* |
||
38 | ** This routine generates code that opens the sqlite_stat1 table for |
||
39 | ** writing with cursor iStatCur. If the library was built with the |
||
40 | ** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is |
||
41 | ** opened for writing using cursor (iStatCur+1) |
||
42 | ** |
||
43 | ** If the sqlite_stat1 tables does not previously exist, it is created. |
||
44 | ** Similarly, if the sqlite_stat2 table does not exist and the library |
||
45 | ** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. |
||
46 | ** |
||
47 | ** Argument zWhere may be a pointer to a buffer containing a table name, |
||
48 | ** or it may be a NULL pointer. If it is not NULL, then all entries in |
||
49 | ** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated |
||
50 | ** with the named table are deleted. If zWhere==0, then code is generated |
||
51 | ** to delete all stat table entries. |
||
52 | */ |
||
53 | public struct _aTable |
||
54 | { |
||
55 | public string zName; |
||
56 | public string zCols; |
||
57 | public _aTable( string zName, string zCols ) |
||
58 | { |
||
59 | this.zName = zName; |
||
60 | this.zCols = zCols; |
||
61 | } |
||
62 | }; |
||
63 | static _aTable[] aTable = new _aTable[]{ |
||
64 | new _aTable( "sqlite_stat1", "tbl,idx,stat" ), |
||
65 | #if SQLITE_ENABLE_STAT2 |
||
66 | new _aTable( "sqlite_stat2", "tbl,idx,sampleno,sample" ), |
||
67 | #endif |
||
68 | }; |
||
69 | |||
70 | static void openStatTable( |
||
71 | Parse pParse, /* Parsing context */ |
||
72 | int iDb, /* The database we are looking in */ |
||
73 | int iStatCur, /* Open the sqlite_stat1 table on this cursor */ |
||
74 | string zWhere, /* Delete entries for this table or index */ |
||
75 | string zWhereType /* Either "tbl" or "idx" */ |
||
76 | ) |
||
77 | { |
||
78 | int[] aRoot = new int[] { 0, 0 }; |
||
79 | u8[] aCreateTbl = new u8[] { 0, 0 }; |
||
80 | |||
81 | int i; |
||
82 | sqlite3 db = pParse.db; |
||
83 | Db pDb; |
||
84 | Vdbe v = sqlite3GetVdbe( pParse ); |
||
85 | |||
86 | if ( v == null ) |
||
87 | return; |
||
88 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); |
||
89 | Debug.Assert( sqlite3VdbeDb( v ) == db ); |
||
90 | pDb = db.aDb[iDb]; |
||
91 | |||
92 | for ( i = 0; i < ArraySize( aTable ); i++ ) |
||
93 | { |
||
94 | string zTab = aTable[i].zName; |
||
95 | Table pStat; |
||
96 | if ( ( pStat = sqlite3FindTable( db, zTab, pDb.zName ) ) == null ) |
||
97 | { |
||
98 | /* The sqlite_stat[12] table does not exist. Create it. Note that a |
||
99 | ** side-effect of the CREATE TABLE statement is to leave the rootpage |
||
100 | ** of the new table in register pParse.regRoot. This is important |
||
101 | ** because the OpenWrite opcode below will be needing it. */ |
||
102 | sqlite3NestedParse( pParse, |
||
103 | "CREATE TABLE %Q.%s(%s)", pDb.zName, zTab, aTable[i].zCols |
||
104 | ); |
||
105 | aRoot[i] = pParse.regRoot; |
||
106 | aCreateTbl[i] = 1; |
||
107 | } |
||
108 | else |
||
109 | { |
||
110 | /* The table already exists. If zWhere is not NULL, delete all entries |
||
111 | ** associated with the table zWhere. If zWhere is NULL, delete the |
||
112 | ** entire contents of the table. */ |
||
113 | aRoot[i] = pStat.tnum; |
||
114 | sqlite3TableLock( pParse, iDb, aRoot[i], 1, zTab ); |
||
115 | if ( !string.IsNullOrEmpty( zWhere ) ) |
||
116 | { |
||
117 | sqlite3NestedParse( pParse, |
||
118 | "DELETE FROM %Q.%s WHERE %s=%Q", pDb.zName, zTab, zWhereType, zWhere |
||
119 | ); |
||
120 | } |
||
121 | else |
||
122 | { |
||
123 | /* The sqlite_stat[12] table already exists. Delete all rows. */ |
||
124 | sqlite3VdbeAddOp2( v, OP_Clear, aRoot[i], iDb ); |
||
125 | } |
||
126 | } |
||
127 | } |
||
128 | |||
129 | /* Open the sqlite_stat[12] tables for writing. */ |
||
130 | for ( i = 0; i < ArraySize( aTable ); i++ ) |
||
131 | { |
||
132 | sqlite3VdbeAddOp3( v, OP_OpenWrite, iStatCur + i, aRoot[i], iDb ); |
||
133 | sqlite3VdbeChangeP4( v, -1, 3, P4_INT32 ); |
||
134 | sqlite3VdbeChangeP5( v, aCreateTbl[i] ); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | /* |
||
139 | ** Generate code to do an analysis of all indices associated with |
||
140 | ** a single table. |
||
141 | */ |
||
142 | static void analyzeOneTable( |
||
143 | Parse pParse, /* Parser context */ |
||
144 | Table pTab, /* Table whose indices are to be analyzed */ |
||
145 | Index pOnlyIdx, /* If not NULL, only analyze this one index */ |
||
146 | int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ |
||
147 | int iMem /* Available memory locations begin here */ |
||
148 | ) |
||
149 | { |
||
150 | sqlite3 db = pParse.db; /* Database handle */ |
||
151 | Index pIdx; /* An index to being analyzed */ |
||
152 | int iIdxCur; /* Cursor open on index being analyzed */ |
||
153 | Vdbe v; /* The virtual machine being built up */ |
||
154 | int i; /* Loop counter */ |
||
155 | int topOfLoop; /* The top of the loop */ |
||
156 | int endOfLoop; /* The end of the loop */ |
||
157 | int jZeroRows = -1; /* Jump from here if number of rows is zero */ |
||
158 | int iDb; /* Index of database containing pTab */ |
||
159 | int regTabname = iMem++; /* Register containing table name */ |
||
160 | int regIdxname = iMem++; /* Register containing index name */ |
||
161 | int regSampleno = iMem++; /* Register containing next sample number */ |
||
162 | int regCol = iMem++; /* Content of a column analyzed table */ |
||
163 | int regRec = iMem++; /* Register holding completed record */ |
||
164 | int regTemp = iMem++; /* Temporary use register */ |
||
165 | int regRowid = iMem++; /* Rowid for the inserted record */ |
||
166 | |||
167 | #if SQLITE_ENABLE_STAT2 |
||
168 | int addr = 0; /* Instruction address */ |
||
169 | int regTemp2 = iMem++; /* Temporary use register */ |
||
170 | int regSamplerecno = iMem++; /* Index of next sample to record */ |
||
171 | int regRecno = iMem++; /* Current sample index */ |
||
172 | int regLast = iMem++; /* Index of last sample to record */ |
||
173 | int regFirst = iMem++; /* Index of first sample to record */ |
||
174 | #endif |
||
175 | |||
176 | v = sqlite3GetVdbe( pParse ); |
||
177 | if ( v == null || NEVER( pTab == null ) ) |
||
178 | { |
||
179 | return; |
||
180 | } |
||
181 | if ( pTab.tnum == 0 ) |
||
182 | { |
||
183 | /* Do not gather statistics on views or virtual tables */ |
||
184 | return; |
||
185 | } |
||
186 | if ( pTab.zName.StartsWith( "sqlite_", StringComparison.OrdinalIgnoreCase ) ) |
||
187 | { |
||
188 | /* Do not gather statistics on system tables */ |
||
189 | return; |
||
190 | } |
||
191 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) ); |
||
192 | iDb = sqlite3SchemaToIndex( db, pTab.pSchema ); |
||
193 | Debug.Assert( iDb >= 0 ); |
||
194 | Debug.Assert( sqlite3SchemaMutexHeld(db, iDb, null) ); |
||
195 | #if !SQLITE_OMIT_AUTHORIZATION |
||
196 | if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab.zName, 0, |
||
197 | db.aDb[iDb].zName ) ){ |
||
198 | return; |
||
199 | } |
||
200 | #endif |
||
201 | |||
202 | /* Establish a read-lock on the table at the shared-cache level. */ |
||
203 | sqlite3TableLock( pParse, iDb, pTab.tnum, 0, pTab.zName ); |
||
204 | |||
205 | iIdxCur = pParse.nTab++; |
||
206 | sqlite3VdbeAddOp4( v, OP_String8, 0, regTabname, 0, pTab.zName, 0 ); |
||
207 | for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext ) |
||
208 | { |
||
209 | int nCol; |
||
210 | KeyInfo pKey; |
||
211 | if ( pOnlyIdx != null && pOnlyIdx != pIdx ) |
||
212 | continue; |
||
213 | nCol = pIdx.nColumn; |
||
214 | pKey = sqlite3IndexKeyinfo( pParse, pIdx ); |
||
215 | |||
216 | if ( iMem + 1 + ( nCol * 2 ) > pParse.nMem ) |
||
217 | { |
||
218 | pParse.nMem = iMem + 1 + ( nCol * 2 ); |
||
219 | } |
||
220 | |||
221 | /* Open a cursor to the index to be analyzed. */ |
||
222 | Debug.Assert( iDb == sqlite3SchemaToIndex( db, pIdx.pSchema ) ); |
||
223 | sqlite3VdbeAddOp4( v, OP_OpenRead, iIdxCur, pIdx.tnum, iDb, |
||
224 | pKey, P4_KEYINFO_HANDOFF ); |
||
225 | VdbeComment( v, "%s", pIdx.zName ); |
||
226 | |||
227 | /* Populate the registers containing the index names. */ |
||
228 | sqlite3VdbeAddOp4( v, OP_String8, 0, regIdxname, 0, pIdx.zName, 0 ); |
||
229 | |||
230 | #if SQLITE_ENABLE_STAT2 |
||
231 | |||
232 | /* If this iteration of the loop is generating code to analyze the |
||
233 | ** first index in the pTab.pIndex list, then register regLast has |
||
234 | ** not been populated. In this case populate it now. */ |
||
235 | if ( pTab.pIndex == pIdx ) |
||
236 | { |
||
237 | sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno ); |
||
238 | sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES * 2 - 1, regTemp ); |
||
239 | sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES * 2, regTemp2 ); |
||
240 | |||
241 | sqlite3VdbeAddOp2( v, OP_Count, iIdxCur, regLast ); |
||
242 | sqlite3VdbeAddOp2( v, OP_Null, 0, regFirst ); |
||
243 | addr = sqlite3VdbeAddOp3( v, OP_Lt, regSamplerecno, 0, regLast ); |
||
244 | sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regLast, regFirst ); |
||
245 | sqlite3VdbeAddOp3( v, OP_Multiply, regLast, regTemp, regLast ); |
||
246 | sqlite3VdbeAddOp2( v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES * 2 - 2 ); |
||
247 | sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regLast, regLast ); |
||
248 | sqlite3VdbeJumpHere( v, addr ); |
||
249 | } |
||
250 | |||
251 | /* Zero the regSampleno and regRecno registers. */ |
||
252 | sqlite3VdbeAddOp2( v, OP_Integer, 0, regSampleno ); |
||
253 | sqlite3VdbeAddOp2( v, OP_Integer, 0, regRecno ); |
||
254 | sqlite3VdbeAddOp2( v, OP_Copy, regFirst, regSamplerecno ); |
||
255 | #endif |
||
256 | |||
257 | /* The block of memory cells initialized here is used as follows. |
||
258 | ** |
||
259 | ** iMem: |
||
260 | ** The total number of rows in the table. |
||
261 | ** |
||
262 | ** iMem+1 .. iMem+nCol: |
||
263 | ** Number of distinct entries in index considering the |
||
264 | ** left-most N columns only, where N is between 1 and nCol, |
||
265 | ** inclusive. |
||
266 | ** |
||
267 | ** iMem+nCol+1 .. Mem+2*nCol: |
||
268 | ** Previous value of indexed columns, from left to right. |
||
269 | ** |
||
270 | ** Cells iMem through iMem+nCol are initialized to 0. The others are |
||
271 | ** initialized to contain an SQL NULL. |
||
272 | */ |
||
273 | for ( i = 0; i <= nCol; i++ ) |
||
274 | { |
||
275 | sqlite3VdbeAddOp2( v, OP_Integer, 0, iMem + i ); |
||
276 | } |
||
277 | for ( i = 0; i < nCol; i++ ) |
||
278 | { |
||
279 | sqlite3VdbeAddOp2( v, OP_Null, 0, iMem + nCol + i + 1 ); |
||
280 | } |
||
281 | |||
282 | /* Start the analysis loop. This loop runs through all the entries in |
||
283 | ** the index b-tree. */ |
||
284 | endOfLoop = sqlite3VdbeMakeLabel( v ); |
||
285 | sqlite3VdbeAddOp2( v, OP_Rewind, iIdxCur, endOfLoop ); |
||
286 | topOfLoop = sqlite3VdbeCurrentAddr( v ); |
||
287 | sqlite3VdbeAddOp2( v, OP_AddImm, iMem, 1 ); |
||
288 | |||
289 | for ( i = 0; i < nCol; i++ ) |
||
290 | { |
||
291 | sqlite3VdbeAddOp3( v, OP_Column, iIdxCur, i, regCol ); |
||
292 | CollSeq pColl; |
||
293 | if ( i == 0 ) |
||
294 | { |
||
295 | #if SQLITE_ENABLE_STAT2 |
||
296 | /* Check if the record that cursor iIdxCur points to contains a |
||
297 | ** value that should be stored in the sqlite_stat2 table. If so, |
||
298 | ** store it. */ |
||
299 | int ne = sqlite3VdbeAddOp3( v, OP_Ne, regRecno, 0, regSamplerecno ); |
||
300 | Debug.Assert( regTabname + 1 == regIdxname |
||
301 | && regTabname + 2 == regSampleno |
||
302 | && regTabname + 3 == regCol |
||
303 | ); |
||
304 | sqlite3VdbeChangeP5( v, SQLITE_JUMPIFNULL ); |
||
305 | sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0 ); |
||
306 | sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur + 1, regRowid ); |
||
307 | sqlite3VdbeAddOp3( v, OP_Insert, iStatCur + 1, regRec, regRowid ); |
||
308 | |||
309 | /* Calculate new values for regSamplerecno and regSampleno. |
||
310 | ** |
||
311 | ** sampleno = sampleno + 1 |
||
312 | ** samplerecno = samplerecno+(remaining records)/(remaining samples) |
||
313 | */ |
||
314 | sqlite3VdbeAddOp2( v, OP_AddImm, regSampleno, 1 ); |
||
315 | sqlite3VdbeAddOp3( v, OP_Subtract, regRecno, regLast, regTemp ); |
||
316 | sqlite3VdbeAddOp2( v, OP_AddImm, regTemp, -1 ); |
||
317 | sqlite3VdbeAddOp2( v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2 ); |
||
318 | sqlite3VdbeAddOp3( v, OP_Subtract, regSampleno, regTemp2, regTemp2 ); |
||
319 | sqlite3VdbeAddOp3( v, OP_Divide, regTemp2, regTemp, regTemp ); |
||
320 | sqlite3VdbeAddOp3( v, OP_Add, regSamplerecno, regTemp, regSamplerecno ); |
||
321 | |||
322 | sqlite3VdbeJumpHere( v, ne ); |
||
323 | sqlite3VdbeAddOp2( v, OP_AddImm, regRecno, 1 ); |
||
324 | #endif |
||
325 | |||
326 | /* Always record the very first row */ |
||
327 | sqlite3VdbeAddOp1( v, OP_IfNot, iMem + 1 ); |
||
328 | } |
||
329 | Debug.Assert( pIdx.azColl != null ); |
||
330 | Debug.Assert( pIdx.azColl[i] != null ); |
||
331 | pColl = sqlite3LocateCollSeq( pParse, pIdx.azColl[i] ); |
||
332 | sqlite3VdbeAddOp4( v, OP_Ne, regCol, 0, iMem + nCol + i + 1, |
||
333 | pColl, P4_COLLSEQ ); |
||
334 | sqlite3VdbeChangeP5( v, SQLITE_NULLEQ ); |
||
335 | } |
||
336 | //if( db.mallocFailed ){ |
||
337 | // /* If a malloc failure has occurred, then the result of the expression |
||
338 | // ** passed as the second argument to the call to sqlite3VdbeJumpHere() |
||
339 | // ** below may be negative. Which causes an Debug.Assert() to fail (or an |
||
340 | // ** out-of-bounds write if SQLITE_DEBUG is not defined). */ |
||
341 | // return; |
||
342 | //} |
||
343 | sqlite3VdbeAddOp2( v, OP_Goto, 0, endOfLoop ); |
||
344 | for ( i = 0; i < nCol; i++ ) |
||
345 | { |
||
346 | int addr2 = sqlite3VdbeCurrentAddr( v ) - ( nCol * 2 ); |
||
347 | if ( i == 0 ) |
||
348 | { |
||
349 | sqlite3VdbeJumpHere( v, addr2 - 1 ); /* Set jump dest for the OP_IfNot */ |
||
350 | } |
||
351 | sqlite3VdbeJumpHere( v, addr2 ); /* Set jump dest for the OP_Ne */ |
||
352 | sqlite3VdbeAddOp2( v, OP_AddImm, iMem + i + 1, 1 ); |
||
353 | sqlite3VdbeAddOp3( v, OP_Column, iIdxCur, i, iMem + nCol + i + 1 ); |
||
354 | } |
||
355 | |||
356 | /* End of the analysis loop. */ |
||
357 | sqlite3VdbeResolveLabel( v, endOfLoop ); |
||
358 | sqlite3VdbeAddOp2( v, OP_Next, iIdxCur, topOfLoop ); |
||
359 | sqlite3VdbeAddOp1( v, OP_Close, iIdxCur ); |
||
360 | |||
361 | /* Store the results in sqlite_stat1. |
||
362 | ** |
||
363 | ** The result is a single row of the sqlite_stat1 table. The first |
||
364 | ** two columns are the names of the table and index. The third column |
||
365 | ** is a string composed of a list of integer statistics about the |
||
366 | ** index. The first integer in the list is the total number of entries |
||
367 | ** in the index. There is one additional integer in the list for each |
||
368 | ** column of the table. This additional integer is a guess of how many |
||
369 | ** rows of the table the index will select. If D is the count of distinct |
||
370 | ** values and K is the total number of rows, then the integer is computed |
||
371 | ** as: |
||
372 | ** |
||
373 | ** I = (K+D-1)/D |
||
374 | ** |
||
375 | ** If K==0 then no entry is made into the sqlite_stat1 table. |
||
376 | ** If K>0 then it is always the case the D>0 so division by zero |
||
377 | ** is never possible. |
||
378 | */ |
||
379 | sqlite3VdbeAddOp2( v, OP_SCopy, iMem, regSampleno ); |
||
380 | if ( jZeroRows < 0 ) |
||
381 | { |
||
382 | jZeroRows = sqlite3VdbeAddOp1( v, OP_IfNot, iMem ); |
||
383 | } |
||
384 | for ( i = 0; i < nCol; i++ ) |
||
385 | { |
||
386 | sqlite3VdbeAddOp4( v, OP_String8, 0, regTemp, 0, " ", 0 ); |
||
387 | sqlite3VdbeAddOp3( v, OP_Concat, regTemp, regSampleno, regSampleno ); |
||
388 | sqlite3VdbeAddOp3( v, OP_Add, iMem, iMem + i + 1, regTemp ); |
||
389 | sqlite3VdbeAddOp2( v, OP_AddImm, regTemp, -1 ); |
||
390 | sqlite3VdbeAddOp3( v, OP_Divide, iMem + i + 1, regTemp, regTemp ); |
||
391 | sqlite3VdbeAddOp1( v, OP_ToInt, regTemp ); |
||
392 | sqlite3VdbeAddOp3( v, OP_Concat, regTemp, regSampleno, regSampleno ); |
||
393 | } |
||
394 | sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0 ); |
||
395 | sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur, regRowid ); |
||
396 | sqlite3VdbeAddOp3( v, OP_Insert, iStatCur, regRec, regRowid ); |
||
397 | sqlite3VdbeChangeP5( v, OPFLAG_APPEND ); |
||
398 | } |
||
399 | |||
400 | /* If the table has no indices, create a single sqlite_stat1 entry |
||
401 | ** containing NULL as the index name and the row count as the content. |
||
402 | */ |
||
403 | if ( pTab.pIndex == null ) |
||
404 | { |
||
405 | sqlite3VdbeAddOp3( v, OP_OpenRead, iIdxCur, pTab.tnum, iDb ); |
||
406 | VdbeComment( v, "%s", pTab.zName ); |
||
407 | sqlite3VdbeAddOp2( v, OP_Count, iIdxCur, regSampleno ); |
||
408 | sqlite3VdbeAddOp1( v, OP_Close, iIdxCur ); |
||
409 | jZeroRows = sqlite3VdbeAddOp1( v, OP_IfNot, regSampleno ); |
||
410 | } |
||
411 | else |
||
412 | { |
||
413 | sqlite3VdbeJumpHere( v, jZeroRows ); |
||
414 | jZeroRows = sqlite3VdbeAddOp0( v, OP_Goto ); |
||
415 | } |
||
416 | sqlite3VdbeAddOp2( v, OP_Null, 0, regIdxname ); |
||
417 | sqlite3VdbeAddOp4( v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0 ); |
||
418 | sqlite3VdbeAddOp2( v, OP_NewRowid, iStatCur, regRowid ); |
||
419 | sqlite3VdbeAddOp3( v, OP_Insert, iStatCur, regRec, regRowid ); |
||
420 | sqlite3VdbeChangeP5( v, OPFLAG_APPEND ); |
||
421 | if ( pParse.nMem < regRec ) |
||
422 | pParse.nMem = regRec; |
||
423 | sqlite3VdbeJumpHere( v, jZeroRows ); |
||
424 | } |
||
425 | |||
426 | /* |
||
427 | ** Generate code that will cause the most recent index analysis to |
||
428 | ** be loaded into internal hash tables where is can be used. |
||
429 | */ |
||
430 | static void loadAnalysis( Parse pParse, int iDb ) |
||
431 | { |
||
432 | Vdbe v = sqlite3GetVdbe( pParse ); |
||
433 | if ( v != null ) |
||
434 | { |
||
435 | sqlite3VdbeAddOp1( v, OP_LoadAnalysis, iDb ); |
||
436 | } |
||
437 | } |
||
438 | |||
439 | /* |
||
440 | ** Generate code that will do an analysis of an entire database |
||
441 | */ |
||
442 | static void analyzeDatabase( Parse pParse, int iDb ) |
||
443 | { |
||
444 | sqlite3 db = pParse.db; |
||
445 | Schema pSchema = db.aDb[iDb].pSchema; /* Schema of database iDb */ |
||
446 | HashElem k; |
||
447 | int iStatCur; |
||
448 | int iMem; |
||
449 | |||
450 | sqlite3BeginWriteOperation( pParse, 0, iDb ); |
||
451 | iStatCur = pParse.nTab; |
||
452 | pParse.nTab += 2; |
||
453 | openStatTable( pParse, iDb, iStatCur, null, null ); |
||
454 | iMem = pParse.nMem + 1; |
||
455 | Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); |
||
456 | //for(k=sqliteHashFirst(pSchema.tblHash); k; k=sqliteHashNext(k)){ |
||
457 | for ( k = pSchema.tblHash.first; k != null; k = k.next ) |
||
458 | { |
||
459 | Table pTab = (Table)k.data;// sqliteHashData( k ); |
||
460 | analyzeOneTable( pParse, pTab, null, iStatCur, iMem ); |
||
461 | } |
||
462 | loadAnalysis( pParse, iDb ); |
||
463 | } |
||
464 | |||
465 | /* |
||
466 | ** Generate code that will do an analysis of a single table in |
||
467 | ** a database. If pOnlyIdx is not NULL then it is a single index |
||
468 | ** in pTab that should be analyzed. |
||
469 | */ |
||
470 | static void analyzeTable( Parse pParse, Table pTab, Index pOnlyIdx) |
||
471 | { |
||
472 | int iDb; |
||
473 | int iStatCur; |
||
474 | |||
475 | Debug.Assert( pTab != null ); |
||
476 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) ); |
||
477 | iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema ); |
||
478 | sqlite3BeginWriteOperation( pParse, 0, iDb ); |
||
479 | iStatCur = pParse.nTab; |
||
480 | pParse.nTab += 2; |
||
481 | if ( pOnlyIdx != null ) |
||
482 | { |
||
483 | openStatTable( pParse, iDb, iStatCur, pOnlyIdx.zName, "idx" ); |
||
484 | } |
||
485 | else |
||
486 | { |
||
487 | openStatTable( pParse, iDb, iStatCur, pTab.zName, "tbl" ); |
||
488 | } |
||
489 | analyzeOneTable( pParse, pTab, pOnlyIdx, iStatCur, pParse.nMem + 1 ); |
||
490 | loadAnalysis( pParse, iDb ); |
||
491 | } |
||
492 | |||
493 | /* |
||
494 | ** Generate code for the ANALYZE command. The parser calls this routine |
||
495 | ** when it recognizes an ANALYZE command. |
||
496 | ** |
||
497 | ** ANALYZE -- 1 |
||
498 | ** ANALYZE <database> -- 2 |
||
499 | ** ANALYZE ?<database>.?<tablename> -- 3 |
||
500 | ** |
||
501 | ** Form 1 causes all indices in all attached databases to be analyzed. |
||
502 | ** Form 2 analyzes all indices the single database named. |
||
503 | ** Form 3 analyzes all indices associated with the named table. |
||
504 | */ |
||
505 | // OVERLOADS, so I don't need to rewrite parse.c |
||
506 | static void sqlite3Analyze( Parse pParse, int null_2, int null_3 ) |
||
507 | { |
||
508 | sqlite3Analyze( pParse, null, null ); |
||
509 | } |
||
510 | static void sqlite3Analyze( Parse pParse, Token pName1, Token pName2 ) |
||
511 | { |
||
512 | sqlite3 db = pParse.db; |
||
513 | int iDb; |
||
514 | int i; |
||
515 | string z, zDb; |
||
516 | Table pTab; |
||
517 | Index pIdx; |
||
518 | Token pTableName = null; |
||
519 | |||
520 | /* Read the database schema. If an error occurs, leave an error message |
||
521 | ** and code in pParse and return NULL. */ |
||
522 | Debug.Assert( sqlite3BtreeHoldsAllMutexes( pParse.db ) ); |
||
523 | if ( SQLITE_OK != sqlite3ReadSchema( pParse ) ) |
||
524 | { |
||
525 | return; |
||
526 | } |
||
527 | |||
528 | Debug.Assert( pName2 != null || pName1 == null ); |
||
529 | if ( pName1 == null ) |
||
530 | { |
||
531 | /* Form 1: Analyze everything */ |
||
532 | for ( i = 0; i < db.nDb; i++ ) |
||
533 | { |
||
534 | if ( i == 1 ) |
||
535 | continue; /* Do not analyze the TEMP database */ |
||
536 | analyzeDatabase( pParse, i ); |
||
537 | } |
||
538 | } |
||
539 | else if ( pName2.n == 0 ) |
||
540 | { |
||
541 | /* Form 2: Analyze the database or table named */ |
||
542 | iDb = sqlite3FindDb( db, pName1 ); |
||
543 | if ( iDb >= 0 ) |
||
544 | { |
||
545 | analyzeDatabase( pParse, iDb ); |
||
546 | } |
||
547 | else |
||
548 | { |
||
549 | z = sqlite3NameFromToken( db, pName1 ); |
||
550 | if ( z != null ) |
||
551 | { |
||
552 | if ( ( pIdx = sqlite3FindIndex( db, z, null ) ) != null ) |
||
553 | { |
||
554 | analyzeTable( pParse, pIdx.pTable, pIdx ); |
||
555 | } |
||
556 | else if ( ( pTab = sqlite3LocateTable( pParse, 0, z, null ) ) != null ) |
||
557 | { |
||
558 | analyzeTable( pParse, pTab, null ); |
||
559 | } |
||
560 | z = null;//sqlite3DbFree( db, z ); |
||
561 | } |
||
562 | } |
||
563 | } |
||
564 | else |
||
565 | { |
||
566 | /* Form 3: Analyze the fully qualified table name */ |
||
567 | iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pTableName ); |
||
568 | if ( iDb >= 0 ) |
||
569 | { |
||
570 | zDb = db.aDb[iDb].zName; |
||
571 | z = sqlite3NameFromToken( db, pTableName ); |
||
572 | if ( z != null ) |
||
573 | { |
||
574 | if ( ( pIdx = sqlite3FindIndex( db, z, zDb ) ) != null ) |
||
575 | { |
||
576 | analyzeTable( pParse, pIdx.pTable, pIdx ); |
||
577 | } |
||
578 | else if ( ( pTab = sqlite3LocateTable( pParse, 0, z, zDb ) ) != null ) |
||
579 | { |
||
580 | analyzeTable( pParse, pTab, null ); |
||
581 | } |
||
582 | z = null; //sqlite3DbFree( db, z ); |
||
583 | } |
||
584 | } |
||
585 | } |
||
586 | } |
||
587 | |||
588 | /* |
||
589 | ** Used to pass information from the analyzer reader through to the |
||
590 | ** callback routine. |
||
591 | */ |
||
592 | //typedef struct analysisInfo analysisInfo; |
||
593 | public struct analysisInfo |
||
594 | { |
||
595 | public sqlite3 db; |
||
596 | public string zDatabase; |
||
597 | }; |
||
598 | |||
599 | /* |
||
600 | ** This callback is invoked once for each index when reading the |
||
601 | ** sqlite_stat1 table. |
||
602 | ** |
||
603 | ** argv[0] = name of the table |
||
604 | ** argv[1] = name of the index (might be NULL) |
||
605 | ** argv[2] = results of analysis - on integer for each column |
||
606 | ** |
||
607 | ** Entries for which argv[1]==NULL simply record the number of rows in |
||
608 | ** the table. |
||
609 | */ |
||
610 | static int analysisLoader( object pData, sqlite3_int64 argc, object Oargv, object NotUsed ) |
||
611 | { |
||
612 | string[] argv = (string[])Oargv; |
||
613 | analysisInfo pInfo = (analysisInfo)pData; |
||
614 | Index pIndex; |
||
615 | Table pTable; |
||
616 | int i, c, n; |
||
617 | int v; |
||
618 | string z; |
||
619 | |||
620 | Debug.Assert( argc == 3 ); |
||
621 | UNUSED_PARAMETER2( NotUsed, argc ); |
||
622 | if ( argv == null || argv[0] == null || argv[2] == null ) |
||
623 | { |
||
624 | return 0; |
||
625 | } |
||
626 | pTable = sqlite3FindTable( pInfo.db, argv[0], pInfo.zDatabase ); |
||
627 | if ( pTable == null ) |
||
628 | { |
||
629 | return 0; |
||
630 | } |
||
631 | if ( !string.IsNullOrEmpty( argv[1] ) ) |
||
632 | { |
||
633 | pIndex = sqlite3FindIndex( pInfo.db, argv[1], pInfo.zDatabase ); |
||
634 | } |
||
635 | else |
||
636 | { |
||
637 | pIndex = null; |
||
638 | } |
||
639 | |||
640 | n = pIndex != null ? pIndex.nColumn : 0; |
||
641 | z = argv[2]; |
||
642 | int zIndex = 0; |
||
643 | for ( i = 0; z != null && i <= n; i++ ) |
||
644 | { |
||
645 | v = 0; |
||
646 | while ( zIndex < z.Length && ( c = z[zIndex] ) >= '0' && c <= '9' ) |
||
647 | { |
||
648 | v = v * 10 + c - '0'; |
||
649 | zIndex++; |
||
650 | } |
||
651 | if ( i == 0 ) |
||
652 | pTable.nRowEst = (uint)v; |
||
653 | if ( pIndex == null ) |
||
654 | break; |
||
655 | pIndex.aiRowEst[i] = v; |
||
656 | if ( zIndex < z.Length && z[zIndex] == ' ' ) |
||
657 | zIndex++; |
||
658 | if ( z.Substring(zIndex).CompareTo("unordered")==0)//memcmp( z, "unordered", 10 ) == 0 ) |
||
659 | { |
||
660 | pIndex.bUnordered = 1; |
||
661 | break; |
||
662 | } |
||
663 | } |
||
664 | return 0; |
||
665 | } |
||
666 | |||
667 | /* |
||
668 | ** If the Index.aSample variable is not NULL, delete the aSample[] array |
||
669 | ** and its contents. |
||
670 | */ |
||
671 | static void sqlite3DeleteIndexSamples( sqlite3 db, Index pIdx ) |
||
672 | { |
||
673 | #if SQLITE_ENABLE_STAT2 |
||
674 | if ( pIdx.aSample != null ) |
||
675 | { |
||
676 | int j; |
||
677 | for ( j = 0; j < SQLITE_INDEX_SAMPLES; j++ ) |
||
678 | { |
||
679 | IndexSample p = pIdx.aSample[j]; |
||
680 | if ( p.eType == SQLITE_TEXT || p.eType == SQLITE_BLOB ) |
||
681 | { |
||
682 | p.u.z = null;//sqlite3DbFree(db, p.u.z); |
||
683 | p.u.zBLOB = null; |
||
684 | } |
||
685 | } |
||
686 | sqlite3DbFree( db, ref pIdx.aSample ); |
||
687 | } |
||
688 | #else |
||
689 | UNUSED_PARAMETER(db); |
||
690 | UNUSED_PARAMETER( pIdx ); |
||
691 | #endif |
||
692 | } |
||
693 | |||
694 | /* |
||
695 | ** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The |
||
696 | ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] |
||
697 | ** arrays. The contents of sqlite_stat2 are used to populate the |
||
698 | ** Index.aSample[] arrays. |
||
699 | ** |
||
700 | ** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR |
||
701 | ** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined |
||
702 | ** during compilation and the sqlite_stat2 table is present, no data is |
||
703 | ** read from it. |
||
704 | ** |
||
705 | ** If SQLITE_ENABLE_STAT2 was defined during compilation and the |
||
706 | ** sqlite_stat2 table is not present in the database, SQLITE_ERROR is |
||
707 | ** returned. However, in this case, data is read from the sqlite_stat1 |
||
708 | ** table (if it is present) before returning. |
||
709 | ** |
||
710 | ** If an OOM error occurs, this function always sets db.mallocFailed. |
||
711 | ** This means if the caller does not care about other errors, the return |
||
712 | ** code may be ignored. |
||
713 | */ |
||
714 | static int sqlite3AnalysisLoad( sqlite3 db, int iDb ) |
||
715 | { |
||
716 | analysisInfo sInfo; |
||
717 | HashElem i; |
||
718 | string zSql; |
||
719 | int rc; |
||
720 | |||
721 | Debug.Assert( iDb >= 0 && iDb < db.nDb ); |
||
722 | Debug.Assert( db.aDb[iDb].pBt != null ); |
||
723 | /* Clear any prior statistics */ |
||
724 | Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); |
||
725 | //for(i=sqliteHashFirst(&db.aDb[iDb].pSchema.idxHash);i;i=sqliteHashNext(i)){ |
||
726 | for ( i = db.aDb[iDb].pSchema.idxHash.first; i != null; i = i.next ) |
||
727 | { |
||
728 | Index pIdx = (Index)i.data;// sqliteHashData( i ); |
||
729 | sqlite3DefaultRowEst( pIdx ); |
||
730 | sqlite3DeleteIndexSamples( db, pIdx ); |
||
731 | pIdx.aSample = null; |
||
732 | } |
||
733 | |||
734 | /* Check to make sure the sqlite_stat1 table exists */ |
||
735 | sInfo.db = db; |
||
736 | sInfo.zDatabase = db.aDb[iDb].zName; |
||
737 | if ( sqlite3FindTable( db, "sqlite_stat1", sInfo.zDatabase ) == null ) |
||
738 | { |
||
739 | return SQLITE_ERROR; |
||
740 | } |
||
741 | |||
742 | |||
743 | /* Load new statistics out of the sqlite_stat1 table */ |
||
744 | zSql = sqlite3MPrintf( db, |
||
745 | "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase ); |
||
746 | //if ( zSql == null ) |
||
747 | //{ |
||
748 | // rc = SQLITE_NOMEM; |
||
749 | //} |
||
750 | //else |
||
751 | { |
||
752 | rc = sqlite3_exec( db, zSql, (dxCallback)analysisLoader, sInfo, 0 ); |
||
753 | sqlite3DbFree( db, ref zSql ); |
||
754 | } |
||
755 | |||
756 | |||
757 | /* Load the statistics from the sqlite_stat2 table. */ |
||
758 | #if SQLITE_ENABLE_STAT2 |
||
759 | if ( rc == SQLITE_OK && null == sqlite3FindTable( db, "sqlite_stat2", sInfo.zDatabase ) ) |
||
760 | { |
||
761 | rc = SQLITE_ERROR; |
||
762 | } |
||
763 | if ( rc == SQLITE_OK ) |
||
764 | { |
||
765 | sqlite3_stmt pStmt = null; |
||
766 | |||
767 | zSql = sqlite3MPrintf( db, |
||
768 | "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase ); |
||
769 | //if( null==zSql ){ |
||
770 | //rc = SQLITE_NOMEM; |
||
771 | //}else{ |
||
772 | rc = sqlite3_prepare( db, zSql, -1, ref pStmt, 0 ); |
||
773 | sqlite3DbFree( db, ref zSql ); |
||
774 | //} |
||
775 | |||
776 | if ( rc == SQLITE_OK ) |
||
777 | { |
||
778 | while ( sqlite3_step( pStmt ) == SQLITE_ROW ) |
||
779 | { |
||
780 | string zIndex; /* Index name */ |
||
781 | Index pIdx; /* Pointer to the index object */ |
||
782 | zIndex = sqlite3_column_text( pStmt, 0 ); |
||
783 | pIdx = !string.IsNullOrEmpty( zIndex ) ? sqlite3FindIndex( db, zIndex, sInfo.zDatabase ) : null; |
||
784 | if ( pIdx != null ) |
||
785 | { |
||
786 | int iSample = sqlite3_column_int( pStmt, 1 ); |
||
787 | if ( iSample < SQLITE_INDEX_SAMPLES && iSample >= 0 ) |
||
788 | { |
||
789 | int eType = sqlite3_column_type( pStmt, 2 ); |
||
790 | |||
791 | if ( pIdx.aSample == null ) |
||
792 | { |
||
793 | //static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES; |
||
794 | //pIdx->aSample = (IndexSample )sqlite3DbMallocRaw(0, sz); |
||
795 | //if( pIdx.aSample==0 ){ |
||
796 | //db.mallocFailed = 1; |
||
797 | //break; |
||
798 | //} |
||
799 | pIdx.aSample = new IndexSample[SQLITE_INDEX_SAMPLES];//memset(pIdx->aSample, 0, sz); |
||
800 | } |
||
801 | |||
802 | //Debug.Assert( pIdx.aSample != null ); |
||
803 | if ( pIdx.aSample[iSample] == null ) |
||
804 | pIdx.aSample[iSample] = new IndexSample(); |
||
805 | IndexSample pSample = pIdx.aSample[iSample]; |
||
806 | { |
||
807 | pSample.eType = (u8)eType; |
||
808 | if ( eType == SQLITE_INTEGER || eType == SQLITE_FLOAT ) |
||
809 | { |
||
810 | pSample.u.r = sqlite3_column_double( pStmt, 2 ); |
||
811 | } |
||
812 | else if ( eType == SQLITE_TEXT || eType == SQLITE_BLOB ) |
||
813 | { |
||
814 | string z = null; |
||
815 | byte[] zBLOB = null; |
||
816 | //string z = (string )( |
||
817 | //(eType==SQLITE_BLOB) ? |
||
818 | //sqlite3_column_blob(pStmt, 2): |
||
819 | //sqlite3_column_text(pStmt, 2) |
||
820 | //); |
||
821 | if ( eType == SQLITE_BLOB ) |
||
822 | zBLOB = sqlite3_column_blob( pStmt, 2 ); |
||
823 | else |
||
824 | z = sqlite3_column_text( pStmt, 2 ); |
||
825 | int n = sqlite3_column_bytes( pStmt, 2 ); |
||
826 | if ( n > 24 ) |
||
827 | { |
||
828 | n = 24; |
||
829 | } |
||
830 | pSample.nByte = (u8)n; |
||
831 | if ( n < 1 ) |
||
832 | { |
||
833 | pSample.u.z = null; |
||
834 | pSample.u.zBLOB = null; |
||
835 | } |
||
836 | else |
||
837 | { |
||
838 | pSample.u.z = z; |
||
839 | pSample.u.zBLOB = zBLOB; |
||
840 | //pSample->u.z = sqlite3DbMallocRaw(dbMem, n); |
||
841 | //if( pSample->u.z ){ |
||
842 | // memcpy(pSample->u.z, z, n); |
||
843 | //}else{ |
||
844 | // db->mallocFailed = 1; |
||
845 | // break; |
||
846 | //} |
||
847 | } |
||
848 | } |
||
849 | } |
||
850 | } |
||
851 | } |
||
852 | } |
||
853 | rc = sqlite3_finalize( pStmt ); |
||
854 | } |
||
855 | } |
||
856 | #endif |
||
857 | |||
858 | //if( rc==SQLITE_NOMEM ){ |
||
859 | // db.mallocFailed = 1; |
||
860 | //} |
||
861 | return rc; |
||
862 | } |
||
863 | |||
864 | #endif // * SQLITE_OMIT_ANALYZE */ |
||
865 | } |
||
866 | } |