mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Add an API to support custom page cache implementations. (CVS 5899)
FossilOrigin-Name: 47866d6708e9b69e367937fd85f93580fd025447
This commit is contained in:
@@ -167,8 +167,8 @@ OBJS0 = alter.lo analyze.lo attach.lo auth.lo bitvec.lo btmutex.lo \
|
|||||||
memjournal.lo \
|
memjournal.lo \
|
||||||
mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \
|
||||||
opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
|
opcodes.lo os.lo os_unix.lo os_win.lo os_os2.lo \
|
||||||
pager.lo parse.lo pcache.lo pragma.lo prepare.lo printf.lo random.lo \
|
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||||
resolve.lo select.lo status.lo \
|
random.lo resolve.lo select.lo status.lo \
|
||||||
table.lo tokenize.lo trigger.lo update.lo \
|
table.lo tokenize.lo trigger.lo update.lo \
|
||||||
util.lo vacuum.lo \
|
util.lo vacuum.lo \
|
||||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbefifo.lo vdbemem.lo \
|
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbefifo.lo vdbemem.lo \
|
||||||
@@ -237,6 +237,7 @@ SRC = \
|
|||||||
$(TOP)/src/parse.y \
|
$(TOP)/src/parse.y \
|
||||||
$(TOP)/src/pcache.c \
|
$(TOP)/src/pcache.c \
|
||||||
$(TOP)/src/pcache.h \
|
$(TOP)/src/pcache.h \
|
||||||
|
$(TOP)/src/pcache1.c \
|
||||||
$(TOP)/src/pragma.c \
|
$(TOP)/src/pragma.c \
|
||||||
$(TOP)/src/prepare.c \
|
$(TOP)/src/prepare.c \
|
||||||
$(TOP)/src/printf.c \
|
$(TOP)/src/printf.c \
|
||||||
@@ -335,6 +336,7 @@ TESTSRC2 = \
|
|||||||
$(TOP)/src/os_win.c \
|
$(TOP)/src/os_win.c \
|
||||||
$(TOP)/src/pager.c \
|
$(TOP)/src/pager.c \
|
||||||
$(TOP)/src/pcache.c \
|
$(TOP)/src/pcache.c \
|
||||||
|
$(TOP)/src/pcache1.c \
|
||||||
$(TOP)/src/pragma.c \
|
$(TOP)/src/pragma.c \
|
||||||
$(TOP)/src/prepare.c \
|
$(TOP)/src/prepare.c \
|
||||||
$(TOP)/src/printf.c \
|
$(TOP)/src/printf.c \
|
||||||
@@ -596,6 +598,9 @@ pager.lo: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h
|
|||||||
pcache.lo: $(TOP)/src/pcache.c $(HDR) $(TOP)/src/pcache.h
|
pcache.lo: $(TOP)/src/pcache.c $(HDR) $(TOP)/src/pcache.h
|
||||||
$(LTCOMPILE) -c $(TOP)/src/pcache.c
|
$(LTCOMPILE) -c $(TOP)/src/pcache.c
|
||||||
|
|
||||||
|
pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h
|
||||||
|
$(LTCOMPILE) -c $(TOP)/src/pcache1.c
|
||||||
|
|
||||||
opcodes.lo: opcodes.c
|
opcodes.lo: opcodes.c
|
||||||
$(LTCOMPILE) -c opcodes.c
|
$(LTCOMPILE) -c opcodes.c
|
||||||
|
|
||||||
|
5
main.mk
5
main.mk
@@ -60,7 +60,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
|
|||||||
memjournal.o \
|
memjournal.o \
|
||||||
mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \
|
||||||
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
opcodes.o os.o os_os2.o os_unix.o os_win.o \
|
||||||
pager.o parse.o pcache.o pragma.o prepare.o printf.o \
|
pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||||
random.o resolve.o rtree.o select.o status.o \
|
random.o resolve.o rtree.o select.o status.o \
|
||||||
table.o tokenize.o trigger.o \
|
table.o tokenize.o trigger.o \
|
||||||
update.o util.o vacuum.o \
|
update.o util.o vacuum.o \
|
||||||
@@ -122,6 +122,7 @@ SRC = \
|
|||||||
$(TOP)/src/parse.y \
|
$(TOP)/src/parse.y \
|
||||||
$(TOP)/src/pcache.c \
|
$(TOP)/src/pcache.c \
|
||||||
$(TOP)/src/pcache.h \
|
$(TOP)/src/pcache.h \
|
||||||
|
$(TOP)/src/pcache1.c \
|
||||||
$(TOP)/src/pragma.c \
|
$(TOP)/src/pragma.c \
|
||||||
$(TOP)/src/prepare.c \
|
$(TOP)/src/prepare.c \
|
||||||
$(TOP)/src/printf.c \
|
$(TOP)/src/printf.c \
|
||||||
@@ -242,7 +243,7 @@ TESTSRC2 = \
|
|||||||
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
|
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
|
||||||
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
|
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
|
||||||
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
|
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
|
||||||
$(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
$(TOP)/src/pcache1.c $(TOP)/src/select.c $(TOP)/src/tokenize.c \
|
||||||
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
$(TOP)/src/utf.c $(TOP)/src/util.c $(TOP)/src/vdbeapi.c $(TOP)/src/vdbeaux.c \
|
||||||
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c \
|
$(TOP)/src/vdbe.c $(TOP)/src/vdbemem.c $(TOP)/src/where.c parse.c \
|
||||||
$(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_tokenizer.c
|
$(TOP)/ext/fts3/fts3.c $(TOP)/ext/fts3/fts3_tokenizer.c
|
||||||
|
29
manifest
29
manifest
@@ -1,7 +1,7 @@
|
|||||||
C Avoid\ssome\sbuffer\soverreads\sdetected\sby\svalgrind\swhile\srunning\scorruptC.test.\s(CVS\s5898)
|
C Add\san\sAPI\sto\ssupport\scustom\spage\scache\simplementations.\s(CVS\s5899)
|
||||||
D 2008-11-12T18:21:36
|
D 2008-11-13T14:28:29
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 48172b58e444a9725ec482e0c022a564749acab4
|
F Makefile.in 6cbc7db84c23804c368bc7ffe51367412212d7b2
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
F README b974cdc3f9f12b87e851b04e75996d720ebf81ac
|
F README b974cdc3f9f12b87e851b04e75996d720ebf81ac
|
||||||
F VERSION 4eb06187e9b9f72fdf3ba27c407e24665d0bb513
|
F VERSION 4eb06187e9b9f72fdf3ba27c407e24665d0bb513
|
||||||
@@ -79,7 +79,7 @@ F ext/rtree/tkt3363.test 6662237ea75bb431cd5d262dfc9535e1023315fc
|
|||||||
F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869
|
F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869
|
||||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
|
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
|
||||||
F ltmain.sh 09fe5815427dc7d0abb188bbcdf0e34896577210
|
F ltmain.sh 09fe5815427dc7d0abb188bbcdf0e34896577210
|
||||||
F main.mk 187bbff650073ffad5e8313795665ea64f36c0d5
|
F main.mk 2da751f09754965bcbc8b3fde56c8c71e67ebdb5
|
||||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||||
@@ -99,7 +99,7 @@ F src/attach.c 208881c87160d9e2c73a46cf86116c5a6d66f9d7
|
|||||||
F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
||||||
F src/bitvec.c 9e922b2577b7e46d8f95349bca6a52f7674d7582
|
F src/bitvec.c 9e922b2577b7e46d8f95349bca6a52f7674d7582
|
||||||
F src/btmutex.c 3a90096c3080b9057dc570b8e16e46511e1c788a
|
F src/btmutex.c 3a90096c3080b9057dc570b8e16e46511e1c788a
|
||||||
F src/btree.c 7c06d81faa3a44116968987a5189b4a2f6a37962
|
F src/btree.c be3e0aa63755b094941f9c4298a987fe93df0f22
|
||||||
F src/btree.h 179c3ea813780df78a289a8f5130db18e6d4616e
|
F src/btree.h 179c3ea813780df78a289a8f5130db18e6d4616e
|
||||||
F src/btreeInt.h e38e9b2b285f40f5bc0a6664f630d4a141622f16
|
F src/btreeInt.h e38e9b2b285f40f5bc0a6664f630d4a141622f16
|
||||||
F src/build.c 98a6884d47c3cc12faeb2e9a926018d3a7382133
|
F src/build.c 98a6884d47c3cc12faeb2e9a926018d3a7382133
|
||||||
@@ -118,7 +118,7 @@ F src/insert.c d61998cb8d934bd42b77bd362f94cc3368c5d5f7
|
|||||||
F src/journal.c cffd2cd214e58c0e99c3ff632b3bee6c7cbb260e
|
F src/journal.c cffd2cd214e58c0e99c3ff632b3bee6c7cbb260e
|
||||||
F src/legacy.c aac57bd984e666059011ea01ec4383892a253be3
|
F src/legacy.c aac57bd984e666059011ea01ec4383892a253be3
|
||||||
F src/loadext.c 3872457afdf25bb174fd383cb4e3e0d2a9e60552
|
F src/loadext.c 3872457afdf25bb174fd383cb4e3e0d2a9e60552
|
||||||
F src/main.c cdc0fa9b11254d7e9f15c67b22a3981cb5090119
|
F src/main.c fd93666b883dbe976f8fb9a5b87784bde2eca43d
|
||||||
F src/malloc.c a213fb461b8df08aed7606f6a1e1d3452e089000
|
F src/malloc.c a213fb461b8df08aed7606f6a1e1d3452e089000
|
||||||
F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c
|
F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c
|
||||||
F src/mem1.c 2091081d1c6bcd4516738f37cd84d42e814cf9a2
|
F src/mem1.c 2091081d1c6bcd4516738f37cd84d42e814cf9a2
|
||||||
@@ -138,11 +138,12 @@ F src/os_common.h 24525d8b7bce66c374dfc1810a6c9043f3359b60
|
|||||||
F src/os_os2.c 63be0987dbeb42e9b08c831863d2a315953b86e1
|
F src/os_os2.c 63be0987dbeb42e9b08c831863d2a315953b86e1
|
||||||
F src/os_unix.c af390f03ebe339e63911aec2d4ccd1e53fde581e
|
F src/os_unix.c af390f03ebe339e63911aec2d4ccd1e53fde581e
|
||||||
F src/os_win.c e208cbbceac63c1dd881d0909de5a4679a2c6992
|
F src/os_win.c e208cbbceac63c1dd881d0909de5a4679a2c6992
|
||||||
F src/pager.c 6b6f8eb4938d184d6612ea89631185dbace246b3
|
F src/pager.c c3f46fcb60a9049dd2f3039dfcde60fbbb83b41b
|
||||||
F src/pager.h 4a57b219c0765fe1870238064e3f46e4eb2cf5af
|
F src/pager.h 4a57b219c0765fe1870238064e3f46e4eb2cf5af
|
||||||
F src/parse.y 2c4758b4c5ead6de8cf7112f5a7cce7561d313fe
|
F src/parse.y 2c4758b4c5ead6de8cf7112f5a7cce7561d313fe
|
||||||
F src/pcache.c 3d547c392145113336d44b901e048b86ee6a4774
|
F src/pcache.c 439bcf164f10dd0595cbd63f7472881d46dcbb61
|
||||||
F src/pcache.h 2caf2deb6cbaa75c423b8b96fc1411069ee77c75
|
F src/pcache.h b6feb183dea39ede8336bb47c5969403d26fa5c0
|
||||||
|
F src/pcache1.c 7612a5d850ba48b4f0230a6937469f55a7cee43d
|
||||||
F src/pragma.c 5d4333a27ef4f770fc69ca4f138419d3b462c554
|
F src/pragma.c 5d4333a27ef4f770fc69ca4f138419d3b462c554
|
||||||
F src/prepare.c ae49b8298eca79acdbc964679962e089b943ec94
|
F src/prepare.c ae49b8298eca79acdbc964679962e089b943ec94
|
||||||
F src/printf.c 785f87120589c1db672e37c6eb1087c456e6f84d
|
F src/printf.c 785f87120589c1db672e37c6eb1087c456e6f84d
|
||||||
@@ -150,9 +151,9 @@ F src/random.c a87afbd598aa877e23ac676ee92fd8ee5c786a51
|
|||||||
F src/resolve.c 266bb03d2b456fe68f5df2dd5687e7e88ff8088d
|
F src/resolve.c 266bb03d2b456fe68f5df2dd5687e7e88ff8088d
|
||||||
F src/select.c b03c6fe474ded7bd110ca7b551bf0236133c12da
|
F src/select.c b03c6fe474ded7bd110ca7b551bf0236133c12da
|
||||||
F src/shell.c 01835f435d2e42be95480f7d7cce48e9b255652e
|
F src/shell.c 01835f435d2e42be95480f7d7cce48e9b255652e
|
||||||
F src/sqlite.h.in b73e17f40b0dcfc4b17f5ef18ba90bca8d1e8bc5
|
F src/sqlite.h.in 85e159e1d634c84ddbf87481293d5b1d26e2d27b
|
||||||
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
||||||
F src/sqliteInt.h f2b50cd40d847ccf2d9baf5a622e744a05f136e3
|
F src/sqliteInt.h 024f3e7722860905c50b59669127e1ac5e520b99
|
||||||
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
|
F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8
|
||||||
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
||||||
F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8
|
F src/table.c 22744786199c9195720c15a7a42cb97b2e2728d8
|
||||||
@@ -656,7 +657,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
|||||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
P 369f74983bb1b6a6426260148018cdc084fc2b49
|
P faa6bd7b615837c920b5b3b027115caa2f56ec15
|
||||||
R 1f4429807d31bf0a13a5939ab83fd57a
|
R d0a03787d9cbd01ae9ab954f29497939
|
||||||
U danielk1977
|
U danielk1977
|
||||||
Z ced029dc2e076de2b81cab7ea9f2d078
|
Z 6ca197766dcdc265d028d54cbf94409d
|
||||||
|
@@ -1 +1 @@
|
|||||||
faa6bd7b615837c920b5b3b027115caa2f56ec15
|
47866d6708e9b69e367937fd85f93580fd025447
|
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.534 2008/11/12 18:21:36 danielk1977 Exp $
|
** $Id: btree.c,v 1.535 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
**
|
**
|
||||||
** This file implements a external (disk-based) database using BTrees.
|
** This file implements a external (disk-based) database using BTrees.
|
||||||
** See the header comment on "btreeInt.h" for additional information.
|
** See the header comment on "btreeInt.h" for additional information.
|
||||||
@@ -2603,9 +2603,14 @@ void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
|
|||||||
BtCursor *p;
|
BtCursor *p;
|
||||||
sqlite3BtreeEnter(pBtree);
|
sqlite3BtreeEnter(pBtree);
|
||||||
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
||||||
|
int i;
|
||||||
sqlite3BtreeClearCursor(p);
|
sqlite3BtreeClearCursor(p);
|
||||||
p->eState = CURSOR_FAULT;
|
p->eState = CURSOR_FAULT;
|
||||||
p->skip = errCode;
|
p->skip = errCode;
|
||||||
|
for(i=0; i<=p->iPage; i++){
|
||||||
|
releasePage(p->apPage[i]);
|
||||||
|
p->apPage[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sqlite3BtreeLeave(pBtree);
|
sqlite3BtreeLeave(pBtree);
|
||||||
}
|
}
|
||||||
@@ -5616,6 +5621,7 @@ static int balance_deeper(BtCursor *pCur){
|
|||||||
memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
|
memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
|
||||||
memcpy(&cdata[cbrk], &data[cbrk], usableSize-cbrk);
|
memcpy(&cdata[cbrk], &data[cbrk], usableSize-cbrk);
|
||||||
|
|
||||||
|
assert( pChild->isInit==0 );
|
||||||
rc = sqlite3BtreeInitPage(pChild);
|
rc = sqlite3BtreeInitPage(pChild);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
int nCopy = pPage->nOverflow*sizeof(pPage->aOvfl[0]);
|
int nCopy = pPage->nOverflow*sizeof(pPage->aOvfl[0]);
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
** other files are for internal use by SQLite and should not be
|
** other files are for internal use by SQLite and should not be
|
||||||
** accessed by users of the library.
|
** accessed by users of the library.
|
||||||
**
|
**
|
||||||
** $Id: main.c,v 1.511 2008/11/10 18:05:36 shane Exp $
|
** $Id: main.c,v 1.512 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -309,6 +309,12 @@ int sqlite3_config(int op, ...){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SQLITE_CONFIG_PCACHE: {
|
||||||
|
/* Specify an alternative malloc implementation */
|
||||||
|
sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||||
case SQLITE_CONFIG_HEAP: {
|
case SQLITE_CONFIG_HEAP: {
|
||||||
/* Designate a buffer for heap memory space */
|
/* Designate a buffer for heap memory space */
|
||||||
|
53
src/pager.c
53
src/pager.c
@@ -18,7 +18,7 @@
|
|||||||
** file simultaneously, or one process from reading the database while
|
** file simultaneously, or one process from reading the database while
|
||||||
** another is writing.
|
** another is writing.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.c,v 1.502 2008/11/07 00:24:54 drh Exp $
|
** @(#) $Id: pager.c,v 1.503 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef SQLITE_OMIT_DISKIO
|
#ifndef SQLITE_OMIT_DISKIO
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
@@ -312,6 +312,10 @@ static int pageInStatement(PgHdr *pPg){
|
|||||||
return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
|
return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pageInJournal(PgHdr *pPg){
|
||||||
|
return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Read a 32-bit integer from the given file descriptor. Store the integer
|
** Read a 32-bit integer from the given file descriptor. Store the integer
|
||||||
** that is read in *pRes. Return SQLITE_OK if everything worked, or an
|
** that is read in *pRes. Return SQLITE_OK if everything worked, or an
|
||||||
@@ -454,7 +458,7 @@ static u32 pager_datahash(int nByte, unsigned char *pData){
|
|||||||
static u32 pager_pagehash(PgHdr *pPage){
|
static u32 pager_pagehash(PgHdr *pPage){
|
||||||
return pager_datahash(pPage->pPager->pageSize, (unsigned char *)pPage->pData);
|
return pager_datahash(pPage->pPager->pageSize, (unsigned char *)pPage->pData);
|
||||||
}
|
}
|
||||||
static u32 pager_set_pagehash(PgHdr *pPage){
|
static void pager_set_pagehash(PgHdr *pPage){
|
||||||
pPage->pageHash = pager_pagehash(pPage);
|
pPage->pageHash = pager_pagehash(pPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,13 +1001,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
|
|||||||
pPager->pInJournal = 0;
|
pPager->pInJournal = 0;
|
||||||
sqlite3BitvecDestroy(pPager->pAlwaysRollback);
|
sqlite3BitvecDestroy(pPager->pAlwaysRollback);
|
||||||
pPager->pAlwaysRollback = 0;
|
pPager->pAlwaysRollback = 0;
|
||||||
sqlite3PcacheCleanAll(pPager->pPCache);
|
|
||||||
#ifdef SQLITE_CHECK_PAGES
|
#ifdef SQLITE_CHECK_PAGES
|
||||||
sqlite3PcacheIterate(pPager->pPCache, pager_set_pagehash);
|
sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
|
||||||
#endif
|
#endif
|
||||||
sqlite3PcacheClearFlags(pPager->pPCache,
|
sqlite3PcacheCleanAll(pPager->pPCache);
|
||||||
PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC
|
|
||||||
);
|
|
||||||
pPager->dirtyCache = 0;
|
pPager->dirtyCache = 0;
|
||||||
pPager->nRec = 0;
|
pPager->nRec = 0;
|
||||||
}else{
|
}else{
|
||||||
@@ -2339,19 +2340,9 @@ static int syncJournal(Pager *pPager){
|
|||||||
|
|
||||||
/* Erase the needSync flag from every page.
|
/* Erase the needSync flag from every page.
|
||||||
*/
|
*/
|
||||||
sqlite3PcacheClearFlags(pPager->pPCache, PGHDR_NEED_SYNC);
|
sqlite3PcacheClearSyncFlags(pPager->pPCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
/* If the Pager.needSync flag is clear then the PgHdr.needSync
|
|
||||||
** flag must also be clear for all pages. Verify that this
|
|
||||||
** invariant is true.
|
|
||||||
*/
|
|
||||||
else{
|
|
||||||
sqlite3PcacheAssertFlags(pPager->pPCache, 0, PGHDR_NEED_SYNC);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2852,9 +2843,6 @@ int sqlite3PagerAcquire(
|
|||||||
int nMax;
|
int nMax;
|
||||||
PAGER_INCR(pPager->nMiss);
|
PAGER_INCR(pPager->nMiss);
|
||||||
pPg->pPager = pPager;
|
pPg->pPager = pPager;
|
||||||
if( sqlite3BitvecTest(pPager->pInJournal, pgno) ){
|
|
||||||
pPg->flags |= PGHDR_IN_JOURNAL;
|
|
||||||
}
|
|
||||||
memset(pPg->pExtra, 0, pPager->nExtra);
|
memset(pPg->pExtra, 0, pPager->nExtra);
|
||||||
|
|
||||||
rc = sqlite3PagerPagecount(pPager, &nMax);
|
rc = sqlite3PagerPagecount(pPager, &nMax);
|
||||||
@@ -3058,7 +3046,6 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
|
|||||||
if( pPager->state==PAGER_SHARED ){
|
if( pPager->state==PAGER_SHARED ){
|
||||||
assert( pPager->pInJournal==0 );
|
assert( pPager->pInJournal==0 );
|
||||||
assert( !MEMDB );
|
assert( !MEMDB );
|
||||||
sqlite3PcacheAssertFlags(pPager->pPCache, 0, PGHDR_IN_JOURNAL);
|
|
||||||
rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
|
rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
pPager->state = PAGER_RESERVED;
|
pPager->state = PAGER_RESERVED;
|
||||||
@@ -3167,9 +3154,7 @@ static int pager_write(PgHdr *pPg){
|
|||||||
** to the journal then we can return right away.
|
** to the journal then we can return right away.
|
||||||
*/
|
*/
|
||||||
makeDirty(pPg);
|
makeDirty(pPg);
|
||||||
if( (pPg->flags&PGHDR_IN_JOURNAL)
|
if( pageInJournal(pPg) && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
|
||||||
&& (pageInStatement(pPg) || pPager->stmtInUse==0)
|
|
||||||
){
|
|
||||||
pPager->dirtyCache = 1;
|
pPager->dirtyCache = 1;
|
||||||
pPager->dbModified = 1;
|
pPager->dbModified = 1;
|
||||||
}else{
|
}else{
|
||||||
@@ -3199,7 +3184,7 @@ static int pager_write(PgHdr *pPg){
|
|||||||
** EXCLUSIVE lock on the main database file. Write the current page to
|
** EXCLUSIVE lock on the main database file. Write the current page to
|
||||||
** the transaction journal if it is not there already.
|
** the transaction journal if it is not there already.
|
||||||
*/
|
*/
|
||||||
if( !(pPg->flags&PGHDR_IN_JOURNAL) && pPager->journalOpen ){
|
if( !pageInJournal(pPg) && pPager->journalOpen ){
|
||||||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||||
u32 cksum;
|
u32 cksum;
|
||||||
char *pData2;
|
char *pData2;
|
||||||
@@ -3254,7 +3239,6 @@ static int pager_write(PgHdr *pPg){
|
|||||||
if( pPg->flags&PGHDR_NEED_SYNC ){
|
if( pPg->flags&PGHDR_NEED_SYNC ){
|
||||||
pPager->needSync = 1;
|
pPager->needSync = 1;
|
||||||
}
|
}
|
||||||
pPg->flags |= PGHDR_IN_JOURNAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the statement journal is open and the page is not in it,
|
/* If the statement journal is open and the page is not in it,
|
||||||
@@ -3268,8 +3252,7 @@ static int pager_write(PgHdr *pPg){
|
|||||||
){
|
){
|
||||||
i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
|
i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
|
||||||
char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
|
char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
|
||||||
assert( (pPg->flags&PGHDR_IN_JOURNAL)
|
assert( pageInJournal(pPg) || (int)pPg->pgno>pPager->origDbSize );
|
||||||
|| (int)pPg->pgno>pPager->origDbSize );
|
|
||||||
rc = write32bits(pPager->stfd, offset, pPg->pgno);
|
rc = write32bits(pPager->stfd, offset, pPg->pgno);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4);
|
rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4);
|
||||||
@@ -3515,7 +3498,6 @@ void sqlite3PagerDontRollback(DbPage *pPg){
|
|||||||
|
|
||||||
assert( pPager->pInJournal!=0 );
|
assert( pPager->pInJournal!=0 );
|
||||||
sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
|
sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
|
||||||
pPg->flags |= PGHDR_IN_JOURNAL;
|
|
||||||
pPg->flags &= ~PGHDR_NEED_READ;
|
pPg->flags &= ~PGHDR_NEED_READ;
|
||||||
if( pPager->stmtInUse ){
|
if( pPager->stmtInUse ){
|
||||||
assert( pPager->stmtSize >= pPager->origDbSize );
|
assert( pPager->stmtSize >= pPager->origDbSize );
|
||||||
@@ -4077,7 +4059,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||||||
*/
|
*/
|
||||||
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
|
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
|
||||||
needSyncPgno = pPg->pgno;
|
needSyncPgno = pPg->pgno;
|
||||||
assert( (pPg->flags&PGHDR_IN_JOURNAL) || (int)pgno>pPager->origDbSize );
|
assert( pageInJournal(pPg) || (int)pgno>pPager->origDbSize );
|
||||||
assert( pPg->flags&PGHDR_DIRTY );
|
assert( pPg->flags&PGHDR_DIRTY );
|
||||||
assert( pPager->needSync );
|
assert( pPager->needSync );
|
||||||
}
|
}
|
||||||
@@ -4087,20 +4069,16 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||||||
** page pgno before the 'move' operation, it needs to be retained
|
** page pgno before the 'move' operation, it needs to be retained
|
||||||
** for the page moved there.
|
** for the page moved there.
|
||||||
*/
|
*/
|
||||||
pPg->flags &= ~(PGHDR_NEED_SYNC|PGHDR_IN_JOURNAL);
|
pPg->flags &= ~PGHDR_NEED_SYNC;
|
||||||
pPgOld = pager_lookup(pPager, pgno);
|
pPgOld = pager_lookup(pPager, pgno);
|
||||||
assert( !pPgOld || pPgOld->nRef==1 );
|
assert( !pPgOld || pPgOld->nRef==1 );
|
||||||
if( pPgOld ){
|
if( pPgOld ){
|
||||||
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
|
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
|
||||||
}
|
}
|
||||||
if( sqlite3BitvecTest(pPager->pInJournal, pgno) ){
|
|
||||||
pPg->flags |= PGHDR_IN_JOURNAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3PcacheMove(pPg, pgno);
|
sqlite3PcacheMove(pPg, pgno);
|
||||||
if( pPgOld ){
|
if( pPgOld ){
|
||||||
sqlite3PcacheMove(pPgOld, 0);
|
sqlite3PcacheDrop(pPgOld);
|
||||||
sqlite3PcacheRelease(pPgOld);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeDirty(pPg);
|
makeDirty(pPg);
|
||||||
@@ -4138,7 +4116,6 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
|
|||||||
pPager->needSync = 1;
|
pPager->needSync = 1;
|
||||||
assert( pPager->noSync==0 && !MEMDB );
|
assert( pPager->noSync==0 && !MEMDB );
|
||||||
pPgHdr->flags |= PGHDR_NEED_SYNC;
|
pPgHdr->flags |= PGHDR_NEED_SYNC;
|
||||||
pPgHdr->flags |= PGHDR_IN_JOURNAL;
|
|
||||||
makeDirty(pPgHdr);
|
makeDirty(pPgHdr);
|
||||||
sqlite3PagerUnref(pPgHdr);
|
sqlite3PagerUnref(pPgHdr);
|
||||||
}
|
}
|
||||||
|
1071
src/pcache.c
1071
src/pcache.c
File diff suppressed because it is too large
Load Diff
33
src/pcache.h
33
src/pcache.h
@@ -12,7 +12,7 @@
|
|||||||
** This header file defines the interface that the sqlite page cache
|
** This header file defines the interface that the sqlite page cache
|
||||||
** subsystem.
|
** subsystem.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pcache.h,v 1.14 2008/10/17 18:51:53 danielk1977 Exp $
|
** @(#) $Id: pcache.h,v 1.15 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PCACHE_H_
|
#ifndef _PCACHE_H_
|
||||||
@@ -34,6 +34,7 @@ struct PgHdr {
|
|||||||
u32 pageHash; /* Hash of page content */
|
u32 pageHash; /* Hash of page content */
|
||||||
#endif
|
#endif
|
||||||
u16 flags; /* PGHDR flags defined below */
|
u16 flags; /* PGHDR flags defined below */
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
** Elements above are public. All that follows is private to pcache.c
|
** Elements above are public. All that follows is private to pcache.c
|
||||||
** and should not be accessed by other modules.
|
** and should not be accessed by other modules.
|
||||||
@@ -41,18 +42,11 @@ struct PgHdr {
|
|||||||
i16 nRef; /* Number of users of this page */
|
i16 nRef; /* Number of users of this page */
|
||||||
PCache *pCache; /* Cache that owns this page */
|
PCache *pCache; /* Cache that owns this page */
|
||||||
|
|
||||||
/**********************************************************************
|
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
|
||||||
** Elements above are accessible at any time by the owner of the cache
|
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
|
||||||
** without the need for a mutex. The elements that follow can only be
|
|
||||||
** accessed while holding the SQLITE_MUTEX_STATIC_LRU mutex.
|
|
||||||
*/
|
|
||||||
PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
|
|
||||||
PgHdr *pNext, *pPrev; /* List of clean or dirty pages */
|
|
||||||
PgHdr *pNextLru, *pPrevLru; /* Part of global LRU list */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Bit values for PgHdr.flags */
|
/* Bit values for PgHdr.flags */
|
||||||
#define PGHDR_IN_JOURNAL 0x001 /* Page is in rollback journal */
|
|
||||||
#define PGHDR_DIRTY 0x002 /* Page has changed */
|
#define PGHDR_DIRTY 0x002 /* Page has changed */
|
||||||
#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
|
#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
|
||||||
** writing this page to the database */
|
** writing this page to the database */
|
||||||
@@ -116,14 +110,7 @@ PgHdr *sqlite3PcacheDirtyList(PCache*);
|
|||||||
void sqlite3PcacheClose(PCache*);
|
void sqlite3PcacheClose(PCache*);
|
||||||
|
|
||||||
/* Clear flags from pages of the page cache */
|
/* Clear flags from pages of the page cache */
|
||||||
void sqlite3PcacheClearFlags(PCache*, int mask);
|
void sqlite3PcacheClearSyncFlags(PCache *);
|
||||||
|
|
||||||
/* Assert flags settings on all pages. Debugging only */
|
|
||||||
#ifndef NDEBUG
|
|
||||||
void sqlite3PcacheAssertFlags(PCache*, int trueMask, int falseMask);
|
|
||||||
#else
|
|
||||||
# define sqlite3PcacheAssertFlags(A,B,C)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Return true if the number of dirty pages is 0 or 1 */
|
/* Return true if the number of dirty pages is 0 or 1 */
|
||||||
int sqlite3PcacheZeroOrOneDirtyPages(PCache*);
|
int sqlite3PcacheZeroOrOneDirtyPages(PCache*);
|
||||||
@@ -143,11 +130,11 @@ int sqlite3PcachePageRefcount(PgHdr*);
|
|||||||
int sqlite3PcachePagecount(PCache*);
|
int sqlite3PcachePagecount(PCache*);
|
||||||
|
|
||||||
#ifdef SQLITE_CHECK_PAGES
|
#ifdef SQLITE_CHECK_PAGES
|
||||||
/* Iterate through all pages currently stored in the cache. This interface
|
/* Iterate through all dirty pages currently stored in the cache. This
|
||||||
** is only available if SQLITE_CHECK_PAGES is defined when the library is
|
** interface is only available if SQLITE_CHECK_PAGES is defined when the
|
||||||
** built.
|
** library is built.
|
||||||
*/
|
*/
|
||||||
void sqlite3PcacheIterate(PCache *pCache, void (*xIter)(PgHdr *));
|
void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set and get the suggested cache-size for the specified pager-cache.
|
/* Set and get the suggested cache-size for the specified pager-cache.
|
||||||
@@ -168,4 +155,6 @@ int sqlite3PcacheReleaseMemory(int);
|
|||||||
void sqlite3PcacheStats(int*,int*,int*,int*);
|
void sqlite3PcacheStats(int*,int*,int*,int*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void sqlite3PCacheSetDefault(void);
|
||||||
|
|
||||||
#endif /* _PCACHE_H_ */
|
#endif /* _PCACHE_H_ */
|
||||||
|
735
src/pcache1.c
Normal file
735
src/pcache1.c
Normal file
@@ -0,0 +1,735 @@
|
|||||||
|
/*
|
||||||
|
** 2008 November 05
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
**
|
||||||
|
** This file implements the default page cache implementation (the
|
||||||
|
** sqlite3_pcache interface). It also contains part of the implementation
|
||||||
|
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
|
||||||
|
** If the default page cache implementation is overriden, then neither of
|
||||||
|
** these two features are available.
|
||||||
|
**
|
||||||
|
** @(#) $Id: pcache1.c,v 1.1 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
|
typedef struct PCache1 PCache1;
|
||||||
|
typedef struct PgHdr1 PgHdr1;
|
||||||
|
typedef struct PgFreeslot PgFreeslot;
|
||||||
|
|
||||||
|
/* Pointers to structures of this type are cast and returned as
|
||||||
|
** opaque sqlite3_pcache* handles
|
||||||
|
*/
|
||||||
|
struct PCache1 {
|
||||||
|
/* Cache configuration parameters. Page size (szPage) and the purgeable
|
||||||
|
** flag (bPurgeable) are set when the cache is created. nMax may be
|
||||||
|
** modified at any time by a call to the pcache1CacheSize() method.
|
||||||
|
** The global mutex must be held when accessing nMax.
|
||||||
|
*/
|
||||||
|
int szPage; /* Size of allocated pages in bytes */
|
||||||
|
int bPurgeable; /* True if cache is purgeable */
|
||||||
|
int nMin; /* Minimum number of pages reserved */
|
||||||
|
int nMax; /* Configured "cache_size" value */
|
||||||
|
|
||||||
|
/* Hash table of all pages. The following variables may only be accessed
|
||||||
|
** when the accessor is holding the global mutex (see pcache1EnterMutex()
|
||||||
|
** and pcache1LeaveMutex()).
|
||||||
|
*/
|
||||||
|
int nRecyclable; /* Number of pages in the LRU list */
|
||||||
|
int nPage; /* Total number of pages in apHash */
|
||||||
|
int nHash; /* Number of slots in apHash[] */
|
||||||
|
PgHdr1 **apHash; /* Hash table for fast lookup by key */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each cache entry is represented by an instance of the following
|
||||||
|
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
|
||||||
|
** directly after the structure in memory (see the PGHDR1_TO_PAGE()
|
||||||
|
** macro below).
|
||||||
|
*/
|
||||||
|
struct PgHdr1 {
|
||||||
|
unsigned int iKey; /* Key value (page number) */
|
||||||
|
PgHdr1 *pNext; /* Next in hash table chain */
|
||||||
|
PCache1 *pCache; /* Cache that currently owns this page */
|
||||||
|
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
|
||||||
|
PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Free slots in the allocator used to divide up the buffer provided using
|
||||||
|
** the SQLITE_CONFIG_PAGECACHE mechanism.
|
||||||
|
*/
|
||||||
|
struct PgFreeslot {
|
||||||
|
PgFreeslot *pNext; /* Next free slot */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Global data used by this cache.
|
||||||
|
*/
|
||||||
|
static SQLITE_WSD struct PCacheGlobal {
|
||||||
|
sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */
|
||||||
|
|
||||||
|
int nMaxPage; /* Sum of nMaxPage for purgeable caches */
|
||||||
|
int nMinPage; /* Sum of nMinPage for purgeable caches */
|
||||||
|
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||||
|
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||||
|
|
||||||
|
/* Variables related to SQLITE_CONFIG_PAGECACHE settings. */
|
||||||
|
int szSlot; /* Size of each free slot */
|
||||||
|
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
|
||||||
|
PgFreeslot *pFree; /* Free page blocks */
|
||||||
|
} pcache1_g = {0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** All code in this file should access the global structure above via the
|
||||||
|
** alias "pcache1". This ensures that the WSD emulation is used when
|
||||||
|
** compiling for systems that do not support real WSD.
|
||||||
|
*/
|
||||||
|
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
|
||||||
|
|
||||||
|
/*
|
||||||
|
** When a PgHdr1 structure is allocated, the associated PCache1.szPage
|
||||||
|
** bytes of data are located directly after it in memory (i.e. the total
|
||||||
|
** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
|
||||||
|
** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
|
||||||
|
** an argument and returns a pointer to the associated block of szPage
|
||||||
|
** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
|
||||||
|
** a pointer to a block of szPage bytes of data and the return value is
|
||||||
|
** a pointer to the associated PgHdr1 structure.
|
||||||
|
**
|
||||||
|
** assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(X))==X );
|
||||||
|
*/
|
||||||
|
#define PGHDR1_TO_PAGE(p) (void *)(&((unsigned char *)p)[sizeof(PgHdr1)])
|
||||||
|
#define PAGE_TO_PGHDR1(p) (PgHdr1 *)(&((unsigned char *)p)[-1*sizeof(PgHdr1)])
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Macros to enter and leave the global LRU mutex.
|
||||||
|
*/
|
||||||
|
#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex)
|
||||||
|
#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex)
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is called during initialization if a static buffer is
|
||||||
|
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
|
||||||
|
** verb to sqlite3_config(). Parameter pBuf points to an allocation large
|
||||||
|
** enough to contain 'n' buffers of 'sz' bytes each.
|
||||||
|
*/
|
||||||
|
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
|
||||||
|
PgFreeslot *p;
|
||||||
|
sz &= ~7;
|
||||||
|
pcache1.szSlot = sz;
|
||||||
|
pcache1.pStart = pBuf;
|
||||||
|
pcache1.pFree = 0;
|
||||||
|
while( n-- ){
|
||||||
|
p = (PgFreeslot*)pBuf;
|
||||||
|
p->pNext = pcache1.pFree;
|
||||||
|
pcache1.pFree = p;
|
||||||
|
pBuf = (void*)&((char*)pBuf)[sz];
|
||||||
|
}
|
||||||
|
pcache1.pEnd = pBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Malloc function used within this file to allocate space from the buffer
|
||||||
|
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
|
||||||
|
** such buffer exists or there is no space left in it, this function falls
|
||||||
|
** back to sqlite3Malloc().
|
||||||
|
*/
|
||||||
|
static void *pcache1Alloc(int nByte){
|
||||||
|
void *p;
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
if( nByte<=pcache1.szSlot && pcache1.pFree ){
|
||||||
|
p = (PgHdr1 *)pcache1.pFree;
|
||||||
|
pcache1.pFree = pcache1.pFree->pNext;
|
||||||
|
sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
|
||||||
|
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
/* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the
|
||||||
|
** global pcache mutex and unlock the pager-cache object pCache. This is
|
||||||
|
** so that if the attempt to allocate a new buffer causes the the
|
||||||
|
** configured soft-heap-limit to be breached, it will be possible to
|
||||||
|
** reclaim memory from this pager-cache.
|
||||||
|
*/
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
p = sqlite3Malloc(nByte);
|
||||||
|
pcache1EnterMutex();
|
||||||
|
if( p ){
|
||||||
|
int sz = sqlite3MallocSize(p);
|
||||||
|
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Free an allocated buffer obtained from pcache1Alloc().
|
||||||
|
*/
|
||||||
|
static void pcache1Free(void *p){
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
if( p==0 ) return;
|
||||||
|
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
||||||
|
PgFreeslot *pSlot;
|
||||||
|
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
|
||||||
|
pSlot = (PgFreeslot*)p;
|
||||||
|
pSlot->pNext = pcache1.pFree;
|
||||||
|
pcache1.pFree = pSlot;
|
||||||
|
}else{
|
||||||
|
int iSize = sqlite3MallocSize(p);
|
||||||
|
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||||
|
sqlite3_free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Allocate a new page object initially associated with cache pCache.
|
||||||
|
*/
|
||||||
|
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||||
|
int nByte = sizeof(PgHdr1) + pCache->szPage;
|
||||||
|
PgHdr1 *p = (PgHdr1 *)pcache1Alloc(nByte);
|
||||||
|
if( p ){
|
||||||
|
memset(p, 0, nByte);
|
||||||
|
if( pCache->bPurgeable ){
|
||||||
|
pcache1.nCurrentPage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Free a page object allocated by pcache1AllocPage().
|
||||||
|
*/
|
||||||
|
static void pcache1FreePage(PgHdr1 *p){
|
||||||
|
if( p ){
|
||||||
|
if( p->pCache->bPurgeable ){
|
||||||
|
pcache1.nCurrentPage--;
|
||||||
|
}
|
||||||
|
pcache1Free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Malloc function used by SQLite to obtain space from the buffer configured
|
||||||
|
** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer
|
||||||
|
** exists, this function falls back to sqlite3Malloc().
|
||||||
|
*/
|
||||||
|
void *sqlite3PageMalloc(int sz){
|
||||||
|
void *p;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
p = pcache1Alloc(sz);
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Free an allocated buffer obtained from sqlite3PageMalloc().
|
||||||
|
*/
|
||||||
|
void sqlite3PageFree(void *p){
|
||||||
|
pcache1EnterMutex();
|
||||||
|
pcache1Free(p);
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******** General Implementation Functions ************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is used to resize the hash table used by the cache passed
|
||||||
|
** as the first argument.
|
||||||
|
**
|
||||||
|
** The global mutex must be held when this function is called.
|
||||||
|
*/
|
||||||
|
static int pcache1ResizeHash(PCache1 *p){
|
||||||
|
PgHdr1 **apNew;
|
||||||
|
int nNew;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
|
||||||
|
nNew = p->nHash*2;
|
||||||
|
if( nNew<256 ){
|
||||||
|
nNew = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
|
||||||
|
pcache1EnterMutex();
|
||||||
|
if( apNew ){
|
||||||
|
memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
|
||||||
|
for(i=0; i<p->nHash; i++){
|
||||||
|
PgHdr1 *pPage;
|
||||||
|
PgHdr1 *pNext = p->apHash[i];
|
||||||
|
while( (pPage = pNext) ){
|
||||||
|
unsigned int h = pPage->iKey % nNew;
|
||||||
|
pNext = pPage->pNext;
|
||||||
|
pPage->pNext = apNew[h];
|
||||||
|
apNew[h] = pPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_free(p->apHash);
|
||||||
|
p->apHash = apNew;
|
||||||
|
p->nHash = nNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (p->apHash ? SQLITE_OK : SQLITE_NOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is used internally to remove the page pPage from the
|
||||||
|
** global LRU list, if is part of it. If pPage is not part of the global
|
||||||
|
** LRU list, then this function is a no-op.
|
||||||
|
**
|
||||||
|
** The global mutex must be held when this function is called.
|
||||||
|
*/
|
||||||
|
static void pcache1PinPage(PgHdr1 *pPage){
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){
|
||||||
|
if( pPage->pLruPrev ){
|
||||||
|
pPage->pLruPrev->pLruNext = pPage->pLruNext;
|
||||||
|
}
|
||||||
|
if( pPage->pLruNext ){
|
||||||
|
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
|
||||||
|
}
|
||||||
|
if( pcache1.pLruHead==pPage ){
|
||||||
|
pcache1.pLruHead = pPage->pLruNext;
|
||||||
|
}
|
||||||
|
if( pcache1.pLruTail==pPage ){
|
||||||
|
pcache1.pLruTail = pPage->pLruPrev;
|
||||||
|
}
|
||||||
|
pPage->pLruNext = 0;
|
||||||
|
pPage->pLruPrev = 0;
|
||||||
|
pPage->pCache->nRecyclable--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Remove the page supplied as an argument from the hash table
|
||||||
|
** (PCache1.apHash structure) that it is currently stored in.
|
||||||
|
**
|
||||||
|
** The global mutex must be held when this function is called.
|
||||||
|
*/
|
||||||
|
static void pcache1RemoveFromHash(PgHdr1 *pPage){
|
||||||
|
unsigned int h;
|
||||||
|
PCache1 *pCache = pPage->pCache;
|
||||||
|
PgHdr1 **pp;
|
||||||
|
|
||||||
|
h = pPage->iKey % pCache->nHash;
|
||||||
|
for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext);
|
||||||
|
*pp = (*pp)->pNext;
|
||||||
|
|
||||||
|
pCache->nPage--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If there are currently more than pcache.nMaxPage pages allocated, try
|
||||||
|
** to recycle pages to reduce the number allocated to pcache.nMaxPage.
|
||||||
|
*/
|
||||||
|
static void pcache1EnforceMaxPage(void){
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){
|
||||||
|
PgHdr1 *p = pcache1.pLruTail;
|
||||||
|
pcache1PinPage(p);
|
||||||
|
pcache1RemoveFromHash(p);
|
||||||
|
pcache1FreePage(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Discard all pages from cache pCache with a page number (key value)
|
||||||
|
** greater than or equal to iLimit. Any pinned pages that meet this
|
||||||
|
** criteria are unpinned before they are discarded.
|
||||||
|
**
|
||||||
|
** The global mutex must be held when this function is called.
|
||||||
|
*/
|
||||||
|
static void pcache1TruncateUnsafe(
|
||||||
|
PCache1 *pCache,
|
||||||
|
unsigned int iLimit
|
||||||
|
){
|
||||||
|
unsigned int h;
|
||||||
|
assert( sqlite3_mutex_held(pcache1.mutex) );
|
||||||
|
for(h=0; h<pCache->nHash; h++){
|
||||||
|
PgHdr1 **pp = &pCache->apHash[h];
|
||||||
|
PgHdr1 *pPage;
|
||||||
|
while( (pPage = *pp) ){
|
||||||
|
if( pPage->iKey>=iLimit ){
|
||||||
|
pcache1PinPage(pPage);
|
||||||
|
*pp = pPage->pNext;
|
||||||
|
pcache1FreePage(pPage);
|
||||||
|
}else{
|
||||||
|
pp = &pPage->pNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******** sqlite3_pcache Methods **********************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xInit method.
|
||||||
|
*/
|
||||||
|
static int pcache1Init(void *pUnused){
|
||||||
|
memset(&pcache1, 0, sizeof(pcache1));
|
||||||
|
if( sqlite3GlobalConfig.bCoreMutex ){
|
||||||
|
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
|
||||||
|
}
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xShutdown method.
|
||||||
|
*/
|
||||||
|
static void pcache1Shutdown(void *pUnused){
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xCreate method.
|
||||||
|
**
|
||||||
|
** Allocate a new cache.
|
||||||
|
*/
|
||||||
|
static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||||
|
PCache1 *pCache;
|
||||||
|
|
||||||
|
pCache = (PCache1 *)sqlite3_malloc(sizeof(PCache1));
|
||||||
|
if( pCache ){
|
||||||
|
memset(pCache, 0, sizeof(PCache1));
|
||||||
|
pCache->szPage = szPage;
|
||||||
|
pCache->bPurgeable = (bPurgeable ? 1 : 0);
|
||||||
|
if( bPurgeable ){
|
||||||
|
pCache->nMin = 10;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
pcache1.nMinPage += pCache->nMin;
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (sqlite3_pcache *)pCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xCachesize method.
|
||||||
|
**
|
||||||
|
** Configure the cache_size limit for a cache.
|
||||||
|
*/
|
||||||
|
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
if( pCache->bPurgeable ){
|
||||||
|
pcache1EnterMutex();
|
||||||
|
pcache1.nMaxPage += (nMax - pCache->nMax);
|
||||||
|
pCache->nMax = nMax;
|
||||||
|
pcache1EnforceMaxPage();
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xPagecount method.
|
||||||
|
*/
|
||||||
|
static int pcache1Pagecount(sqlite3_pcache *p){
|
||||||
|
int n;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
n = ((PCache1 *)p)->nPage;
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xFetch method.
|
||||||
|
**
|
||||||
|
** Fetch a page by key value.
|
||||||
|
**
|
||||||
|
** Whether or not a new page may be allocated by this function depends on
|
||||||
|
** the value of the createFlag argument.
|
||||||
|
**
|
||||||
|
** There are three different approaches to obtaining space for a page,
|
||||||
|
** depending on the value of parameter createFlag (which may be 0, 1 or 2).
|
||||||
|
**
|
||||||
|
** 1. Regardless of the value of createFlag, the cache is searched for a
|
||||||
|
** copy of the requested page. If one is found, it is returned.
|
||||||
|
**
|
||||||
|
** 2. If createFlag==0 and the page is not already in the cache, NULL is
|
||||||
|
** returned.
|
||||||
|
**
|
||||||
|
** 3. If createFlag is 1, the cache is marked as purgeable and the page is
|
||||||
|
** not already in the cache, and if either of the following are true,
|
||||||
|
** return NULL:
|
||||||
|
**
|
||||||
|
** (a) the number of pages pinned by the cache is greater than
|
||||||
|
** PCache1.nMax, or
|
||||||
|
** (b) the number of pages pinned by the cache is greater than
|
||||||
|
** the sum of nMax for all purgeable caches, less the sum of
|
||||||
|
** nMin for all other purgeable caches.
|
||||||
|
**
|
||||||
|
** 4. If none of the first three conditions apply and the cache is marked
|
||||||
|
** as purgeable, and if one of the following is true:
|
||||||
|
**
|
||||||
|
** (a) The number of pages allocated for the cache is already
|
||||||
|
** PCache1.nMax, or
|
||||||
|
**
|
||||||
|
** (b) The number of pages allocated for all purgeable caches is
|
||||||
|
** already equal to or greater than the sum of nMax for all
|
||||||
|
** purgeable caches,
|
||||||
|
**
|
||||||
|
** then attempt to recycle a page from the LRU list. If it is the right
|
||||||
|
** size, return the recycled buffer. Otherwise, free the buffer and
|
||||||
|
** proceed to step 5.
|
||||||
|
**
|
||||||
|
** 5. Otherwise, allocate and return a new page buffer.
|
||||||
|
*/
|
||||||
|
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||||
|
int nPinned;
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
PgHdr1 *pPage = 0;
|
||||||
|
|
||||||
|
pcache1EnterMutex();
|
||||||
|
if( createFlag==1 ) sqlite3BeginBenignMalloc();
|
||||||
|
|
||||||
|
/* Search the hash table for an existing entry. */
|
||||||
|
if( pCache->nHash>0 ){
|
||||||
|
unsigned int h = iKey % pCache->nHash;
|
||||||
|
for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pPage || createFlag==0 ){
|
||||||
|
pcache1PinPage(pPage);
|
||||||
|
goto fetch_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 3 of header comment. */
|
||||||
|
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||||
|
if( createFlag==1 && pCache->bPurgeable && (
|
||||||
|
nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage)
|
||||||
|
|| nPinned>=(pCache->nMax)
|
||||||
|
)){
|
||||||
|
goto fetch_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
|
||||||
|
goto fetch_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 4. Try to recycle a page buffer if appropriate. */
|
||||||
|
if( pCache->bPurgeable && pcache1.pLruTail && (
|
||||||
|
pCache->nPage>=pCache->nMax-1 || pcache1.nCurrentPage>=pcache1.nMaxPage
|
||||||
|
)){
|
||||||
|
pPage = pcache1.pLruTail;
|
||||||
|
pcache1RemoveFromHash(pPage);
|
||||||
|
pcache1PinPage(pPage);
|
||||||
|
if( pPage->pCache->szPage!=pCache->szPage ){
|
||||||
|
pcache1FreePage(pPage);
|
||||||
|
pPage = 0;
|
||||||
|
}else{
|
||||||
|
pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 5. If a usable page buffer has still not been found,
|
||||||
|
** attempt to allocate a new one.
|
||||||
|
*/
|
||||||
|
if( !pPage ){
|
||||||
|
pPage = pcache1AllocPage(pCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pPage ){
|
||||||
|
unsigned int h = iKey % pCache->nHash;
|
||||||
|
memset(pPage, 0, pCache->szPage + sizeof(PgHdr1));
|
||||||
|
pCache->nPage++;
|
||||||
|
pPage->iKey = iKey;
|
||||||
|
pPage->pNext = pCache->apHash[h];
|
||||||
|
pPage->pCache = pCache;
|
||||||
|
pCache->apHash[h] = pPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_out:
|
||||||
|
if( createFlag==1 ) sqlite3EndBenignMalloc();
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xUnpin method.
|
||||||
|
**
|
||||||
|
** Mark a page as unpinned (eligible for asynchronous recycling).
|
||||||
|
*/
|
||||||
|
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);
|
||||||
|
|
||||||
|
pcache1EnterMutex();
|
||||||
|
|
||||||
|
/* It is an error to call this function if the page is already
|
||||||
|
** part of the global LRU list.
|
||||||
|
*/
|
||||||
|
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
|
||||||
|
assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage );
|
||||||
|
|
||||||
|
if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){
|
||||||
|
pcache1RemoveFromHash(pPage);
|
||||||
|
pcache1FreePage(pPage);
|
||||||
|
}else{
|
||||||
|
/* Add the page to the global LRU list. Normally, the page is added to
|
||||||
|
** the head of the list (last page to be recycled). However, if the
|
||||||
|
** reuseUnlikely flag passed to this function is true, the page is added
|
||||||
|
** to the tail of the list (first page to be recycled).
|
||||||
|
*/
|
||||||
|
if( pcache1.pLruHead ){
|
||||||
|
pcache1.pLruHead->pLruPrev = pPage;
|
||||||
|
pPage->pLruNext = pcache1.pLruHead;
|
||||||
|
pcache1.pLruHead = pPage;
|
||||||
|
}else{
|
||||||
|
pcache1.pLruTail = pPage;
|
||||||
|
pcache1.pLruHead = pPage;
|
||||||
|
}
|
||||||
|
pCache->nRecyclable++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xRekey method.
|
||||||
|
*/
|
||||||
|
static void pcache1Rekey(
|
||||||
|
sqlite3_pcache *p,
|
||||||
|
void *pPg,
|
||||||
|
unsigned int iOld,
|
||||||
|
unsigned int iNew
|
||||||
|
){
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
PgHdr1 *pPage = PAGE_TO_PGHDR1(pPg);
|
||||||
|
PgHdr1 **pp;
|
||||||
|
unsigned int h;
|
||||||
|
assert( pPage->iKey==iOld );
|
||||||
|
|
||||||
|
pcache1EnterMutex();
|
||||||
|
|
||||||
|
h = iOld%pCache->nHash;
|
||||||
|
pp = &pCache->apHash[h];
|
||||||
|
while( (*pp)!=pPage ){
|
||||||
|
pp = &(*pp)->pNext;
|
||||||
|
}
|
||||||
|
*pp = pPage->pNext;
|
||||||
|
|
||||||
|
h = iNew%pCache->nHash;
|
||||||
|
pPage->iKey = iNew;
|
||||||
|
pPage->pNext = pCache->apHash[h];
|
||||||
|
pCache->apHash[h] = pPage;
|
||||||
|
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xTruncate method.
|
||||||
|
**
|
||||||
|
** Discard all unpinned pages in the cache with a page number equal to
|
||||||
|
** or greater than parameter iLimit. Any pinned pages with a page number
|
||||||
|
** equal to or greater than iLimit are implicitly unpinned.
|
||||||
|
*/
|
||||||
|
static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
pcache1TruncateUnsafe(pCache, iLimit);
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Implementation of the sqlite3_pcache.xDestroy method.
|
||||||
|
**
|
||||||
|
** Destroy a cache allocated using pcache1Create().
|
||||||
|
*/
|
||||||
|
static void pcache1Destroy(sqlite3_pcache *p){
|
||||||
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
pcache1TruncateUnsafe(pCache, 0);
|
||||||
|
pcache1.nMaxPage -= pCache->nMax;
|
||||||
|
pcache1.nMinPage -= pCache->nMin;
|
||||||
|
pcache1EnforceMaxPage();
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
sqlite3_free(pCache->apHash);
|
||||||
|
sqlite3_free(pCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function is called during initialization (sqlite3_initialize()) to
|
||||||
|
** install the default pluggable cache module, assuming the user has not
|
||||||
|
** already provided an alternative.
|
||||||
|
*/
|
||||||
|
void sqlite3PCacheSetDefault(void){
|
||||||
|
static sqlite3_pcache_methods defaultMethods = {
|
||||||
|
0, /* pArg */
|
||||||
|
pcache1Init, /* xInit */
|
||||||
|
pcache1Shutdown, /* xShutdown */
|
||||||
|
pcache1Create, /* xCreate */
|
||||||
|
pcache1Cachesize, /* xCachesize */
|
||||||
|
pcache1Pagecount, /* xPagecount */
|
||||||
|
pcache1Fetch, /* xFetch */
|
||||||
|
pcache1Unpin, /* xUnpin */
|
||||||
|
pcache1Rekey, /* xRekey */
|
||||||
|
pcache1Truncate, /* xTruncate */
|
||||||
|
pcache1Destroy /* xDestroy */
|
||||||
|
};
|
||||||
|
sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||||
|
/*
|
||||||
|
** This function is called to free superfluous dynamically allocated memory
|
||||||
|
** held by the pager system. Memory in use by any SQLite pager allocated
|
||||||
|
** by the current thread may be sqlite3_free()ed.
|
||||||
|
**
|
||||||
|
** nReq is the number of bytes of memory required. Once this much has
|
||||||
|
** been released, the function returns. The return value is the total number
|
||||||
|
** of bytes of memory released.
|
||||||
|
*/
|
||||||
|
int sqlite3PcacheReleaseMemory(int nReq){
|
||||||
|
int nFree = 0;
|
||||||
|
if( pcache1.pStart==0 ){
|
||||||
|
PgHdr1 *p;
|
||||||
|
pcache1EnterMutex();
|
||||||
|
while( (nReq<0 || nFree<nReq) && (p=pcache1.pLruTail) ){
|
||||||
|
nFree += sqlite3MallocSize(p);
|
||||||
|
pcache1PinPage(p);
|
||||||
|
pcache1RemoveFromHash(p);
|
||||||
|
pcache1FreePage(p);
|
||||||
|
}
|
||||||
|
pcache1LeaveMutex();
|
||||||
|
}
|
||||||
|
return nFree;
|
||||||
|
}
|
||||||
|
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
|
||||||
|
|
||||||
|
#ifdef SQLITE_TEST
|
||||||
|
/*
|
||||||
|
** This function is used by test procedures to inspect the internal state
|
||||||
|
** of the global cache.
|
||||||
|
*/
|
||||||
|
void sqlite3PcacheStats(
|
||||||
|
int *pnCurrent, /* OUT: Total number of pages cached */
|
||||||
|
int *pnMax, /* OUT: Global maximum cache size */
|
||||||
|
int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */
|
||||||
|
int *pnRecyclable /* OUT: Total number of pages available for recycling */
|
||||||
|
){
|
||||||
|
PgHdr1 *p;
|
||||||
|
int nRecyclable = 0;
|
||||||
|
for(p=pcache1.pLruHead; p; p=p->pLruNext){
|
||||||
|
nRecyclable++;
|
||||||
|
}
|
||||||
|
*pnCurrent = pcache1.nCurrentPage;
|
||||||
|
*pnMax = pcache1.nMaxPage;
|
||||||
|
*pnMin = pcache1.nMinPage;
|
||||||
|
*pnRecyclable = nRecyclable;
|
||||||
|
}
|
||||||
|
#endif
|
141
src/sqlite.h.in
141
src/sqlite.h.in
@@ -30,7 +30,7 @@
|
|||||||
** the version number) and changes its name to "sqlite3.h" as
|
** the version number) and changes its name to "sqlite3.h" as
|
||||||
** part of the build process.
|
** part of the build process.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqlite.h.in,v 1.412 2008/11/10 23:54:06 drh Exp $
|
** @(#) $Id: sqlite.h.in,v 1.413 2008/11/13 14:28:29 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITE3_H_
|
#ifndef _SQLITE3_H_
|
||||||
#define _SQLITE3_H_
|
#define _SQLITE3_H_
|
||||||
@@ -1334,6 +1334,8 @@ struct sqlite3_mem_methods {
|
|||||||
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
||||||
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
||||||
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
||||||
|
#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
|
||||||
|
#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Configuration Options {H10170} <S20000>
|
** CAPI3REF: Configuration Options {H10170} <S20000>
|
||||||
@@ -6555,6 +6557,143 @@ SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
|||||||
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
|
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
|
||||||
#define SQLITE_STMTSTATUS_SORT 2
|
#define SQLITE_STMTSTATUS_SORT 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The sqlite3_pcache type is opaque. It is implemented by
|
||||||
|
** the pluggable module. The SQLite core has no knowledge of
|
||||||
|
** its size or internal structure and never deals with the
|
||||||
|
** sqlite3_pcache object except by holding and passing pointers
|
||||||
|
** to the object.
|
||||||
|
*/
|
||||||
|
typedef struct sqlite3_pcache sqlite3_pcache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Custom Page Cache API.
|
||||||
|
** EXPERIMENTAL
|
||||||
|
**
|
||||||
|
** The sqlite3_config(SQLITE_CONFIG_SET_PCACHE, ...) interface can
|
||||||
|
** register an alternative page cache implementation by passing in an
|
||||||
|
** instance of the sqlite3_pcache_methods structure. The majority of the
|
||||||
|
** heap memory used by sqlite is used by the page cache to cache data read
|
||||||
|
** from, or ready to be written to, the database file. By implementing a
|
||||||
|
** custom page cache using this API, an application can control more
|
||||||
|
** precisely the amount of memory consumed by sqlite, the way in which
|
||||||
|
** said memory is allocated and released, and the policies used to
|
||||||
|
** determine exactly which parts of a database file are cached and for
|
||||||
|
** how long.
|
||||||
|
**
|
||||||
|
** The contents of the structure are copied to an internal buffer by sqlite
|
||||||
|
** within the call to [sqlite3_config].
|
||||||
|
**
|
||||||
|
** The xInit() method is called once for each call to sqlite3_initialize()
|
||||||
|
** (usually only once during the lifetime of the process). It is passed
|
||||||
|
** a copy of the sqlite3_pcache_methods.pArg value. It can be used to set
|
||||||
|
** up global structures and mutexes required by the custom page cache
|
||||||
|
** implementation. The xShutdown() method is called from within
|
||||||
|
** sqlite3_shutdown(), if the application invokes this API. It can be used
|
||||||
|
** to clean up any outstanding resources before process shutdown, if required.
|
||||||
|
**
|
||||||
|
** The xCreate() method is used to construct a new cache instance. The
|
||||||
|
** first parameter, szPage, is the size in bytes of the pages that must
|
||||||
|
** be allocated by the cache. szPage will not be a power of two. The
|
||||||
|
** second argument, bPurgeable, is true if the cache being created will
|
||||||
|
** be used to cache database pages read from a file stored on disk, or
|
||||||
|
** false if it is used for an in-memory database. The cache implementation
|
||||||
|
** does not have to do anything special based on the value of bPurgeable,
|
||||||
|
** it is purely advisory.
|
||||||
|
**
|
||||||
|
** The xCachesize() method may be called at any time by SQLite to set the
|
||||||
|
** suggested maximum cache-size (number of pages stored by) the cache
|
||||||
|
** instance passed as the first argument. This is the value configured using
|
||||||
|
** the SQLite "PRAGMA cache_size" command. As with the bPurgeable parameter,
|
||||||
|
** the implementation is not required to do anything special with this
|
||||||
|
** value, it is advisory only.
|
||||||
|
**
|
||||||
|
** The xPagecount() method should return the number of pages currently
|
||||||
|
** stored in the cache supplied as an argument.
|
||||||
|
**
|
||||||
|
** The xFetch() method is used to fetch a page and return a pointer to it.
|
||||||
|
** A 'page', in this context, is a buffer of szPage bytes aligned at an
|
||||||
|
** 8-byte boundary. The page to be fetched is determined by the key. The
|
||||||
|
** mimimum key value is 1. After it has been retrieved using xFetch, the page
|
||||||
|
** is considered to be pinned.
|
||||||
|
**
|
||||||
|
** If the requested page is already in the page cache, then a pointer to
|
||||||
|
** the cached buffer should be returned with its contents intact. If the
|
||||||
|
** page is not already in the cache, then the expected behaviour of the
|
||||||
|
** cache is determined by the value of the createFlag parameter passed
|
||||||
|
** to xFetch, according to the following table:
|
||||||
|
**
|
||||||
|
** <table border=1 width=85% align=center>
|
||||||
|
** <tr><th>createFlag<th>Expected Behaviour
|
||||||
|
** <tr><td>0<td>NULL should be returned. No new cache entry is created.
|
||||||
|
** <tr><td>1<td>If createFlag is set to 1, this indicates that
|
||||||
|
** SQLite is holding pinned pages that can be unpinned
|
||||||
|
** by writing their contents to the database file (a
|
||||||
|
** relatively expensive operation). In this situation the
|
||||||
|
** cache implementation has two choices: it can return NULL,
|
||||||
|
** in which case SQLite will attempt to unpin one or more
|
||||||
|
** pages before re-requesting the same page, or it can
|
||||||
|
** allocate a new page and return a pointer to it. If a new
|
||||||
|
** page is allocated, then it must be completely zeroed before
|
||||||
|
** it is returned.
|
||||||
|
** <tr><td>2<td>If createFlag is set to 2, then SQLite is not holding any
|
||||||
|
** pinned pages associated with the specific cache passed
|
||||||
|
** as the first argument to xFetch() that can be unpinned. The
|
||||||
|
** cache implementation should attempt to allocate a new
|
||||||
|
** cache entry and return a pointer to it. Again, the new
|
||||||
|
** page should be zeroed before it is returned. If the xFetch()
|
||||||
|
** method returns NULL when createFlag==2, SQLite assumes that
|
||||||
|
** a memory allocation failed and returns SQLITE_NOMEM to the
|
||||||
|
** user.
|
||||||
|
** </table>
|
||||||
|
**
|
||||||
|
** xUnpin() is called by SQLite with a pointer to a currently pinned page
|
||||||
|
** as its second argument. If the third parameter, discard, is non-zero,
|
||||||
|
** then the page should be evicted from the cache. In this case SQLite
|
||||||
|
** assumes that the next time the page is retrieved from the cache using
|
||||||
|
** the xFetch() method, it will be zeroed. If the discard parameter is
|
||||||
|
** zero, then the page is considered to be unpinned. The cache implementation
|
||||||
|
** may choose to reclaim (free or recycle) unpinned pages at any time.
|
||||||
|
** SQLite assumes that next time the page is retrieved from the cache
|
||||||
|
** it will either be zeroed, or contain the same data that it did when it
|
||||||
|
** was unpinned.
|
||||||
|
**
|
||||||
|
** The cache is not required to perform any reference counting. A single
|
||||||
|
** call to xUnpin() unpins the page regardless of the number of prior calls
|
||||||
|
** to xFetch().
|
||||||
|
**
|
||||||
|
** The xRekey() method is used to change the key value associated with the
|
||||||
|
** page passed as the second argument from oldKey to newKey. If the cache
|
||||||
|
** contains an entry associated with oldKey, it should be discarded. Any
|
||||||
|
** cache entry associated with oldKey is guaranteed not to be pinned.
|
||||||
|
**
|
||||||
|
** When SQLite calls the xTruncate() method, the cache must discard all
|
||||||
|
** existing cache entries with page numbers (keys) greater than or equal
|
||||||
|
** to the value of the iLimit parameter passed to xTruncate(). If any
|
||||||
|
** of these pages are pinned, they are implicitly unpinned, meaning that
|
||||||
|
** they can be safely discarded.
|
||||||
|
**
|
||||||
|
** The xDestroy() method is used to delete a cache allocated by xCreate().
|
||||||
|
** All resources associated with the specified cache should be freed. After
|
||||||
|
** calling the xDestroy() method, SQLite considers the sqlite3_pcache*
|
||||||
|
** handle invalid, and will not use it with any other sqlite3_pcache_methods
|
||||||
|
** functions.
|
||||||
|
*/
|
||||||
|
typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
|
||||||
|
struct sqlite3_pcache_methods {
|
||||||
|
void *pArg;
|
||||||
|
int (*xInit)(void*);
|
||||||
|
void (*xShutdown)(void*);
|
||||||
|
sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable);
|
||||||
|
void (*xCachesize)(sqlite3_pcache*, int nCachesize);
|
||||||
|
int (*xPagecount)(sqlite3_pcache*);
|
||||||
|
void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
|
||||||
|
void (*xUnpin)(sqlite3_pcache*, void*, int discard);
|
||||||
|
void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey);
|
||||||
|
void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
|
||||||
|
void (*xDestroy)(sqlite3_pcache*);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Undo the hack that converts floating point types to integer for
|
** Undo the hack that converts floating point types to integer for
|
||||||
** builds on processors without floating point support.
|
** builds on processors without floating point support.
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.790 2008/11/11 18:29:00 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.791 2008/11/13 14:28:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITEINT_H_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@@ -1958,6 +1958,7 @@ struct Sqlite3Config {
|
|||||||
int nLookaside; /* Default lookaside buffer count */
|
int nLookaside; /* Default lookaside buffer count */
|
||||||
sqlite3_mem_methods m; /* Low-level memory allocation interface */
|
sqlite3_mem_methods m; /* Low-level memory allocation interface */
|
||||||
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
|
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
|
||||||
|
sqlite3_pcache_methods pcache; /* Low-level page-cache interface */
|
||||||
void *pHeap; /* Heap storage space */
|
void *pHeap; /* Heap storage space */
|
||||||
int nHeap; /* Size of pHeap[] */
|
int nHeap; /* Size of pHeap[] */
|
||||||
int mnReq, mxReq; /* Min and max heap requests sizes */
|
int mnReq, mxReq; /* Min and max heap requests sizes */
|
||||||
|
Reference in New Issue
Block a user