1 | /* |
2 | ** 2008 June 18 |
3 | ** |
4 | ** The author disclaims copyright to this source code. In place of |
5 | ** a legal notice, here is a blessing: |
6 | ** |
7 | ** May you do good and not evil. |
8 | ** May you find forgiveness for yourself and forgive others. |
9 | ** May you share freely, never taking more than you give. |
10 | ** |
11 | ************************************************************************* |
12 | ** |
13 | ** This module implements the sqlite3_status() interface and related |
14 | ** functionality. |
15 | */ |
16 | #include "sqliteInt.h" |
17 | #include "vdbeInt.h" |
18 | |
19 | /* |
20 | ** Variables in which to record status information. |
21 | */ |
22 | #if SQLITE_PTRSIZE>4 |
23 | typedef sqlite3_int64 sqlite3StatValueType; |
24 | #else |
25 | typedef u32 sqlite3StatValueType; |
26 | #endif |
27 | typedef struct sqlite3StatType sqlite3StatType; |
28 | static SQLITE_WSD struct sqlite3StatType { |
29 | sqlite3StatValueType nowValue[10]; /* Current value */ |
30 | sqlite3StatValueType mxValue[10]; /* Maximum value */ |
31 | } sqlite3Stat = { {0,}, {0,} }; |
32 | |
33 | /* |
34 | ** Elements of sqlite3Stat[] are protected by either the memory allocator |
35 | ** mutex, or by the pcache1 mutex. The following array determines which. |
36 | */ |
37 | static const char statMutex[] = { |
38 | 0, /* SQLITE_STATUS_MEMORY_USED */ |
39 | 1, /* SQLITE_STATUS_PAGECACHE_USED */ |
40 | 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */ |
41 | 0, /* SQLITE_STATUS_SCRATCH_USED */ |
42 | 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */ |
43 | 0, /* SQLITE_STATUS_MALLOC_SIZE */ |
44 | 0, /* SQLITE_STATUS_PARSER_STACK */ |
45 | 1, /* SQLITE_STATUS_PAGECACHE_SIZE */ |
46 | 0, /* SQLITE_STATUS_SCRATCH_SIZE */ |
47 | 0, /* SQLITE_STATUS_MALLOC_COUNT */ |
48 | }; |
49 | |
50 | |
51 | /* The "wsdStat" macro will resolve to the status information |
52 | ** state vector. If writable static data is unsupported on the target, |
53 | ** we have to locate the state vector at run-time. In the more common |
54 | ** case where writable static data is supported, wsdStat can refer directly |
55 | ** to the "sqlite3Stat" state vector declared above. |
56 | */ |
57 | #ifdef SQLITE_OMIT_WSD |
58 | # define wsdStatInit sqlite3StatType *x = &GLOBAL(sqlite3StatType,sqlite3Stat) |
59 | # define wsdStat x[0] |
60 | #else |
61 | # define wsdStatInit |
62 | # define wsdStat sqlite3Stat |
63 | #endif |
64 | |
65 | /* |
66 | ** Return the current value of a status parameter. The caller must |
67 | ** be holding the appropriate mutex. |
68 | */ |
69 | sqlite3_int64 sqlite3StatusValue(int op){ |
70 | wsdStatInit; |
71 | assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); |
72 | assert( op>=0 && op<ArraySize(statMutex) ); |
73 | assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() |
74 | : sqlite3MallocMutex()) ); |
75 | return wsdStat.nowValue[op]; |
76 | } |
77 | |
78 | /* |
79 | ** Add N to the value of a status record. The caller must hold the |
80 | ** appropriate mutex. (Locking is checked by assert()). |
81 | ** |
82 | ** The StatusUp() routine can accept positive or negative values for N. |
83 | ** The value of N is added to the current status value and the high-water |
84 | ** mark is adjusted if necessary. |
85 | ** |
86 | ** The StatusDown() routine lowers the current value by N. The highwater |
87 | ** mark is unchanged. N must be non-negative for StatusDown(). |
88 | */ |
89 | void sqlite3StatusUp(int op, int N){ |
90 | wsdStatInit; |
91 | assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); |
92 | assert( op>=0 && op<ArraySize(statMutex) ); |
93 | assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() |
94 | : sqlite3MallocMutex()) ); |
95 | wsdStat.nowValue[op] += N; |
96 | if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){ |
97 | wsdStat.mxValue[op] = wsdStat.nowValue[op]; |
98 | } |
99 | } |
100 | void sqlite3StatusDown(int op, int N){ |
101 | wsdStatInit; |
102 | assert( N>=0 ); |
103 | assert( op>=0 && op<ArraySize(statMutex) ); |
104 | assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() |
105 | : sqlite3MallocMutex()) ); |
106 | assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); |
107 | wsdStat.nowValue[op] -= N; |
108 | } |
109 | |
110 | /* |
111 | ** Adjust the highwater mark if necessary. |
112 | ** The caller must hold the appropriate mutex. |
113 | */ |
114 | void sqlite3StatusHighwater(int op, int X){ |
115 | sqlite3StatValueType newValue; |
116 | wsdStatInit; |
117 | assert( X>=0 ); |
118 | newValue = (sqlite3StatValueType)X; |
119 | assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); |
120 | assert( op>=0 && op<ArraySize(statMutex) ); |
121 | assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() |
122 | : sqlite3MallocMutex()) ); |
123 | assert( op==SQLITE_STATUS_MALLOC_SIZE |
124 | || op==SQLITE_STATUS_PAGECACHE_SIZE |
125 | || op==SQLITE_STATUS_PARSER_STACK ); |
126 | if( newValue>wsdStat.mxValue[op] ){ |
127 | wsdStat.mxValue[op] = newValue; |
128 | } |
129 | } |
130 | |
131 | /* |
132 | ** Query status information. |
133 | */ |
134 | int sqlite3_status64( |
135 | int op, |
136 | sqlite3_int64 *pCurrent, |
137 | sqlite3_int64 *pHighwater, |
138 | int resetFlag |
139 | ){ |
140 | sqlite3_mutex *pMutex; |
141 | wsdStatInit; |
142 | if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ |
143 | return SQLITE_MISUSE_BKPT; |
144 | } |
145 | #ifdef SQLITE_ENABLE_API_ARMOR |
146 | if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; |
147 | #endif |
148 | pMutex = statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex(); |
149 | sqlite3_mutex_enter(pMutex); |
150 | *pCurrent = wsdStat.nowValue[op]; |
151 | *pHighwater = wsdStat.mxValue[op]; |
152 | if( resetFlag ){ |
153 | wsdStat.mxValue[op] = wsdStat.nowValue[op]; |
154 | } |
155 | sqlite3_mutex_leave(pMutex); |
156 | (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */ |
157 | return SQLITE_OK; |
158 | } |
159 | int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ |
160 | sqlite3_int64 iCur = 0, iHwtr = 0; |
161 | int rc; |
162 | #ifdef SQLITE_ENABLE_API_ARMOR |
163 | if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; |
164 | #endif |
165 | rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag); |
166 | if( rc==0 ){ |
167 | *pCurrent = (int)iCur; |
168 | *pHighwater = (int)iHwtr; |
169 | } |
170 | return rc; |
171 | } |
172 | |
173 | /* |
174 | ** Return the number of LookasideSlot elements on the linked list |
175 | */ |
176 | static u32 countLookasideSlots(LookasideSlot *p){ |
177 | u32 cnt = 0; |
178 | while( p ){ |
179 | p = p->pNext; |
180 | cnt++; |
181 | } |
182 | return cnt; |
183 | } |
184 | |
185 | /* |
186 | ** Count the number of slots of lookaside memory that are outstanding |
187 | */ |
188 | int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){ |
189 | u32 nInit = countLookasideSlots(db->lookaside.pInit); |
190 | u32 nFree = countLookasideSlots(db->lookaside.pFree); |
191 | #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE |
192 | nInit += countLookasideSlots(db->lookaside.pSmallInit); |
193 | nFree += countLookasideSlots(db->lookaside.pSmallFree); |
194 | #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ |
195 | if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; |
196 | return db->lookaside.nSlot - (nInit+nFree); |
197 | } |
198 | |
199 | /* |
200 | ** Query status information for a single database connection |
201 | */ |
202 | int sqlite3_db_status( |
203 | sqlite3 *db, /* The database connection whose status is desired */ |
204 | int op, /* Status verb */ |
205 | int *pCurrent, /* Write current value here */ |
206 | int *pHighwater, /* Write high-water mark here */ |
207 | int resetFlag /* Reset high-water mark if true */ |
208 | ){ |
209 | int rc = SQLITE_OK; /* Return code */ |
210 | #ifdef SQLITE_ENABLE_API_ARMOR |
211 | if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){ |
212 | return SQLITE_MISUSE_BKPT; |
213 | } |
214 | #endif |
215 | sqlite3_mutex_enter(db->mutex); |
216 | switch( op ){ |
217 | case SQLITE_DBSTATUS_LOOKASIDE_USED: { |
218 | *pCurrent = sqlite3LookasideUsed(db, pHighwater); |
219 | if( resetFlag ){ |
220 | LookasideSlot *p = db->lookaside.pFree; |
221 | if( p ){ |
222 | while( p->pNext ) p = p->pNext; |
223 | p->pNext = db->lookaside.pInit; |
224 | db->lookaside.pInit = db->lookaside.pFree; |
225 | db->lookaside.pFree = 0; |
226 | } |
227 | #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE |
228 | p = db->lookaside.pSmallFree; |
229 | if( p ){ |
230 | while( p->pNext ) p = p->pNext; |
231 | p->pNext = db->lookaside.pSmallInit; |
232 | db->lookaside.pSmallInit = db->lookaside.pSmallFree; |
233 | db->lookaside.pSmallFree = 0; |
234 | } |
235 | #endif |
236 | } |
237 | break; |
238 | } |
239 | |
240 | case SQLITE_DBSTATUS_LOOKASIDE_HIT: |
241 | case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: |
242 | case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { |
243 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT ); |
244 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); |
245 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); |
246 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); |
247 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); |
248 | *pCurrent = 0; |
249 | *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; |
250 | if( resetFlag ){ |
251 | db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; |
252 | } |
253 | break; |
254 | } |
255 | |
256 | /* |
257 | ** Return an approximation for the amount of memory currently used |
258 | ** by all pagers associated with the given database connection. The |
259 | ** highwater mark is meaningless and is returned as zero. |
260 | */ |
261 | case SQLITE_DBSTATUS_CACHE_USED_SHARED: |
262 | case SQLITE_DBSTATUS_CACHE_USED: { |
263 | int totalUsed = 0; |
264 | int i; |
265 | sqlite3BtreeEnterAll(db); |
266 | for(i=0; i<db->nDb; i++){ |
267 | Btree *pBt = db->aDb[i].pBt; |
268 | if( pBt ){ |
269 | Pager * = sqlite3BtreePager(pBt); |
270 | int nByte = sqlite3PagerMemUsed(pPager); |
271 | if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){ |
272 | nByte = nByte / sqlite3BtreeConnectionCount(pBt); |
273 | } |
274 | totalUsed += nByte; |
275 | } |
276 | } |
277 | sqlite3BtreeLeaveAll(db); |
278 | *pCurrent = totalUsed; |
279 | *pHighwater = 0; |
280 | break; |
281 | } |
282 | |
283 | /* |
284 | ** *pCurrent gets an accurate estimate of the amount of memory used |
285 | ** to store the schema for all databases (main, temp, and any ATTACHed |
286 | ** databases. *pHighwater is set to zero. |
287 | */ |
288 | case SQLITE_DBSTATUS_SCHEMA_USED: { |
289 | int i; /* Used to iterate through schemas */ |
290 | int nByte = 0; /* Used to accumulate return value */ |
291 | |
292 | sqlite3BtreeEnterAll(db); |
293 | db->pnBytesFreed = &nByte; |
294 | assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); |
295 | db->lookaside.pEnd = db->lookaside.pStart; |
296 | for(i=0; i<db->nDb; i++){ |
297 | Schema *pSchema = db->aDb[i].pSchema; |
298 | if( ALWAYS(pSchema!=0) ){ |
299 | HashElem *p; |
300 | |
301 | nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * ( |
302 | pSchema->tblHash.count |
303 | + pSchema->trigHash.count |
304 | + pSchema->idxHash.count |
305 | + pSchema->fkeyHash.count |
306 | ); |
307 | nByte += sqlite3_msize(pSchema->tblHash.ht); |
308 | nByte += sqlite3_msize(pSchema->trigHash.ht); |
309 | nByte += sqlite3_msize(pSchema->idxHash.ht); |
310 | nByte += sqlite3_msize(pSchema->fkeyHash.ht); |
311 | |
312 | for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ |
313 | sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); |
314 | } |
315 | for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ |
316 | sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); |
317 | } |
318 | } |
319 | } |
320 | db->pnBytesFreed = 0; |
321 | db->lookaside.pEnd = db->lookaside.pTrueEnd; |
322 | sqlite3BtreeLeaveAll(db); |
323 | |
324 | *pHighwater = 0; |
325 | *pCurrent = nByte; |
326 | break; |
327 | } |
328 | |
329 | /* |
330 | ** *pCurrent gets an accurate estimate of the amount of memory used |
331 | ** to store all prepared statements. |
332 | ** *pHighwater is set to zero. |
333 | */ |
334 | case SQLITE_DBSTATUS_STMT_USED: { |
335 | struct Vdbe *pVdbe; /* Used to iterate through VMs */ |
336 | int nByte = 0; /* Used to accumulate return value */ |
337 | |
338 | db->pnBytesFreed = &nByte; |
339 | assert( db->lookaside.pEnd==db->lookaside.pTrueEnd ); |
340 | db->lookaside.pEnd = db->lookaside.pStart; |
341 | for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){ |
342 | sqlite3VdbeDelete(pVdbe); |
343 | } |
344 | db->lookaside.pEnd = db->lookaside.pTrueEnd; |
345 | db->pnBytesFreed = 0; |
346 | |
347 | *pHighwater = 0; /* IMP: R-64479-57858 */ |
348 | *pCurrent = nByte; |
349 | |
350 | break; |
351 | } |
352 | |
353 | /* |
354 | ** Set *pCurrent to the total cache hits or misses encountered by all |
355 | ** pagers the database handle is connected to. *pHighwater is always set |
356 | ** to zero. |
357 | */ |
358 | case SQLITE_DBSTATUS_CACHE_SPILL: |
359 | op = SQLITE_DBSTATUS_CACHE_WRITE+1; |
360 | /* no break */ deliberate_fall_through |
361 | case SQLITE_DBSTATUS_CACHE_HIT: |
362 | case SQLITE_DBSTATUS_CACHE_MISS: |
363 | case SQLITE_DBSTATUS_CACHE_WRITE:{ |
364 | int i; |
365 | int nRet = 0; |
366 | assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); |
367 | assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); |
368 | |
369 | for(i=0; i<db->nDb; i++){ |
370 | if( db->aDb[i].pBt ){ |
371 | Pager * = sqlite3BtreePager(db->aDb[i].pBt); |
372 | sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); |
373 | } |
374 | } |
375 | *pHighwater = 0; /* IMP: R-42420-56072 */ |
376 | /* IMP: R-54100-20147 */ |
377 | /* IMP: R-29431-39229 */ |
378 | *pCurrent = nRet; |
379 | break; |
380 | } |
381 | |
382 | /* Set *pCurrent to non-zero if there are unresolved deferred foreign |
383 | ** key constraints. Set *pCurrent to zero if all foreign key constraints |
384 | ** have been satisfied. The *pHighwater is always set to zero. |
385 | */ |
386 | case SQLITE_DBSTATUS_DEFERRED_FKS: { |
387 | *pHighwater = 0; /* IMP: R-11967-56545 */ |
388 | *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0; |
389 | break; |
390 | } |
391 | |
392 | default: { |
393 | rc = SQLITE_ERROR; |
394 | } |
395 | } |
396 | sqlite3_mutex_leave(db->mutex); |
397 | return rc; |
398 | } |
399 | |