wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Diagnostics;
3 using System.Text;
4  
5 using u32 = System.UInt32;
6 using Pgno = System.UInt32;
7  
8 namespace Community.CsharpSqlite
9 {
10 using sqlite3_value = Sqlite3.Mem;
11 using sqlite3_pcache = Sqlite3.PCache1;
12  
13 public partial class Sqlite3
14 {
15 /*
16 ** 2008 August 05
17 **
18 ** The author disclaims copyright to this source code. In place of
19 ** a legal notice, here is a blessing:
20 **
21 ** May you do good and not evil.
22 ** May you find forgiveness for yourself and forgive others.
23 ** May you share freely, never taking more than you give.
24 **
25 *************************************************************************
26 ** This file implements that page cache.
27 *************************************************************************
28 ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
29 ** C#-SQLite is an independent reimplementation of the SQLite software library
30 **
31 ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
32 **
33 *************************************************************************
34 */
35 //#include "sqliteInt.h"
36  
37 /*
38 ** A complete page cache is an instance of this structure.
39 */
40 public class PCache
41 {
42 public PgHdr pDirty, pDirtyTail; /* List of dirty pages in LRU order */
43 public PgHdr pSynced; /* Last synced page in dirty page list */
44 public int _nRef; /* Number of referenced pages */
45 public int nMax; /* Configured cache size */
46 public int szPage; /* Size of every page in this cache */
47 public int szExtra; /* Size of extra space for each page */
48 public bool bPurgeable; /* True if pages are on backing store */
49 public dxStress xStress; //int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
50 public object pStress; /* Argument to xStress */
51 public sqlite3_pcache pCache; /* Pluggable cache module */
52 public PgHdr pPage1; /* Reference to page 1 */
53  
54 public int nRef /* Number of referenced pages */
55 {
56 get
57 {
58 return _nRef;
59 }
60 set
61 {
62 _nRef = value;
63 }
64 }
65  
66 public void Clear()
67 {
68 pDirty = null;
69 pDirtyTail = null;
70 pSynced = null;
71 nRef = 0;
72 }
73 };
74  
75 /*
76 ** Some of the Debug.Assert() macros in this code are too expensive to run
77 ** even during normal debugging. Use them only rarely on long-running
78 ** tests. Enable the expensive asserts using the
79 ** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
80 */
81 #if SQLITE_ENABLE_EXPENSIVE_ASSERT
82 //# define expensive_assert(X) Debug.Assert(X)
83 static void expensive_assert( bool x ) { Debug.Assert( x ); }
84 #else
85 //# define expensive_assert(X)
86 #endif
87  
88 /********************************** Linked List Management ********************/
89  
90 #if !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT
91 /*
92 ** Check that the pCache.pSynced variable is set correctly. If it
93 ** is not, either fail an Debug.Assert or return zero. Otherwise, return
94 ** non-zero. This is only used in debugging builds, as follows:
95 **
96 ** expensive_assert( pcacheCheckSynced(pCache) );
97 */
98 static int pcacheCheckSynced(PCache pCache){
99 PgHdr p ;
100 for(p=pCache.pDirtyTail; p!=pCache.pSynced; p=p.pDirtyPrev){
101 Debug.Assert( p.nRef !=0|| (p.flags&PGHDR_NEED_SYNC) !=0);
102 }
103 return (p==null || p.nRef!=0 || (p.flags&PGHDR_NEED_SYNC)==0)?1:0;
104 }
105 #endif //* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
106  
107 /*
108 ** Remove page pPage from the list of dirty pages.
109 */
110 static void pcacheRemoveFromDirtyList( PgHdr pPage )
111 {
112 PCache p = pPage.pCache;
113  
114 Debug.Assert( pPage.pDirtyNext != null || pPage == p.pDirtyTail );
115 Debug.Assert( pPage.pDirtyPrev != null || pPage == p.pDirty );
116  
117 /* Update the PCache1.pSynced variable if necessary. */
118 if ( p.pSynced == pPage )
119 {
120 PgHdr pSynced = pPage.pDirtyPrev;
121 while ( pSynced != null && ( pSynced.flags & PGHDR_NEED_SYNC ) != 0 )
122 {
123 pSynced = pSynced.pDirtyPrev;
124 }
125 p.pSynced = pSynced;
126 }
127  
128 if ( pPage.pDirtyNext != null )
129 {
130 pPage.pDirtyNext.pDirtyPrev = pPage.pDirtyPrev;
131 }
132 else
133 {
134 Debug.Assert( pPage == p.pDirtyTail );
135 p.pDirtyTail = pPage.pDirtyPrev;
136 }
137 if ( pPage.pDirtyPrev != null )
138 {
139 pPage.pDirtyPrev.pDirtyNext = pPage.pDirtyNext;
140 }
141 else
142 {
143 Debug.Assert( pPage == p.pDirty );
144 p.pDirty = pPage.pDirtyNext;
145 }
146 pPage.pDirtyNext = null;
147 pPage.pDirtyPrev = null;
148  
149 #if SQLITE_ENABLE_EXPENSIVE_ASSERT
150 expensive_assert( pcacheCheckSynced(p) );
151 #endif
152 }
153  
154 /*
155 ** Add page pPage to the head of the dirty list (PCache1.pDirty is set to
156 ** pPage).
157 */
158 static void pcacheAddToDirtyList( PgHdr pPage )
159 {
160 PCache p = pPage.pCache;
161  
162 Debug.Assert( pPage.pDirtyNext == null && pPage.pDirtyPrev == null && p.pDirty != pPage );
163  
164 pPage.pDirtyNext = p.pDirty;
165 if ( pPage.pDirtyNext != null )
166 {
167 Debug.Assert( pPage.pDirtyNext.pDirtyPrev == null );
168 pPage.pDirtyNext.pDirtyPrev = pPage;
169 }
170 p.pDirty = pPage;
171 if ( null == p.pDirtyTail )
172 {
173 p.pDirtyTail = pPage;
174 }
175 if ( null == p.pSynced && 0 == ( pPage.flags & PGHDR_NEED_SYNC ) )
176 {
177 p.pSynced = pPage;
178 }
179 #if SQLITE_ENABLE_EXPENSIVE_ASSERT
180 expensive_assert( pcacheCheckSynced(p) );
181 #endif
182 }
183  
184 /*
185 ** Wrapper around the pluggable caches xUnpin method. If the cache is
186 ** being used for an in-memory database, this function is a no-op.
187 */
188 static void pcacheUnpin( PgHdr p )
189 {
190 PCache pCache = p.pCache;
191 if ( pCache.bPurgeable )
192 {
193 if ( p.pgno == 1 )
194 {
195 pCache.pPage1 = null;
196 }
197 sqlite3GlobalConfig.pcache.xUnpin( pCache.pCache, p, false );
198 }
199 }
200  
201 /*************************************************** General Interfaces ******
202 **
203 ** Initialize and shutdown the page cache subsystem. Neither of these
204 ** functions are threadsafe.
205 */
206 static int sqlite3PcacheInitialize()
207 {
208 if ( sqlite3GlobalConfig.pcache.xInit == null )
209 {
210 /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
211 ** built-in default page cache is used instead of the application defined
212 ** page cache. */
213 sqlite3PCacheSetDefault();
214 }
215 return sqlite3GlobalConfig.pcache.xInit( sqlite3GlobalConfig.pcache.pArg );
216 }
217 static void sqlite3PcacheShutdown()
218 {
219 if ( sqlite3GlobalConfig.pcache.xShutdown != null )
220 {
221 /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
222 sqlite3GlobalConfig.pcache.xShutdown( sqlite3GlobalConfig.pcache.pArg );
223 }
224 }
225  
226 /*
227 ** Return the size in bytes of a PCache object.
228 */
229 static int sqlite3PcacheSize()
230 {
231 return 4;
232 }// sizeof( PCache ); }
233  
234 /*
235 ** Create a new PCache object. Storage space to hold the object
236 ** has already been allocated and is passed in as the p pointer.
237 ** The caller discovers how much space needs to be allocated by
238 ** calling sqlite3PcacheSize().
239 */
240 static void sqlite3PcacheOpen(
241 int szPage, /* Size of every page */
242 int szExtra, /* Extra space associated with each page */
243 bool bPurgeable, /* True if pages are on backing store */
244 dxStress xStress,//int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */
245 object pStress, /* Argument to xStress */
246 PCache p /* Preallocated space for the PCache */
247 )
248 {
249 p.Clear();//memset(p, 0, sizeof(PCache));
250 p.szPage = szPage;
251 p.szExtra = szExtra;
252 p.bPurgeable = bPurgeable;
253 p.xStress = xStress;
254 p.pStress = pStress;
255 p.nMax = 100;
256 }
257  
258 /*
259 ** Change the page size for PCache object. The caller must ensure that there
260 ** are no outstanding page references when this function is called.
261 */
262 static void sqlite3PcacheSetPageSize( PCache pCache, int szPage )
263 {
264 Debug.Assert( pCache.nRef == 0 && pCache.pDirty == null );
265 if ( pCache.pCache != null )
266 {
267 sqlite3GlobalConfig.pcache.xDestroy( ref pCache.pCache );
268 pCache.pCache = null;
269 }
270 pCache.szPage = szPage;
271 }
272  
273 /*
274 ** Try to obtain a page from the cache.
275 */
276 static int sqlite3PcacheFetch(
277 PCache pCache, /* Obtain the page from this cache */
278 u32 pgno, /* Page number to obtain */
279 int createFlag, /* If true, create page if it does not exist already */
280 ref PgHdr ppPage /* Write the page here */
281 )
282 {
283 PgHdr pPage = null;
284 int eCreate;
285  
286 Debug.Assert( pCache != null );
287 Debug.Assert( createFlag == 1 || createFlag == 0 );
288 Debug.Assert( pgno > 0 );
289  
290 /* If the pluggable cache (sqlite3_pcache*) has not been allocated,
291 ** allocate it now.
292 */
293 if ( null == pCache.pCache && createFlag != 0 )
294 {
295 sqlite3_pcache p;
296 int nByte;
297 nByte = pCache.szPage + pCache.szExtra + 0;// sizeof( PgHdr );
298 p = sqlite3GlobalConfig.pcache.xCreate( nByte, pCache.bPurgeable );
299 //if ( null == p )
300 //{
301 // return SQLITE_NOMEM;
302 //}
303 sqlite3GlobalConfig.pcache.xCachesize( p, pCache.nMax );
304 pCache.pCache = p;
305 }
306  
307 eCreate = createFlag * ( 1 + ( ( !pCache.bPurgeable || null == pCache.pDirty ) ? 1 : 0 ) );
308  
309 if ( pCache.pCache != null )
310 {
311 pPage = sqlite3GlobalConfig.pcache.xFetch( pCache.pCache, pgno, eCreate );
312 }
313  
314 if ( null == pPage && eCreate == 1 )
315 {
316 PgHdr pPg;
317  
318 /* Find a dirty page to write-out and recycle. First try to find a
319 ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
320 ** cleared), but if that is not possible settle for any other
321 ** unreferenced dirty page.
322 */
323 #if SQLITE_ENABLE_EXPENSIVE_ASSERT
324 expensive_assert( pcacheCheckSynced(pCache) );
325 #endif
326 for ( pPg = pCache.pSynced;
327 pPg != null && ( pPg.nRef != 0 || ( pPg.flags & PGHDR_NEED_SYNC ) != 0 );
328 pPg = pPg.pDirtyPrev
329 )
330 ;
331 pCache.pSynced = pPg;
332 if ( null == pPg )
333 {
334 for ( pPg = pCache.pDirtyTail; pPg != null && pPg.nRef != 0; pPg = pPg.pDirtyPrev )
335 ;
336 }
337 if ( pPg != null )
338 {
339 int rc;
340 #if SQLITE_LOG_CACHE_SPILL
341 sqlite3_log(SQLITE_FULL,
342 "spill page %d making room for %d - cache used: %d/%d",
343 pPg->pgno, pgno,
344 sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
345 pCache->nMax);
346 #endif
347 rc = pCache.xStress( pCache.pStress, pPg );
348 if ( rc != SQLITE_OK && rc != SQLITE_BUSY )
349 {
350 return rc;
351 }
352 }
353  
354 pPage = sqlite3GlobalConfig.pcache.xFetch( pCache.pCache, pgno, 2 );
355 }
356  
357 if ( pPage != null )
358 {
359 if ( null == pPage.pData )
360 {
361 // memset(pPage, 0, sizeof(PgHdr));
362 pPage.pData = sqlite3Malloc( pCache.szPage );// pPage->pData = (void*)&pPage[1];
363 //pPage->pExtra = (void*)&((char*)pPage->pData)[pCache->szPage];
364 //memset(pPage->pExtra, 0, pCache->szExtra);
365 pPage.pCache = pCache;
366 pPage.pgno = pgno;
367 }
368 Debug.Assert( pPage.pCache == pCache );
369 Debug.Assert( pPage.pgno == pgno );
370 //assert(pPage->pData == (void*)&pPage[1]);
371 //assert(pPage->pExtra == (void*)&((char*)&pPage[1])[pCache->szPage]);
372 if ( 0 == pPage.nRef )
373 {
374 pCache.nRef++;
375 }
376 pPage.nRef++;
377 if ( pgno == 1 )
378 {
379 pCache.pPage1 = pPage;
380 }
381 }
382 ppPage = pPage;
383 return ( pPage == null && eCreate != 0 ) ? SQLITE_NOMEM : SQLITE_OK;
384 }
385  
386 /*
387 ** Decrement the reference count on a page. If the page is clean and the
388 ** reference count drops to 0, then it is made elible for recycling.
389 */
390 static void sqlite3PcacheRelease( PgHdr p )
391 {
392 Debug.Assert( p.nRef > 0 );
393 p.nRef--;
394 if ( p.nRef == 0 )
395 {
396 PCache pCache = p.pCache;
397 pCache.nRef--;
398 if ( ( p.flags & PGHDR_DIRTY ) == 0 )
399 {
400 pcacheUnpin( p );
401 }
402 else
403 {
404 /* Move the page to the head of the dirty list. */
405 pcacheRemoveFromDirtyList( p );
406 pcacheAddToDirtyList( p );
407 }
408 }
409 }
410  
411 /*
412 ** Increase the reference count of a supplied page by 1.
413 */
414 static void sqlite3PcacheRef( PgHdr p )
415 {
416 Debug.Assert( p.nRef > 0 );
417 p.nRef++;
418 }
419  
420 /*
421 ** Drop a page from the cache. There must be exactly one reference to the
422 ** page. This function deletes that reference, so after it returns the
423 ** page pointed to by p is invalid.
424 */
425 static void sqlite3PcacheDrop( PgHdr p )
426 {
427 PCache pCache;
428 Debug.Assert( p.nRef == 1 );
429 if ( ( p.flags & PGHDR_DIRTY ) != 0 )
430 {
431 pcacheRemoveFromDirtyList( p );
432 }
433 pCache = p.pCache;
434 pCache.nRef--;
435 if ( p.pgno == 1 )
436 {
437 pCache.pPage1 = null;
438 }
439 sqlite3GlobalConfig.pcache.xUnpin( pCache.pCache, p, true );
440 }
441  
442 /*
443 ** Make sure the page is marked as dirty. If it isn't dirty already,
444 ** make it so.
445 */
446 static void sqlite3PcacheMakeDirty( PgHdr p )
447 {
448 p.flags &= ~PGHDR_DONT_WRITE;
449 Debug.Assert( p.nRef > 0 );
450 if ( 0 == ( p.flags & PGHDR_DIRTY ) )
451 {
452 p.flags |= PGHDR_DIRTY;
453 pcacheAddToDirtyList( p );
454 }
455 }
456  
457 /*
458 ** Make sure the page is marked as clean. If it isn't clean already,
459 ** make it so.
460 */
461 static void sqlite3PcacheMakeClean( PgHdr p )
462 {
463 if ( ( p.flags & PGHDR_DIRTY ) != 0 )
464 {
465 pcacheRemoveFromDirtyList( p );
466 p.flags &= ~( PGHDR_DIRTY | PGHDR_NEED_SYNC );
467 if ( p.nRef == 0 )
468 {
469 pcacheUnpin( p );
470 }
471 }
472 }
473  
474 /*
475 ** Make every page in the cache clean.
476 */
477 static void sqlite3PcacheCleanAll( PCache pCache )
478 {
479 PgHdr p;
480 while ( ( p = pCache.pDirty ) != null )
481 {
482 sqlite3PcacheMakeClean( p );
483 }
484 }
485  
486 /*
487 ** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
488 */
489 static void sqlite3PcacheClearSyncFlags( PCache pCache )
490 {
491 PgHdr p;
492 for ( p = pCache.pDirty; p != null; p = p.pDirtyNext )
493 {
494 p.flags &= ~PGHDR_NEED_SYNC;
495 }
496 pCache.pSynced = pCache.pDirtyTail;
497 }
498  
499 /*
500 ** Change the page number of page p to newPgno.
501 */
502 static void sqlite3PcacheMove( PgHdr p, Pgno newPgno )
503 {
504 PCache pCache = p.pCache;
505 Debug.Assert( p.nRef > 0 );
506 Debug.Assert( newPgno > 0 );
507 sqlite3GlobalConfig.pcache.xRekey( pCache.pCache, p, p.pgno, newPgno );
508 p.pgno = newPgno;
509 if ( ( p.flags & PGHDR_DIRTY ) != 0 && ( p.flags & PGHDR_NEED_SYNC ) != 0 )
510 {
511 pcacheRemoveFromDirtyList( p );
512 pcacheAddToDirtyList( p );
513 }
514 }
515  
516 /*
517 ** Drop every cache entry whose page number is greater than "pgno". The
518 ** caller must ensure that there are no outstanding references to any pages
519 ** other than page 1 with a page number greater than pgno.
520 **
521 ** If there is a reference to page 1 and the pgno parameter passed to this
522 ** function is 0, then the data area associated with page 1 is zeroed, but
523 ** the page object is not dropped.
524 */
525 static void sqlite3PcacheTruncate( PCache pCache, u32 pgno )
526 {
527 if ( pCache.pCache != null )
528 {
529 PgHdr p;
530 PgHdr pNext;
531 for ( p = pCache.pDirty; p != null; p = pNext )
532 {
533 pNext = p.pDirtyNext;
534 /* This routine never gets call with a positive pgno except right
535 ** after sqlite3PcacheCleanAll(). So if there are dirty pages,
536 ** it must be that pgno==0.
537 */
538 Debug.Assert( p.pgno > 0 );
539 if ( ALWAYS( p.pgno > pgno ) )
540 {
541 Debug.Assert( ( p.flags & PGHDR_DIRTY ) != 0 );
542 sqlite3PcacheMakeClean( p );
543 }
544 }
545 if ( pgno == 0 && pCache.pPage1 != null )
546 {
547 // memset( pCache.pPage1.pData, 0, pCache.szPage );
548 pCache.pPage1.pData = sqlite3Malloc( pCache.szPage );
549 pgno = 1;
550 }
551 sqlite3GlobalConfig.pcache.xTruncate( pCache.pCache, pgno + 1 );
552 }
553 }
554  
555 /*
556 ** Close a cache.
557 */
558 static void sqlite3PcacheClose( PCache pCache )
559 {
560 if ( pCache.pCache != null )
561 {
562 sqlite3GlobalConfig.pcache.xDestroy( ref pCache.pCache );
563 }
564 }
565  
566 /*
567 ** Discard the contents of the cache.
568 */
569 static void sqlite3PcacheClear( PCache pCache )
570 {
571 sqlite3PcacheTruncate( pCache, 0 );
572 }
573  
574  
575 /*
576 ** Merge two lists of pages connected by pDirty and in pgno order.
577 ** Do not both fixing the pDirtyPrev pointers.
578 */
579 static PgHdr pcacheMergeDirtyList( PgHdr pA, PgHdr pB )
580 {
581 PgHdr result = new PgHdr();
582 PgHdr pTail = result;
583 while ( pA != null && pB != null )
584 {
585 if ( pA.pgno < pB.pgno )
586 {
587 pTail.pDirty = pA;
588 pTail = pA;
589 pA = pA.pDirty;
590 }
591 else
592 {
593 pTail.pDirty = pB;
594 pTail = pB;
595 pB = pB.pDirty;
596 }
597 }
598 if ( pA != null )
599 {
600 pTail.pDirty = pA;
601 }
602 else if ( pB != null )
603 {
604 pTail.pDirty = pB;
605 }
606 else
607 {
608 pTail.pDirty = null;
609 }
610 return result.pDirty;
611 }
612  
613 /*
614 ** Sort the list of pages in accending order by pgno. Pages are
615 ** connected by pDirty pointers. The pDirtyPrev pointers are
616 ** corrupted by this sort.
617 **
618 ** Since there cannot be more than 2^31 distinct pages in a database,
619 ** there cannot be more than 31 buckets required by the merge sorter.
620 ** One extra bucket is added to catch overflow in case something
621 ** ever changes to make the previous sentence incorrect.
622 */
623 //#define N_SORT_BUCKET 32
624 const int N_SORT_BUCKET = 32;
625  
626 static PgHdr pcacheSortDirtyList( PgHdr pIn )
627 {
628 PgHdr[] a;
629 PgHdr p;//a[N_SORT_BUCKET], p;
630 int i;
631 a = new PgHdr[N_SORT_BUCKET];//memset(a, 0, sizeof(a));
632 while ( pIn != null )
633 {
634 p = pIn;
635 pIn = p.pDirty;
636 p.pDirty = null;
637 for ( i = 0; ALWAYS( i < N_SORT_BUCKET - 1 ); i++ )
638 {
639 if ( a[i] == null )
640 {
641 a[i] = p;
642 break;
643 }
644 else
645 {
646 p = pcacheMergeDirtyList( a[i], p );
647 a[i] = null;
648 }
649 }
650 if ( NEVER( i == N_SORT_BUCKET - 1 ) )
651 {
652 /* To get here, there need to be 2^(N_SORT_BUCKET) elements in
653 ** the input list. But that is impossible.
654 */
655 a[i] = pcacheMergeDirtyList( a[i], p );
656 }
657 }
658 p = a[0];
659 for ( i = 1; i < N_SORT_BUCKET; i++ )
660 {
661 p = pcacheMergeDirtyList( p, a[i] );
662 }
663 return p;
664 }
665  
666 /*
667 ** Return a list of all dirty pages in the cache, sorted by page number.
668 */
669 static PgHdr sqlite3PcacheDirtyList( PCache pCache )
670 {
671 PgHdr p;
672 for ( p = pCache.pDirty; p != null; p = p.pDirtyNext )
673 {
674 p.pDirty = p.pDirtyNext;
675 }
676 return pcacheSortDirtyList( pCache.pDirty );
677 }
678  
679 /*
680 ** Return the total number of referenced pages held by the cache.
681 */
682 static int sqlite3PcacheRefCount( PCache pCache )
683 {
684 return pCache.nRef;
685 }
686  
687 /*
688 ** Return the number of references to the page supplied as an argument.
689 */
690 static int sqlite3PcachePageRefcount( PgHdr p )
691 {
692 return p.nRef;
693 }
694  
695 /*
696 ** Return the total number of pages in the cache.
697 */
698 static int sqlite3PcachePagecount( PCache pCache )
699 {
700 int nPage = 0;
701 if ( pCache.pCache != null )
702 {
703 nPage = sqlite3GlobalConfig.pcache.xPagecount( pCache.pCache );
704 }
705 return nPage;
706 }
707  
708 #if SQLITE_TEST
709 /*
710 ** Get the suggested cache-size value.
711 */
712 static int sqlite3PcacheGetCachesize( PCache pCache )
713 {
714 return pCache.nMax;
715 }
716 #endif
717  
718 /*
719 ** Set the suggested cache-size value.
720 */
721 static void sqlite3PcacheSetCachesize( PCache pCache, int mxPage )
722 {
723 pCache.nMax = mxPage;
724 if ( pCache.pCache != null )
725 {
726 sqlite3GlobalConfig.pcache.xCachesize( pCache.pCache, mxPage );
727 }
728 }
729  
730 #if SQLITE_CHECK_PAGES || (SQLITE_DEBUG)
731 /*
732 ** For all dirty pages currently in the cache, invoke the specified
733 ** callback. This is only used if the SQLITE_CHECK_PAGES macro is
734 ** defined.
735 */
736 static void sqlite3PcacheIterateDirty( PCache pCache, dxIter xIter )
737 {
738 PgHdr pDirty;
739 for ( pDirty = pCache.pDirty; pDirty != null; pDirty = pDirty.pDirtyNext )
740 {
741 xIter( pDirty );
742 }
743 }
744 #endif
745 }
746 }