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; inDb; 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} ** 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 */