| Adds a new API function sqlite3_preload(). This fills the page cache |
| with the first pages of the database. |
| |
| Index: src/build.c |
| =================================================================== |
| --- src/build.c 2009-09-11 07:02:46.000000000 -0700 |
| +++ src/build.c 2009-09-14 18:16:46.000000000 -0700 |
| @@ -26,6 +26,9 @@ |
| */ |
| #include "sqliteInt.h" |
| |
| +#include "pager.h" |
| +#include "btree.h" |
| + |
| /* |
| ** This routine is called when a new SQL statement is beginning to |
| ** be parsed. Initialize the pParse structure as needed. |
| @@ -3659,3 +3662,30 @@ |
| } |
| return pKey; |
| } |
| + |
| +/* Begin preload-cache.patch for Chromium */ |
| +/* See declaration in sqlite3.h for information */ |
| +int sqlite3_preload(sqlite3 *db) |
| +{ |
| + Pager *pPager; |
| + Btree *pBt; |
| + int rc; |
| + int i; |
| + int dbsLoaded = 0; |
| + |
| + for(i=0; i<db->nDb; i++) { |
| + pBt = db->aDb[i].pBt; |
| + if( !pBt ) |
| + continue; |
| + pPager = sqlite3BtreePager(pBt); |
| + if( pPager ) { |
| + rc = sqlite3PagerLoadall(pPager); |
| + if (rc == SQLITE_OK) |
| + dbsLoaded++; |
| + } |
| + } |
| + if (dbsLoaded == 0) |
| + return SQLITE_ERROR; |
| + return SQLITE_OK; |
| +} |
| +/* End preload-cache.patch for Chromium */ |
| Index: src/sqlite3.h.in |
| =================================================================== |
| --- src/sqlite.h.in 2009-09-09 07:03:20.000000000 -0700 |
| +++ src/sqlite.h.in 2009-09-15 11:34:26.000000000 -0700 |
| @@ -4677,6 +4677,21 @@ |
| */ |
| int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); |
| |
| +/* Begin preload-cache.patch for Chromium */ |
| +/* |
| +** Preload the databases into the pager cache, up to the maximum size of the |
| +** pager cache. |
| +** |
| +** For a database to be loaded successfully, the pager must be active. That is, |
| +** there must be an open statement on that database. See sqlite3pager_loadall |
| +** |
| +** There might be many databases attached to the given connection. We iterate |
| +** them all and try to load them. If none are loadable successfully, we return |
| +** an error. Otherwise, we return OK. |
| +*/ |
| +int sqlite3_preload(sqlite3 *db); |
| +/* End preload-cache.patch for Chromium */ |
| + |
| /* |
| ** CAPI3REF: Virtual File System Objects {H11200} <S20100> |
| ** |
| Index: src/pager.c |
| =================================================================== |
| --- src/pager.c 2009-09-07 08:58:09.000000000 -0700 |
| +++ src/pager.c 2009-09-15 16:43:07.000000000 -0700 |
| @@ -388,6 +388,16 @@ |
| */ |
| #define PAGER_MAX_PGNO 2147483647 |
| |
| +/* Begin preload-cache.patch for Chromium */ |
| +/* See comments above the definition. */ |
| +int sqlite3PagerAcquire2( |
| + Pager *pPager, |
| + Pgno pgno, |
| + DbPage **ppPage, |
| + int noContent, |
| + unsigned char *pDataToFill); |
| +/* End preload-cache.patch for Chromium */ |
| + |
| #ifndef NDEBUG |
| /* |
| ** Usage: |
| @@ -3788,6 +3798,25 @@ |
| DbPage **ppPage, /* Write a pointer to the page here */ |
| int noContent /* Do not bother reading content from disk if true */ |
| ){ |
| + /* This just passes through to our modified version with NULL data. */ |
| + return sqlite3PagerAcquire2(pPager, pgno, ppPage, noContent, 0); |
| +} |
| + |
| +/* |
| +** This is an internal version of sqlite3PagerAcquire that takes an extra |
| +** parameter of data to use to fill the page with. This allows more efficient |
| +** filling for preloaded data. If this extra parameter is NULL, we'll go to |
| +** the file. |
| +** |
| +** See sqlite3PagerLoadall which uses this function. |
| +*/ |
| +int sqlite3PagerAcquire2( |
| + Pager *pPager, /* The pager open on the database file */ |
| + Pgno pgno, /* Page number to fetch */ |
| + DbPage **ppPage, /* Write a pointer to the page here */ |
| + int noContent, /* Do not bother reading content from disk if true */ |
| + unsigned char* pDataToFill |
| +){ |
| int rc; |
| PgHdr *pPg; |
| |
| @@ -3870,9 +3899,17 @@ |
| IOTRACE(("ZERO %p %d\n", pPager, pgno)); |
| }else{ |
| assert( pPg->pPager==pPager ); |
| - rc = readDbPage(pPg); |
| - if( rc!=SQLITE_OK ){ |
| - goto pager_acquire_err; |
| + if( pDataToFill ){ |
| + /* Just copy from the given memory */ |
| + memcpy(pPg->pData, pDataToFill, pPager->pageSize); |
| + CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM; |
| + goto pager_acquire_err); |
| + }else{ |
| + /* Load from disk (old regular sqlite code path) */ |
| + rc = readDbPage(pPg); |
| + if( rc!=SQLITE_OK ){ |
| + goto pager_acquire_err; |
| + } |
| } |
| } |
| #ifdef SQLITE_CHECK_PAGES |
| @@ -5221,6 +5258,91 @@ |
| } |
| #endif |
| |
| +/* Begin preload-cache.patch for Chromium */ |
| +/** |
| +** When making large allocations, there is no need to stress the heap and |
| +** potentially hold its lock while we allocate a bunch of memory. If we know |
| +** the allocation will be large, go directly to the OS instead of the heap. |
| +**/ |
| +static void* allocLarge(size_t size) { |
| +#if SQLITE_OS_WIN |
| + return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
| +#else |
| + return sqlite3Malloc(size); |
| +#endif |
| +} |
| + |
| +static void freeLarge(void* ptr) { |
| +#if SQLITE_OS_WIN |
| + VirtualFree(ptr, 0, MEM_RELEASE); |
| +#else |
| + sqlite3_free(ptr); |
| +#endif |
| +} |
| + |
| +/** |
| +** Addition: This will attempt to populate the database cache with |
| +** the first N bytes of the file, where N is the total size of the cache. |
| +** Because we can load this as one chunk from the disk, this is much faster |
| +** than loading a subset of the pages one at a time in random order. |
| +** |
| +** The pager must be initialized before this function is called. This means a |
| +* statement must be open that has initialized the pager and is keeping the |
| +** cache in memory. |
| +**/ |
| +int sqlite3PagerLoadall(Pager* pPager) |
| +{ |
| + int i; |
| + int rc; |
| + int nMax; |
| + int loadSize; |
| + int loadPages; |
| + unsigned char *fileData; |
| + |
| + if (pPager->dbSize < 0 || pPager->pageSize < 0) { |
| + /* pager not initialized, this means a statement is not open */ |
| + return SQLITE_MISUSE; |
| + } |
| + |
| + /* compute sizes */ |
| + nMax = sqlite3PcacheGetCachesize(pPager->pPCache); |
| + if (nMax < pPager->dbSize) |
| + loadPages = nMax; |
| + else |
| + loadPages = pPager->dbSize; |
| + loadSize = loadPages * pPager->pageSize; |
| + |
| + /* load the file as one chunk */ |
| + fileData = allocLarge(loadSize); |
| + if (! fileData) |
| + return SQLITE_NOMEM; |
| + rc = sqlite3OsRead(pPager->fd, fileData, loadSize, 0); |
| + if (rc != SQLITE_OK) { |
| + freeLarge(fileData); |
| + return rc; |
| + } |
| + |
| + /* Copy the data to each page. Note that the page numbers we pass to _get |
| + * are one-based, 0 is a marker for no page. We also need to check that we |
| + * haven't loaded more pages than the cache can hold total. There may have |
| + * already been a few pages loaded before, so we may fill the cache before |
| + * loading all of the pages we want to. |
| + */ |
| + for(i=1; |
| + i <= loadPages && sqlite3PcachePagecount(pPager->pPCache) < nMax; |
| + i++) { |
| + DbPage *pPage = 0; |
| + rc = sqlite3PagerAcquire2(pPager, i, &pPage, 0, |
| + &fileData[(i-1)*(i64)pPager->pageSize]); |
| + if (rc != SQLITE_OK) |
| + break; |
| + sqlite3PagerUnref(pPage); |
| + } |
| + freeLarge(fileData); |
| + return SQLITE_OK; |
| +} |
| +/* End preload-cache.patch for Chromium */ |
| + |
| /* |
| ** Return a pointer to the data for the specified page. |
| */ |
| Index: src/pager.h |
| =================================================================== |
| --- src/pager.h 2009-09-04 13:37:42.000000000 -0700 |
| +++ src/pager.h 2009-09-15 11:31:55.000000000 -0700 |
| @@ -143,6 +143,8 @@ |
| sqlite3_file *sqlite3PagerFile(Pager*); |
| const char *sqlite3PagerJournalname(Pager*); |
| int sqlite3PagerNosync(Pager*); |
| +/* This function is for preload-cache.patch for Chromium: */ |
| +int sqlite3PagerLoadall(Pager*); |
| void *sqlite3PagerTempSpace(Pager*); |
| int sqlite3PagerIsMemdb(Pager*); |
| |
| Index: src/pcache.c |
| =================================================================== |
| --- src/pcache.c 2009-09-04 13:37:42.000000000 -0700 |
| +++ src/pcache.c 2009-09-15 16:41:55.000000000 -0700 |
| @@ -542,14 +542,12 @@ |
| return nPage; |
| } |
| |
| -#ifdef SQLITE_TEST |
| /* |
| ** Get the suggested cache-size value. |
| */ |
| int sqlite3PcacheGetCachesize(PCache *pCache){ |
| return pCache->nMax; |
| } |
| -#endif |
| |
| /* |
| ** Set the suggested cache-size value. |
| Index: src/pcache.h |
| =================================================================== |
| --- src/pcache.h 2009-09-04 13:37:42.000000000 -0700 |
| +++ src/pcache.h 2009-09-15 16:41:52.000000000 -0700 |
| @@ -139,9 +139,7 @@ |
| ** of the suggested cache-sizes. |
| */ |
| void sqlite3PcacheSetCachesize(PCache *, int); |
| -#ifdef SQLITE_TEST |
| int sqlite3PcacheGetCachesize(PCache *); |
| -#endif |
| |
| #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| /* Try to return memory used by the pcache module to the main memory heap */ |