track 3.7.10
This commit is contained in:
parent
ddb595fbc9
commit
d09051a1f2
|
@ -31,7 +31,7 @@ TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree
|
|||
# Define this for the autoconf-based build, so that the code knows it can
|
||||
# include the generated config.h
|
||||
#
|
||||
TCC += -D_HAVE_SQLITE_CONFIG_H
|
||||
TCC += -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite
|
||||
|
||||
# Define -DNDEBUG to compile without debugging (i.e., for production usage)
|
||||
# Omitting the define will cause extra debugging code to be inserted and
|
||||
|
@ -518,6 +518,12 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h
|
|||
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl
|
||||
|
||||
tclsqlite3.c: sqlite3.c
|
||||
echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c
|
||||
cat sqlite3.c >>tclsqlite3.c
|
||||
echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c
|
||||
cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c
|
||||
|
||||
sqlite3-all.c: sqlite3.c $(TOP)/tool/split-sqlite3c.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/split-sqlite3c.tcl
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
/* Define to 1 if you have the `localtime_s' function. */
|
||||
#undef HAVE_LOCALTIME_S
|
||||
|
||||
/* Define to 1 if you have the `malloc_usable_size' function. */
|
||||
#undef HAVE_MALLOC_USABLE_SIZE
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h])
|
|||
#########
|
||||
# Figure out whether or not we have these functions
|
||||
#
|
||||
AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s utime])
|
||||
AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s utime malloc_usable_size])
|
||||
|
||||
#########
|
||||
# By default, we use the amalgamation (this may be changed below...)
|
||||
|
|
|
@ -712,6 +712,7 @@ static void fts3Appendf(
|
|||
char *z;
|
||||
va_start(ap, zFormat);
|
||||
z = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
if( z && *pz ){
|
||||
char *z2 = sqlite3_mprintf("%s%s", *pz, z);
|
||||
sqlite3_free(z);
|
||||
|
|
|
@ -40,7 +40,7 @@ typedef struct porter_tokenizer {
|
|||
} porter_tokenizer;
|
||||
|
||||
/*
|
||||
** Class derived from sqlit3_tokenizer_cursor
|
||||
** Class derived from sqlite3_tokenizer_cursor
|
||||
*/
|
||||
typedef struct porter_tokenizer_cursor {
|
||||
sqlite3_tokenizer_cursor base;
|
||||
|
|
|
@ -1386,7 +1386,6 @@ int sqlite3Fts3SegReaderNew(
|
|||
int nRoot, /* Size of buffer containing root node */
|
||||
Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Fts3SegReader *pReader; /* Newly allocated SegReader object */
|
||||
int nExtra = 0; /* Bytes to allocate segment root node */
|
||||
|
||||
|
@ -1414,13 +1413,8 @@ int sqlite3Fts3SegReaderNew(
|
|||
}else{
|
||||
pReader->iCurrentBlock = iStartLeaf-1;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
*ppReader = pReader;
|
||||
}else{
|
||||
sqlite3Fts3SegReaderFree(pReader);
|
||||
}
|
||||
return rc;
|
||||
*ppReader = pReader;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1470,6 +1464,7 @@ int sqlite3Fts3SegReaderPending(
|
|||
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
|
||||
){
|
||||
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
|
||||
Fts3HashElem *pE; /* Iterator variable */
|
||||
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
|
||||
int nElem = 0; /* Size of array at aElem */
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
|
@ -1478,7 +1473,6 @@ int sqlite3Fts3SegReaderPending(
|
|||
pHash = &p->aIndex[iIndex].hPending;
|
||||
if( bPrefix ){
|
||||
int nAlloc = 0; /* Size of allocated array at aElem */
|
||||
Fts3HashElem *pE = 0; /* Iterator variable */
|
||||
|
||||
for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
|
||||
char *zKey = (char *)fts3HashKey(pE);
|
||||
|
@ -1512,8 +1506,13 @@ int sqlite3Fts3SegReaderPending(
|
|||
|
||||
}else{
|
||||
/* The query is a simple term lookup that matches at most one term in
|
||||
** the index. All that is required is a straight hash-lookup. */
|
||||
Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
|
||||
** the index. All that is required is a straight hash-lookup.
|
||||
**
|
||||
** Because the stack address of pE may be accessed via the aElem pointer
|
||||
** below, the "Fts3HashElem *pE" must be declared so that it is valid
|
||||
** within this entire function, not just this "else{...}" block.
|
||||
*/
|
||||
pE = fts3HashFindElem(pHash, zTerm, nTerm);
|
||||
if( pE ){
|
||||
aElem = &pE;
|
||||
nElem = 1;
|
||||
|
|
|
@ -1193,7 +1193,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
|
|||
int nBlob;
|
||||
|
||||
/* Check that value is actually a blob. */
|
||||
if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR;
|
||||
if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR;
|
||||
|
||||
/* Check that the blob is roughly the right size. */
|
||||
nBlob = sqlite3_value_bytes(pValue);
|
||||
|
|
247
manifest
247
manifest
|
@ -1,12 +1,12 @@
|
|||
C Version\s3.7.9
|
||||
D 2011-11-01T00:52:41.132
|
||||
C Version\s3.7.10
|
||||
D 2012-01-16T13:28:40.069
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in a162fe39e249b8ed4a65ee947c30152786cfe897
|
||||
F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc dcad80fa69f17d46fe6778ba873fc108ca16298d
|
||||
F Makefile.vxworks 1deb39c8bb047296c30161ffa10c1b5423e632f9
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION bb37c274b503bbe73f00ea4f374eb817cba4b171
|
||||
F VERSION af03cd6400f9d71d38bdb7a9d66a1aefdc2f3e0d
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
|
||||
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
|
||||
|
@ -21,10 +21,10 @@ F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
|||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 405a958bdb3af382a809dccb08a44694923ddd61
|
||||
F config.h.in 31cc8c4943f56e60c4aa4fba929c9d4c70e418b4
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 806c06aef5895860da49600ce098dbe4d5a8435c x
|
||||
F configure.ac 298a759c086e72c013da459c2aec02a104f4224f
|
||||
F configure 5c1cf496e63a233d2abcccf7a3570ba84d5dce01 x
|
||||
F configure.ac 75323bdac56fb0e69f6a3fc5b23f24359550b9d9
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549
|
||||
F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e
|
||||
|
@ -63,7 +63,7 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
|
|||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 246ef2d0cef67517d156d39c9247cd6c432f0d79
|
||||
F ext/fts3/fts3.c bd570b99f1f65b17d587361a421d7f2f28082aa0
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h def7a900f98c5ab5fa4772e922bfa219d5097f05
|
||||
F ext/fts3/fts3_aux.c 0ebfa7b86cf8ff6a0861605fcc63b83ec1b70691
|
||||
|
@ -71,21 +71,21 @@ F ext/fts3/fts3_expr.c f5df26bddf46a5916b2a5f80c4027996e92b7b15
|
|||
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
|
||||
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa
|
||||
F ext/fts3/fts3_porter.c 8d946908f4812c005d3d33fcbe78418b1f4eb70c
|
||||
F ext/fts3/fts3_porter.c b7e5276f9f0a5fc7018b6fa55ce0f31f269ef881
|
||||
F ext/fts3/fts3_snippet.c 1f9ee6a8e0e242649645968dcec4deb253d86c2a
|
||||
F ext/fts3/fts3_term.c a5457992723455a58804cb75c8cbd8978db5c2ef
|
||||
F ext/fts3/fts3_test.c 24fa13f330db011500acb95590da9eee24951894
|
||||
F ext/fts3/fts3_tokenizer.c 9ff7ec66ae3c5c0340fa081958e64f395c71a106
|
||||
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
|
||||
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
|
||||
F ext/fts3/fts3_write.c c097228bff4d33c6b8a270c9717b9f8339068776
|
||||
F ext/fts3/fts3_write.c fdf0c99830360146ec7128150271c8c014a8fef7
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 692e9192d148f318b3dca9f744600346a175eedd
|
||||
F ext/rtree/rtree.c b92ab2e91e35c4964644647322813419c65fe1ce
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
|
@ -119,82 +119,82 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
|||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
|
||||
F src/analyze.c 5a1db16a651ce6310c8b046b2cbb736e030e14b9
|
||||
F src/analyze.c f32ff304da413851eefa562b04e61ff6cb88248b
|
||||
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 4368158da74d4711888e03264105c5c527d76caf
|
||||
F src/backup.c e9538bad2d4a4fcd4308f1aed7cb18a0fbc968f9
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 32199e2d939233ade25340eaba450f818b37c079
|
||||
F src/btree.c a65816cc000bdd421772986e64c88c9035331332
|
||||
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
|
||||
F src/btreeInt.h 67978c014fa4f7cc874032dd3aacadd8db656bc3
|
||||
F src/build.c 8af67a08a852ff4c63701963cb1ab7166f577814
|
||||
F src/btreeInt.h 6c9960645c431c9456ca56498f43a2b3bf1fa8c2
|
||||
F src/build.c 8e2a4dedad860fed982270ef43968505f35ec57f
|
||||
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c ff68e5ef23aee08c0ff528f699a19397ed8bbed8
|
||||
F src/expr.c fbf116f90cabc917ae50bba24a73a0b55519a0c8
|
||||
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
|
||||
F src/expr.c 537591e95eac74af783e4eb033954fb218cf398e
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
|
||||
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
|
||||
F src/global.c e230227de13601714b29f9363028514aada5ae2f
|
||||
F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c ca18783512323f74aaf4ee74b46ffd75ec80d031
|
||||
F src/insert.c d7c69718acbb92e10e4b121da7bed13903342962
|
||||
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
|
||||
F src/loadext.c d0d2022a5a07274d408820b978b9e549189d314f
|
||||
F src/main.c df06f5229b8046f85dde253dfd7fe35ae9e4902e
|
||||
F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
|
||||
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
|
||||
F src/main.c e60abee4a7ca3da31b67745ccf02b8d29f138614
|
||||
F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 00bd8265c81abb665c48fea1e0c234eb3b922206
|
||||
F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
|
||||
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
|
||||
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
|
||||
F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
|
||||
F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6
|
||||
F src/mutex.c 6949180803ff05a7d0e2b9334a95b4fb5a00e23f
|
||||
F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc
|
||||
F src/mutex.h 2a79e0c10c26412546b501ee0f3d92b42decf63e
|
||||
F src/mutex_noop.c d5cfbca87168c661a0b118cd8e329a908e453151
|
||||
F src/mutex_noop.c 7682796b7d8d39bf1c138248858efcd10c9e1553
|
||||
F src/mutex_os2.c 882d735098c07c8c6a5472b8dd66e19675fe117f
|
||||
F src/mutex_unix.c b4f4e923bb8de93ec3f251fadb50855f23df9579
|
||||
F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc
|
||||
F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33
|
||||
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
|
||||
F src/os.c 5d9b02782ed36345348d6fe21d7762ed3a9cfd2a
|
||||
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
|
||||
F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c
|
||||
F src/os.h a2219c3b05ce31230bb000fdc4f1a542b33ee649
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
|
||||
F src/os_unix.c ddda0b1c5ae536669634d7bff31b3f8f4d654866
|
||||
F src/os_win.c 49d418916428a59d773f39993db0ecde56ab4c37
|
||||
F src/pager.c ad62daa0c21e27ae332b3ceb4f579a2a97046ddc
|
||||
F src/pager.h 9f81b08efb06db4ba8be69446e10b005c351373d
|
||||
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
|
||||
F src/pcache.c 49e718c095810c6b3334e3a6d89970aceaddefce
|
||||
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
|
||||
F src/pcache1.c 24f5e85a78514584b46190260ba7ab0a66312197
|
||||
F src/pragma.c da8ef96b3eec351e81e0061c39810e548bcc96d7
|
||||
F src/prepare.c e64261559a3187698a3e7e6c8b001a4f4f98dab4
|
||||
F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
|
||||
F src/os_unix.c 657672fab2580a84116c140b36ee3d6b6fc75b4e
|
||||
F src/os_win.c 5ac061ae1326a71500cee578ed0fd9113b4f6a37
|
||||
F src/pager.c 4d58c983d9f4d34bc2d48e4280361ccaeecd03c5
|
||||
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
|
||||
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f
|
||||
F src/pcache1.c 281822d22265245b19f908cb3f5df725f7e11b06
|
||||
F src/pragma.c fb979b7b5103ad0db1b72bcf349c83f5dec62574
|
||||
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
|
||||
F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
|
||||
F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c 80f3ac44a8514b1d107b80f5df4a424ae059d2b6
|
||||
F src/shell.c f0ab793261ab045a0b8c47fa2707e8a894d2898f
|
||||
F src/sqlite.h.in ff950aef7b378963c67add42dda5d446a0b7330e
|
||||
F src/select.c a1d075db66a0ea42807353501b62997969e5be79
|
||||
F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d
|
||||
F src/sqlite.h.in 53516617d2945a411d028674d7fa20dd394b9ec0
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h c74457cd2c4bd77683bac76e698bf2ec2d3e13f9
|
||||
F src/sqliteInt.h b8fdd9c39c8d7f5c794f4ea917293d9c75b9aff2
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c de581e2e71f5e7f98366156afad83b4742ac6fe0
|
||||
F src/test1.c 0f41b7c67719207a5de24b009e172c4dcf189827
|
||||
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
|
||||
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
|
||||
F src/tclsqlite.c 2aeb69958965dad0842d5ea1456f1a958ef296e6
|
||||
F src/test1.c 1b1e514e85ffe7152b02cba38bd0a1ce8cd56113
|
||||
F src/test2.c 711555927f1f7e8db9aab86b512bc6934a774abe
|
||||
F src/test3.c 91d3f1a09cfae3533ef17d8b484a160f3d1f1a21
|
||||
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
|
||||
F src/test5.c e1a19845625144caf038031234a12185e40d315c
|
||||
F src/test6.c 3191b4ab964a144230ff9ef96c093520375c7b2a
|
||||
F src/test6.c cf6ab27a59e1ab64b011bb251ba600131e803e59
|
||||
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
|
||||
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
|
@ -202,57 +202,58 @@ F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
|
|||
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
|
||||
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
|
||||
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
|
||||
F src/test_config.c bc8826296a7b3a86eeaba1ac2af5551d1c20c35b
|
||||
F src/test_config.c a036a69b550ebc477ab9ca2b37269201f888436e
|
||||
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
|
||||
F src/test_func.c 6232d722a4ddb193035aa13a03796bf57d6c12fd
|
||||
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
|
||||
F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
|
||||
F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c
|
||||
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
|
||||
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
|
||||
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
|
||||
F src/test_journal.c 03313c693cca72959dcaaf79f8d76f21c01e19ff
|
||||
F src/test_journal.c a6a6baf343f79b942331f13378d045e7e270ae64
|
||||
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
|
||||
F src/test_malloc.c 8d416f29ad8573f32601f6056c9d2b17472e9ad5
|
||||
F src/test_multiplex.c 3fc368022c46fe44ec22c5e1ed727223a54a6a1d
|
||||
F src/test_multiplex.c afab2c9d292677ab31e0dd4b3dde2420fb655c5f
|
||||
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
|
||||
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
|
||||
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
|
||||
F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32
|
||||
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
|
||||
F src/test_quota.c a391c866217e92986c6f523f05b08aa6956c8419
|
||||
F src/test_osinst.c 6abf0a37ce831120c4ef1b913afdd813e7ac1a73
|
||||
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
|
||||
F src/test_quota.c b4a6519417d87870e7ef5838dbf3cae164dcc28d
|
||||
F src/test_quota.h 9ffa1d3ad6d0a6a24e8670ea64b909c717ec3358
|
||||
F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
|
||||
F src/test_stat.c 69de4361c7a69fc1136d31ab7144408cd00805c7
|
||||
F src/test_stat.c 80271ad7d776a79babe0e025bb3a1bfcd3a3cfb1
|
||||
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
|
||||
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
F src/test_thread.c 35022393dd54d147b998b6b7f7e945b01114d666
|
||||
F src/test_vfs.c b0baec983bd6f872715a4b44c8f39104fec333af
|
||||
F src/test_vfstrace.c 0b884e06094a746da729119a2cabdc7aa790063d
|
||||
F src/test_vfs.c 07157a0bbfe161cb5e32cad2079abd26cd611c4b
|
||||
F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823
|
||||
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c c819d9f72168a035d545a5bdafe9b085b20df705
|
||||
F src/trigger.c 1cfb80e2290ef66ea89cb4e821caae65a02c0d56
|
||||
F src/update.c 25e046a8f69d5e557aabde2000487b8545509d8d
|
||||
F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
|
||||
F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
|
||||
F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
|
||||
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
|
||||
F src/util.c df83983bd57057df4951516880066b42b7055269
|
||||
F src/util.c 9e07bd67dfafe9c75b1da78c87ba030cebbb5388
|
||||
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
|
||||
F src/vdbe.c 326994a64a9a08853122200dc9f62cb96b8f0831
|
||||
F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
|
||||
F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
|
||||
F src/vdbeapi.c 4dbba7f94f127f6ea8d2d0505ee1f98e5ffbf546
|
||||
F src/vdbeaux.c a950e34449a508d48d90475acc287943a4094f3a
|
||||
F src/vdbe.c 64e3fc0bb56c54ceac3cc1d1e65257bfda21550e
|
||||
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
|
||||
F src/vdbeInt.h ef9b8584b23b033894a0804dc6b90196c6779fb9
|
||||
F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91
|
||||
F src/vdbeaux.c eb13a6917ed7455b5b49236fe5cfb3d3c3e4c57b
|
||||
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
|
||||
F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
|
||||
F src/vdbemem.c 4f7d25d5ea2e2040254095b8f6de07f8dbbadf80
|
||||
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
|
||||
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
|
||||
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
|
||||
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
|
||||
F src/wal.c 9658df8d404b82e6b2d40fd05944463214e2d935
|
||||
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
|
||||
F src/wal.c 5f7bcc0610af759953defd769eacebfd98a22bc8
|
||||
F src/wal.h eaa00b9a403ddda2b56d01b7afc19ef600f9363f
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c 7c85f4c93058e27100d404f0777aaeb0d1b296ae
|
||||
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
|
@ -274,7 +275,7 @@ F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
|
|||
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
|
||||
F test/async5.test 0dd8701bd588bf6e70c2557a22ae3f22b2567b4c
|
||||
F test/attach.test 0e6f8de2589f11a5f474ef57fe5af2877e61c0e8
|
||||
F test/attach.test 0d112b7713611fdf0340260192749737135fda5f
|
||||
F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966
|
||||
F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e
|
||||
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
|
||||
|
@ -289,13 +290,14 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
|||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
F test/backcompat.test 71eeb75ea567c060774c4e8db4b0e703f21c7677
|
||||
F test/backup.test 6970614b002b056ae5bab5b76559905e02b6f0b2
|
||||
F test/backup2.test b4966934b2dc10a9a6546114566ea69b34a5185e
|
||||
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0
|
||||
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
|
||||
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
|
||||
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103
|
||||
F test/bigfile2.test f8e83eca9abef60692a34255a2ebcb96aff897fc
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
|
||||
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
|
||||
|
@ -315,7 +317,7 @@ F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
|
|||
F test/capi3.test 7200dff6acb17b9a4b6f9918f554eaae04968ddd
|
||||
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test ccf0acf045dbacd09f6229aa4efed670aaba76a9
|
||||
F test/capi3d.test cd36571f014f34bdc4421967f6453cbb597d5d16
|
||||
F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
|
||||
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
|
||||
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
|
||||
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
|
||||
|
@ -347,6 +349,7 @@ F test/corruptB.test 20d4a20cbed23958888c3e8995b424a47223d647
|
|||
F test/corruptC.test 62a767fe64acb1975f58cc6171192839c783edbb
|
||||
F test/corruptD.test 99b1999dbfa7cc04aaeac9d695a2445d4e7c7458
|
||||
F test/corruptE.test 1b9eb20a8711251ce57b44a257e241085b39b52d
|
||||
F test/corruptF.test 984b1706c9c0e4248141b056c21124612628d12e
|
||||
F test/count.test 454e1ce985c94d13efeac405ce54439f49336163
|
||||
F test/crash.test 519dc29f6fea151f015a23236e555239353946eb
|
||||
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
|
||||
|
@ -361,7 +364,7 @@ F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
|
|||
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
|
||||
F test/dbstatus.test 9eb484ba837c6f3f9bbcaecc29e6060a8c3ba6d2
|
||||
F test/dbstatus.test 3e978f8bdb2362a36a4be63c36a59f542c4cc2a3
|
||||
F test/dbstatus2.test dc57b0d9610851c0ff58a8e1b5b191678398b72a
|
||||
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
|
@ -373,21 +376,21 @@ F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
|
|||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test df5b11ad606439129c88720a86787bc9ca181f31
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test 4771686a586b6ae414f927c389b2c101cc05c028
|
||||
F test/e_delete.test e2ae0d3fce5efd70fef99025e932afffc5616fab
|
||||
F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a
|
||||
F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d
|
||||
F test/e_expr.test d93ccded2409c66637dc1649a02f169e041b63d8
|
||||
F test/e_createtable.test 48598b15e8fe6554d301e7b65a10c9851f177e84
|
||||
F test/e_delete.test ec168cd4b08d681e6d5847f462203755ad647532
|
||||
F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235
|
||||
F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e
|
||||
F test/e_expr.test 4e4399006b3d1ab333721b8e386cabb9fb6d5a89
|
||||
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a
|
||||
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
|
||||
F test/e_insert.test 76d4bb5da9b28014d515d91ffe29a79a1e99f2bc
|
||||
F test/e_reindex.test a064f0878b8f848fbca38f1f61f82f15a3000c64
|
||||
F test/e_insert.test 234242b71855af8e8a9b1e141c3533f6d43d8645
|
||||
F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test 8d7fac7a268eaeb80b9a7ba7964505b9d30f5458
|
||||
F test/e_select.test 6f488c22851c1c280ed74e16a9a1430706a3a8cb
|
||||
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
|
||||
F test/e_update.test b926341a65955d69a6375c9eb4fd82e7089bc83a
|
||||
F test/e_update.test dba988a4d079156549a40854074ba4890b0a4577
|
||||
F test/e_uri.test 6f35b491f80dac005c8144f38b2dfb4d96483596
|
||||
F test/e_vacuum.test 6c09c2af7f2f140518f371c5342100118f779dcf
|
||||
F test/e_vacuum.test 5296e25ef871965bac010b9da083dd7e4734526e
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad
|
||||
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
|
@ -513,7 +516,7 @@ F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
|
|||
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
|
||||
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
|
||||
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
|
||||
F test/incrvacuum2.test 62fbeb85459fe4e501684d8fb5b6e98a23e3b0c0
|
||||
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
|
||||
F test/incrvacuum_ioerr.test 22f208d01c528403240e05beecc41dc98ed01637
|
||||
F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
|
||||
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
|
||||
|
@ -525,7 +528,7 @@ F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
|||
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
|
||||
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
|
||||
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
|
||||
F test/insert4.test 63ea672b0fc6d3a9a0ccee774a771510b1e684c4
|
||||
F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
|
||||
F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
|
||||
|
@ -543,7 +546,7 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
|||
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
|
||||
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
|
||||
F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d
|
||||
F test/journal2.test 29937bdbb253bbfd92057610120bdc0aa7e84a0a
|
||||
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
|
||||
F test/journal3.test 6fd28532c88b447db844186bc190523108b6dbb4
|
||||
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
|
||||
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
|
||||
|
@ -570,7 +573,7 @@ F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
|||
F test/malloc.test bc745155ff4252d4f35ec8316625b0dfe2abc659
|
||||
F test/malloc3.test de8eca0c3e748878845fdca3663ec4b642073caf
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test 30dc30b57fa22552eba0d8c95210d96c3d958a39
|
||||
F test/malloc5.test a577cbbcc1594c7763b9b3515b3633555751c7f0
|
||||
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
|
||||
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
|
||||
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
|
||||
|
@ -592,7 +595,7 @@ F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
|
|||
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
|
||||
F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6
|
||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||
F test/memsubsys1.test 16ce163ac1ace3d71bf0eaa6a821ed153addd91f
|
||||
F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
|
||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
|
||||
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
|
||||
|
@ -605,7 +608,9 @@ F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
|
|||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test eafaa41b9133d7a2ded4641bbe5f340731d35a52
|
||||
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
|
||||
F test/multiplex.test 9df8bf738b3b97c718fceb3fadb30900ba494418
|
||||
F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256
|
||||
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
|
||||
F test/multiplex3.test 15903c343f1eaa4b00998b7ceacfc4987e4ccfe9
|
||||
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
|
||||
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
|
||||
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
|
||||
|
@ -616,7 +621,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
|
|||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 1b630b3248c7d28862fe9e190cfe52234b502504
|
||||
F test/pager1.test 9e9f5f1c6d4df4831dbff213b1262ef94bf72118
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
|
||||
|
@ -626,14 +631,16 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
|
|||
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/permutations.test 522823b47238cb1754198f80817fe9f9158ede55
|
||||
F test/pragma.test 1ea0c85be853135bb7468e6eed48ee12b04794d4
|
||||
F test/permutations.test fa6f0e5f13fe0b1d3f7a7613179b7f7b20028184
|
||||
F test/pragma.test 7fa35e53085812dac94c2bfcbb02c2a4ad35df5e
|
||||
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quota.test 1c59a396e8f7b5d8466fa74b59f2aeb778d74f7a
|
||||
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
|
||||
F test/quota.test af47d25c166aa7b33ef25f21bb7f2afb29d82c77
|
||||
F test/quota2.test 1b8df088e604f2df573f96e726b5e518cb0cddaa
|
||||
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
|
||||
|
@ -666,7 +673,7 @@ F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
|
|||
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
|
||||
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
|
||||
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
|
||||
F test/selectB.test 0d072c5846071b569766e6cd7f923f646a8b2bfa
|
||||
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
|
||||
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25
|
||||
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
|
||||
F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48
|
||||
|
@ -678,6 +685,7 @@ F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
|
|||
F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de
|
||||
F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
|
||||
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
|
||||
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
|
||||
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test c16709a16ad79fa43b32929b2e623d1d117ccf53
|
||||
|
@ -697,17 +705,17 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
|
|||
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
|
||||
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/superlock.test 7b1167925e9d30a5d1f0701d24812fdda42c3a86
|
||||
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test 966addf703faee6a5d509abe6d8885e393e552fd
|
||||
F test/syscall.test 265cda616f56a297406728ee1e74c9b4a93aa6dd
|
||||
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
|
||||
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tclsqlite.test 5ebcbb0dccc3fbc1edc3bba84c38e2c2d574c5aa
|
||||
F test/tclsqlite.test 1597d353308531527583481d14d9da52ea8ed0af
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
|
||||
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
|
||||
F test/tester.tcl 0b2999b578964297663de4870babbbee29225622
|
||||
F test/tester.tcl 001051eaf28c1040800f588a64c63e0bd0e1f36b
|
||||
F test/thread001.test 7cc2ce08f9cde95964736d11e91f9ab610f82f91
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
|
@ -727,6 +735,7 @@ F test/tkt-31338dca7e.test 1f714c14b6682c5db715e0bda347926a3456f7a9
|
|||
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
|
||||
F test/tkt-38cb5df375.test 9e9b19857dba0896a8efdaf334d405ba423492f2
|
||||
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
|
||||
F test/tkt-3a77c9714e.test 1675c22a5be71d7fa026e5db5daeeb4dd64f7824
|
||||
F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
|
||||
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
|
||||
F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894
|
||||
|
@ -735,6 +744,7 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
|
|||
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
|
||||
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
|
||||
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
|
||||
F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf
|
||||
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
|
||||
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
|
||||
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
|
||||
|
@ -863,7 +873,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
|||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
||||
F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
|
||||
F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46
|
||||
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
|
||||
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
|
||||
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b
|
||||
|
@ -893,24 +903,25 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
|
|||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
|
||||
F test/wal.test e11da8d5ea8a38a247339455098357e9adf63d76
|
||||
F test/wal2.test ad6412596815f553cd30f271d291ab003092bc7e
|
||||
F test/wal3.test 18da4e65c30c43c646ad40e145e9a074e4062fc9
|
||||
F test/wal.test edefe316b4125d7f68004ea53c5e73c398d436cc
|
||||
F test/wal2.test f11883dd3cb7f647c5d2acfd7b5c6d4ba5770cc9
|
||||
F test/wal3.test 6504bbf348b2d6dfade64a064f1050fd617e8706
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
|
||||
F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437
|
||||
F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
|
||||
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
|
||||
F test/walbig.test 0ab8a430ef420a3114f7092e0f30fc9585ffa155
|
||||
F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
|
||||
F test/walcrash.test 4fcb661faf71db91214156d52d43ee327f52bde1
|
||||
F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2
|
||||
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
|
||||
F test/walfault.test efb0d5724893133e71b8d9d90abdb781845a6bb0
|
||||
F test/walcrash3.test 595e44c6197f0d0aa509fc135be2fd0209d11a2c
|
||||
F test/walfault.test 97394d8de82a99f7abf1c12ed229640607fd0ad2
|
||||
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
|
||||
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
|
||||
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
|
||||
F test/walpersist.test fd40d33765b2693f721c90c66d97f99757559006
|
||||
F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
|
||||
F test/walro.test e6bb27762c9f22601cbb8bff6e0acfd124e74b63
|
||||
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
|
||||
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
|
||||
|
@ -927,9 +938,11 @@ F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
|||
F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2
|
||||
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test 0de750389990b1078bab203c712dc3fefd1d8b82
|
||||
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
|
@ -937,7 +950,7 @@ F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
|||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 949328f67cac94969d3112b105b8457edf27f44e
|
||||
F tool/lemon.c 445f18999b700d83b83a5d9be00c596546c21052
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
|
@ -974,8 +987,8 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
|||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
P 6635cd9a7714b681dd8aa96e90be462a40d10178
|
||||
R b059ff356abfc5b4524a9b548916f43e
|
||||
T +sym-version-3.7.9 *
|
||||
P 9da1cd0a06aca4eb3fb21fa4d321b9e3e07444b4
|
||||
R 81a3b6b323708ac95becda2f7731a3a4
|
||||
T +sym-version-3.7.10 *
|
||||
U drh
|
||||
Z a9ecbb5c487c786a176874d979505217
|
||||
Z 1c2c75389d69d7c8466f72bd2161ada0
|
||||
|
|
|
@ -1 +1 @@
|
|||
c7c6050ef060877ebe77b41d959e9df13f8c9b5e
|
||||
ebd01a8deffb5024a5d7494eef800d2366d97204
|
||||
|
|
|
@ -529,6 +529,7 @@ static void analyzeOneTable(
|
|||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
|
||||
sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
|
||||
(char*)&stat3InitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2);
|
||||
|
|
|
@ -678,7 +678,9 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
|||
pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
|
||||
if( pFd->pMethods ){
|
||||
i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
|
||||
sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
|
||||
rc = sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
|
||||
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
|
||||
if( rc ) goto copy_finished;
|
||||
}
|
||||
|
||||
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
|
||||
|
@ -703,12 +705,13 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
|
|||
assert( b.rc!=SQLITE_OK );
|
||||
rc = sqlite3_backup_finish(&b);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTo->pBt->pageSizeFixed = 0;
|
||||
pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
|
||||
}else{
|
||||
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
|
||||
}
|
||||
|
||||
assert( sqlite3BtreeIsInTrans(pTo)==0 );
|
||||
copy_finished:
|
||||
sqlite3BtreeLeave(pFrom);
|
||||
sqlite3BtreeLeave(pTo);
|
||||
return rc;
|
||||
|
|
164
src/btree.c
164
src/btree.c
|
@ -243,7 +243,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
|
|||
/* If some other connection is holding an exclusive lock, the
|
||||
** requested lock may not be obtained.
|
||||
*/
|
||||
if( pBt->pWriter!=p && pBt->isExclusive ){
|
||||
if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){
|
||||
sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
|
|||
sqlite3ConnectionBlocked(p->db, pIter->pBtree->db);
|
||||
if( eLock==WRITE_LOCK ){
|
||||
assert( p==pBt->pWriter );
|
||||
pBt->isPending = 1;
|
||||
pBt->btsFlags |= BTS_PENDING;
|
||||
}
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
|
|||
** the setSharedCacheTableLock() procedure) held by Btree object p.
|
||||
**
|
||||
** This function assumes that Btree p has an open read or write
|
||||
** transaction. If it does not, then the BtShared.isPending variable
|
||||
** transaction. If it does not, then the BTS_PENDING flag
|
||||
** may be incorrectly cleared.
|
||||
*/
|
||||
static void clearAllSharedCacheTableLocks(Btree *p){
|
||||
|
@ -365,7 +365,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
|||
|
||||
while( *ppIter ){
|
||||
BtLock *pLock = *ppIter;
|
||||
assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
|
||||
assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
|
||||
assert( pLock->pBtree->inTrans>=pLock->eLock );
|
||||
if( pLock->pBtree==p ){
|
||||
*ppIter = pLock->pNext;
|
||||
|
@ -378,22 +378,21 @@ static void clearAllSharedCacheTableLocks(Btree *p){
|
|||
}
|
||||
}
|
||||
|
||||
assert( pBt->isPending==0 || pBt->pWriter );
|
||||
assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter );
|
||||
if( pBt->pWriter==p ){
|
||||
pBt->pWriter = 0;
|
||||
pBt->isExclusive = 0;
|
||||
pBt->isPending = 0;
|
||||
pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
|
||||
}else if( pBt->nTransaction==2 ){
|
||||
/* This function is called when Btree p is concluding its
|
||||
** transaction. If there currently exists a writer, and p is not
|
||||
** that writer, then the number of locks held by connections other
|
||||
** than the writer must be about to drop to zero. In this case
|
||||
** set the isPending flag to 0.
|
||||
** set the BTS_PENDING flag to 0.
|
||||
**
|
||||
** If there is not currently a writer, then BtShared.isPending must
|
||||
** If there is not currently a writer, then BTS_PENDING must
|
||||
** be zero already. So this next line is harmless in that case.
|
||||
*/
|
||||
pBt->isPending = 0;
|
||||
pBt->btsFlags &= ~BTS_PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,8 +404,7 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){
|
|||
if( pBt->pWriter==p ){
|
||||
BtLock *pLock;
|
||||
pBt->pWriter = 0;
|
||||
pBt->isExclusive = 0;
|
||||
pBt->isPending = 0;
|
||||
pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
|
||||
for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
|
||||
assert( pLock->eLock==READ_LOCK || pLock->pBtree==p );
|
||||
pLock->eLock = READ_LOCK;
|
||||
|
@ -859,7 +857,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
|||
** This routine works only for pages that do not contain overflow cells.
|
||||
*/
|
||||
#define findCell(P,I) \
|
||||
((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
|
||||
((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))
|
||||
#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
|
||||
|
||||
|
||||
|
@ -1264,7 +1262,7 @@ static int freeSpace(MemPage *pPage, int start, int size){
|
|||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
assert( size>=0 ); /* Minimum cell size is 4 */
|
||||
|
||||
if( pPage->pBt->secureDelete ){
|
||||
if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){
|
||||
/* Overwrite deleted information with zeros when the secure_delete
|
||||
** option is enabled */
|
||||
memset(&data[start], 0, size);
|
||||
|
@ -1367,6 +1365,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){
|
|||
}else{
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
pPage->max1bytePayload = pBt->max1bytePayload;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -1409,6 +1408,8 @@ static int btreeInitPage(MemPage *pPage){
|
|||
pPage->nOverflow = 0;
|
||||
usableSize = pBt->usableSize;
|
||||
pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
|
||||
pPage->aDataEnd = &data[usableSize];
|
||||
pPage->aCellIdx = &data[cellOffset];
|
||||
top = get2byteNotZero(&data[hdr+5]);
|
||||
pPage->nCell = get2byte(&data[hdr+3]);
|
||||
if( pPage->nCell>MX_CELL(pBt) ){
|
||||
|
@ -1500,7 +1501,7 @@ static void zeroPage(MemPage *pPage, int flags){
|
|||
assert( sqlite3PagerGetData(pPage->pDbPage) == data );
|
||||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
if( pBt->secureDelete ){
|
||||
if( pBt->btsFlags & BTS_SECURE_DELETE ){
|
||||
memset(&data[hdr], 0, pBt->usableSize - hdr);
|
||||
}
|
||||
data[hdr] = (char)flags;
|
||||
|
@ -1512,6 +1513,8 @@ static void zeroPage(MemPage *pPage, int flags){
|
|||
decodeFlags(pPage, flags);
|
||||
pPage->hdrOffset = hdr;
|
||||
pPage->cellOffset = first;
|
||||
pPage->aDataEnd = &data[pBt->usableSize];
|
||||
pPage->aCellIdx = &data[first];
|
||||
pPage->nOverflow = 0;
|
||||
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
|
||||
pPage->maskPage = (u16)(pBt->pageSize - 1);
|
||||
|
@ -1772,7 +1775,12 @@ int sqlite3BtreeOpen(
|
|||
sqlite3_free(p);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
|
||||
rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
|
||||
if( rc ){
|
||||
sqlite3_free(zFullPathname);
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
#if SQLITE_THREADSAFE
|
||||
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
|
||||
sqlite3_mutex_enter(mutexOpen);
|
||||
|
@ -1846,9 +1854,9 @@ int sqlite3BtreeOpen(
|
|||
|
||||
pBt->pCursor = 0;
|
||||
pBt->pPage1 = 0;
|
||||
pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
|
||||
if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY;
|
||||
#ifdef SQLITE_SECURE_DELETE
|
||||
pBt->secureDelete = 1;
|
||||
pBt->btsFlags |= BTS_SECURE_DELETE;
|
||||
#endif
|
||||
pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
|
||||
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|
||||
|
@ -1869,7 +1877,7 @@ int sqlite3BtreeOpen(
|
|||
nReserve = 0;
|
||||
}else{
|
||||
nReserve = zDbHeader[20];
|
||||
pBt->pageSizeFixed = 1;
|
||||
pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);
|
||||
pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
|
||||
|
@ -2157,7 +2165,7 @@ int sqlite3BtreeSyncDisabled(Btree *p){
|
|||
** If parameter nReserve is less than zero, then the number of reserved
|
||||
** bytes per page is left unchanged.
|
||||
**
|
||||
** If the iFix!=0 then the pageSizeFixed flag is set so that the page size
|
||||
** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
|
||||
** and autovacuum mode can no longer be changed.
|
||||
*/
|
||||
int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
||||
|
@ -2165,7 +2173,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
|||
BtShared *pBt = p->pBt;
|
||||
assert( nReserve>=-1 && nReserve<=255 );
|
||||
sqlite3BtreeEnter(p);
|
||||
if( pBt->pageSizeFixed ){
|
||||
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
|
||||
sqlite3BtreeLeave(p);
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
|
@ -2182,7 +2190,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
|||
}
|
||||
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
|
||||
pBt->usableSize = pBt->pageSize - (u16)nReserve;
|
||||
if( iFix ) pBt->pageSizeFixed = 1;
|
||||
if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
|
@ -2222,8 +2230,8 @@ int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
|
|||
}
|
||||
|
||||
/*
|
||||
** Set the secureDelete flag if newFlag is 0 or 1. If newFlag is -1,
|
||||
** then make no changes. Always return the value of the secureDelete
|
||||
** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1,
|
||||
** then make no changes. Always return the value of the BTS_SECURE_DELETE
|
||||
** setting after the change.
|
||||
*/
|
||||
int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
|
||||
|
@ -2231,9 +2239,10 @@ int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
|
|||
if( p==0 ) return 0;
|
||||
sqlite3BtreeEnter(p);
|
||||
if( newFlag>=0 ){
|
||||
p->pBt->secureDelete = (newFlag!=0) ? 1 : 0;
|
||||
p->pBt->btsFlags &= ~BTS_SECURE_DELETE;
|
||||
if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE;
|
||||
}
|
||||
b = p->pBt->secureDelete;
|
||||
b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0;
|
||||
sqlite3BtreeLeave(p);
|
||||
return b;
|
||||
}
|
||||
|
@ -2254,7 +2263,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
|
|||
u8 av = (u8)autoVacuum;
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){
|
||||
if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else{
|
||||
pBt->autoVacuum = av ?1:0;
|
||||
|
@ -2328,14 +2337,14 @@ static int lockBtree(BtShared *pBt){
|
|||
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
if( page1[18]>1 ){
|
||||
pBt->readOnly = 1;
|
||||
pBt->btsFlags |= BTS_READ_ONLY;
|
||||
}
|
||||
if( page1[19]>1 ){
|
||||
goto page1_init_failed;
|
||||
}
|
||||
#else
|
||||
if( page1[18]>2 ){
|
||||
pBt->readOnly = 1;
|
||||
pBt->btsFlags |= BTS_READ_ONLY;
|
||||
}
|
||||
if( page1[19]>2 ){
|
||||
goto page1_init_failed;
|
||||
|
@ -2349,7 +2358,7 @@ static int lockBtree(BtShared *pBt){
|
|||
** may not be the latest version - there may be a newer one in the log
|
||||
** file.
|
||||
*/
|
||||
if( page1[19]==2 && pBt->doNotUseWAL==0 ){
|
||||
if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
|
||||
int isOpen = 0;
|
||||
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -2426,6 +2435,11 @@ static int lockBtree(BtShared *pBt){
|
|||
pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
|
||||
pBt->maxLeaf = (u16)(pBt->usableSize - 35);
|
||||
pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
|
||||
if( pBt->maxLocal>127 ){
|
||||
pBt->max1bytePayload = 127;
|
||||
}else{
|
||||
pBt->max1bytePayload = (u8)pBt->maxLocal;
|
||||
}
|
||||
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
|
||||
pBt->pPage1 = pPage1;
|
||||
pBt->nPage = nPage;
|
||||
|
@ -2489,7 +2503,7 @@ static int newDatabase(BtShared *pBt){
|
|||
data[23] = 32;
|
||||
memset(&data[24], 0, 100-24);
|
||||
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
|
||||
pBt->pageSizeFixed = 1;
|
||||
pBt->btsFlags |= BTS_PAGESIZE_FIXED;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );
|
||||
assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 );
|
||||
|
@ -2553,7 +2567,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
}
|
||||
|
||||
/* Write transactions are not possible on a read-only database */
|
||||
if( pBt->readOnly && wrflag ){
|
||||
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
|
||||
rc = SQLITE_READONLY;
|
||||
goto trans_begun;
|
||||
}
|
||||
|
@ -2563,7 +2577,9 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
** on this shared-btree structure and a second write transaction is
|
||||
** requested, return SQLITE_LOCKED.
|
||||
*/
|
||||
if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){
|
||||
if( (wrflag && pBt->inTransaction==TRANS_WRITE)
|
||||
|| (pBt->btsFlags & BTS_PENDING)!=0
|
||||
){
|
||||
pBlock = pBt->pWriter->db;
|
||||
}else if( wrflag>1 ){
|
||||
BtLock *pIter;
|
||||
|
@ -2587,7 +2603,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
|
||||
if( SQLITE_OK!=rc ) goto trans_begun;
|
||||
|
||||
pBt->initiallyEmpty = (u8)(pBt->nPage==0);
|
||||
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
|
||||
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
|
||||
do {
|
||||
/* Call lockBtree() until either pBt->pPage1 is populated or
|
||||
** lockBtree() returns something other than SQLITE_OK. lockBtree()
|
||||
|
@ -2599,7 +2616,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );
|
||||
|
||||
if( rc==SQLITE_OK && wrflag ){
|
||||
if( pBt->readOnly ){
|
||||
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
|
||||
rc = SQLITE_READONLY;
|
||||
}else{
|
||||
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
|
||||
|
@ -2636,7 +2653,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
|||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
assert( !pBt->pWriter );
|
||||
pBt->pWriter = p;
|
||||
pBt->isExclusive = (u8)(wrflag>1);
|
||||
pBt->btsFlags &= ~BTS_EXCLUSIVE;
|
||||
if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE;
|
||||
#endif
|
||||
|
||||
/* If the db-size header field is incorrect (as it may be if an old
|
||||
|
@ -3365,7 +3383,7 @@ int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
|
|||
BtShared *pBt = p->pBt;
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
assert( pBt->readOnly==0 );
|
||||
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
assert( iStatement>0 );
|
||||
assert( iStatement>p->db->nSavepoint );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
|
@ -3400,7 +3418,9 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
|||
sqlite3BtreeEnter(p);
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0;
|
||||
if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){
|
||||
pBt->nPage = 0;
|
||||
}
|
||||
rc = newDatabase(pBt);
|
||||
pBt->nPage = get4byte(28 + pBt->pPage1->aData);
|
||||
|
||||
|
@ -3470,7 +3490,7 @@ static int btreeCursor(
|
|||
assert( wrFlag==0 || p->inTrans==TRANS_WRITE );
|
||||
assert( pBt->pPage1 && pBt->pPage1->aData );
|
||||
|
||||
if( NEVER(wrFlag && pBt->readOnly) ){
|
||||
if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
if( iTable==1 && btreePagecount(pBt)==0 ){
|
||||
|
@ -3970,7 +3990,7 @@ static int accessPayload(
|
|||
u8 aSave[4];
|
||||
u8 *aWrite = &pBuf[-4];
|
||||
memcpy(aSave, aWrite, 4);
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1));
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
|
||||
nextPage = get4byte(aWrite);
|
||||
memcpy(aWrite, aSave, 4);
|
||||
}else
|
||||
|
@ -4174,7 +4194,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#if 0
|
||||
/*
|
||||
** Page pParent is an internal (non-leaf) tree page. This function
|
||||
** asserts that page number iChild is the left-child if the iIdx'th
|
||||
|
@ -4207,11 +4227,21 @@ static void moveToParent(BtCursor *pCur){
|
|||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->iPage>0 );
|
||||
assert( pCur->apPage[pCur->iPage] );
|
||||
|
||||
/* UPDATE: It is actually possible for the condition tested by the assert
|
||||
** below to be untrue if the database file is corrupt. This can occur if
|
||||
** one cursor has modified page pParent while a reference to it is held
|
||||
** by a second cursor. Which can only happen if a single page is linked
|
||||
** into more than one b-tree structure in a corrupt database. */
|
||||
#if 0
|
||||
assertParentIndex(
|
||||
pCur->apPage[pCur->iPage-1],
|
||||
pCur->aiIdx[pCur->iPage-1],
|
||||
pCur->apPage[pCur->iPage]->pgno
|
||||
);
|
||||
#endif
|
||||
testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
|
||||
|
||||
releasePage(pCur->apPage[pCur->iPage]);
|
||||
pCur->iPage--;
|
||||
pCur->info.nSize = 0;
|
||||
|
@ -4550,16 +4580,21 @@ int sqlite3BtreeMovetoUnpacked(
|
|||
** 2 bytes of the cell.
|
||||
*/
|
||||
int nCell = pCell[0];
|
||||
if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
|
||||
if( nCell<=pPage->max1bytePayload
|
||||
/* && (pCell+nCell)<pPage->aDataEnd */
|
||||
){
|
||||
/* This branch runs if the record-size field of the cell is a
|
||||
** single byte varint and the record fits entirely on the main
|
||||
** b-tree page. */
|
||||
testcase( pCell+nCell+1==pPage->aDataEnd );
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
|
||||
}else if( !(pCell[1] & 0x80)
|
||||
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
|
||||
/* && (pCell+nCell+2)<=pPage->aDataEnd */
|
||||
){
|
||||
/* The record-size field is a 2 byte varint and the record
|
||||
** fits entirely on the main b-tree page. */
|
||||
testcase( pCell+nCell+2==pPage->aDataEnd );
|
||||
c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
|
||||
}else{
|
||||
/* The record flows over onto one or more overflow pages. In
|
||||
|
@ -4676,7 +4711,13 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
|
|||
pPage = pCur->apPage[pCur->iPage];
|
||||
idx = ++pCur->aiIdx[pCur->iPage];
|
||||
assert( pPage->isInit );
|
||||
assert( idx<=pPage->nCell );
|
||||
|
||||
/* If the database file is corrupt, it is possible for the value of idx
|
||||
** to be invalid here. This can only occur if a second cursor modifies
|
||||
** the page while cursor pCur is holding a reference to it. Which can
|
||||
** only happen if the database is corrupt in such a way as to link the
|
||||
** page into more than one b-tree structure. */
|
||||
testcase( idx>pPage->nCell );
|
||||
|
||||
pCur->info.nSize = 0;
|
||||
pCur->validNKey = 0;
|
||||
|
@ -5101,7 +5142,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
|||
nFree = get4byte(&pPage1->aData[36]);
|
||||
put4byte(&pPage1->aData[36], nFree+1);
|
||||
|
||||
if( pBt->secureDelete ){
|
||||
if( pBt->btsFlags & BTS_SECURE_DELETE ){
|
||||
/* If the secure_delete option is enabled, then
|
||||
** always fully overwrite deleted information with zeros.
|
||||
*/
|
||||
|
@ -5162,7 +5203,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
|
|||
if( rc==SQLITE_OK ){
|
||||
put4byte(&pTrunk->aData[4], nLeaf+1);
|
||||
put4byte(&pTrunk->aData[8+nLeaf*4], iPage);
|
||||
if( pPage && !pBt->secureDelete ){
|
||||
if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){
|
||||
sqlite3PagerDontWrite(pPage->pDbPage);
|
||||
}
|
||||
rc = btreeSetHasContent(pBt, iPage);
|
||||
|
@ -5454,7 +5495,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
|||
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
data = pPage->aData;
|
||||
ptr = &data[pPage->cellOffset + 2*idx];
|
||||
ptr = &pPage->aCellIdx[2*idx];
|
||||
pc = get2byte(ptr);
|
||||
hdr = pPage->hdrOffset;
|
||||
testcase( pc==get2byte(&data[hdr+5]) );
|
||||
|
@ -5468,7 +5509,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
|||
*pRC = rc;
|
||||
return;
|
||||
}
|
||||
endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
|
||||
endPtr = &pPage->aCellIdx[2*pPage->nCell - 2];
|
||||
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
|
||||
while( ptr<endPtr ){
|
||||
*(u16*)ptr = *(u16*)&ptr[2];
|
||||
|
@ -5610,7 +5651,7 @@ static void assemblePage(
|
|||
assert( pPage->nCell==0 );
|
||||
assert( get2byteNotZero(&data[hdr+5])==nUsable );
|
||||
|
||||
pCellptr = &data[pPage->cellOffset + nCell*2];
|
||||
pCellptr = &pPage->aCellIdx[nCell*2];
|
||||
cellbody = nUsable;
|
||||
for(i=nCell-1; i>=0; i--){
|
||||
u16 sz = aSize[i];
|
||||
|
@ -6003,7 +6044,7 @@ static int balance_nonroot(
|
|||
** In this case, temporarily copy the cell into the aOvflSpace[]
|
||||
** buffer. It will be copied out again as soon as the aSpace[] buffer
|
||||
** is allocated. */
|
||||
if( pBt->secureDelete ){
|
||||
if( pBt->btsFlags & BTS_SECURE_DELETE ){
|
||||
int iOff;
|
||||
|
||||
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
|
||||
|
@ -6188,8 +6229,14 @@ static int balance_nonroot(
|
|||
/* Either we found one or more cells (cntnew[0])>0) or pPage is
|
||||
** a virtual root page. A virtual root page is when the real root
|
||||
** page is page 1 and we are the only child of that page.
|
||||
**
|
||||
** UPDATE: The assert() below is not necessarily true if the database
|
||||
** file is corrupt. The corruption will be detected and reported later
|
||||
** in this procedure so there is no need to act upon it now.
|
||||
*/
|
||||
#if 0
|
||||
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) );
|
||||
#endif
|
||||
|
||||
TRACE(("BALANCE: old: %d %d %d ",
|
||||
apOld[0]->pgno,
|
||||
|
@ -6739,7 +6786,8 @@ int sqlite3BtreeInsert(
|
|||
}
|
||||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly );
|
||||
assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE
|
||||
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
|
||||
|
||||
/* Assert that the caller has been consistent. If this cursor was opened
|
||||
|
@ -6868,7 +6916,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
|||
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
assert( !pBt->readOnly );
|
||||
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
assert( pCur->wrFlag );
|
||||
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
|
||||
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
|
||||
|
@ -6989,7 +7037,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
|
|||
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
assert( !pBt->readOnly );
|
||||
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
|
||||
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
|
||||
|
@ -7363,7 +7411,9 @@ void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
|
|||
/* If auto-vacuum is disabled in this build and this is an auto-vacuum
|
||||
** database, mark the database as read-only. */
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1;
|
||||
if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){
|
||||
pBt->btsFlags |= BTS_READ_ONLY;
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3BtreeLeave(p);
|
||||
|
@ -8163,7 +8213,8 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
|
|||
if( !pCsr->wrFlag ){
|
||||
return SQLITE_READONLY;
|
||||
}
|
||||
assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
|
||||
assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0
|
||||
&& pCsr->pBt->inTransaction==TRANS_WRITE );
|
||||
assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
|
||||
assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
|
||||
assert( pCsr->apPage[pCsr->iPage]->intKey );
|
||||
|
@ -8203,7 +8254,8 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
|
|||
/* If setting the version fields to 1, do not automatically open the
|
||||
** WAL connection, even if the version fields are currently set to 2.
|
||||
*/
|
||||
pBt->doNotUseWAL = (u8)(iVersion==1);
|
||||
pBt->btsFlags &= ~BTS_NO_WAL;
|
||||
if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
|
||||
|
||||
rc = sqlite3BtreeBeginTrans(pBtree, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -8220,6 +8272,6 @@ int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
|
|||
}
|
||||
}
|
||||
|
||||
pBt->doNotUseWAL = 0;
|
||||
pBt->btsFlags &= ~BTS_NO_WAL;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -277,6 +277,7 @@ struct MemPage {
|
|||
u8 hasData; /* True if this page stores data */
|
||||
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
|
||||
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
|
||||
u8 max1bytePayload; /* min(maxLocal,127) */
|
||||
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
|
||||
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
|
||||
u16 cellOffset; /* Index in aData of first cell pointer */
|
||||
|
@ -289,6 +290,8 @@ struct MemPage {
|
|||
} aOvfl[5];
|
||||
BtShared *pBt; /* Pointer to BtShared that this page is part of */
|
||||
u8 *aData; /* Pointer to disk image of the page data */
|
||||
u8 *aDataEnd; /* One byte past the end of usable data */
|
||||
u8 *aCellIdx; /* The cell index area */
|
||||
DbPage *pDbPage; /* Pager page handle */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
};
|
||||
|
@ -368,7 +371,7 @@ struct Btree {
|
|||
/*
|
||||
** An instance of this object represents a single database file.
|
||||
**
|
||||
** A single database file can be in use as the same time by two
|
||||
** A single database file can be in use at the same time by two
|
||||
** or more database connections. When two or more connections are
|
||||
** sharing the same database file, each connection has it own
|
||||
** private Btree object for the file and each of those Btrees points
|
||||
|
@ -405,17 +408,14 @@ struct BtShared {
|
|||
sqlite3 *db; /* Database connection currently using this Btree */
|
||||
BtCursor *pCursor; /* A list of all open cursors */
|
||||
MemPage *pPage1; /* First page of the database */
|
||||
u8 readOnly; /* True if the underlying file is readonly */
|
||||
u8 pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
u8 secureDelete; /* True if secure_delete is enabled */
|
||||
u8 initiallyEmpty; /* Database is empty at start of transaction */
|
||||
u8 openFlags; /* Flags to sqlite3BtreeOpen() */
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
u8 autoVacuum; /* True if auto-vacuum is enabled */
|
||||
u8 incrVacuum; /* True if incr-vacuum is enabled */
|
||||
#endif
|
||||
u8 inTransaction; /* Transaction state */
|
||||
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
|
||||
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
|
||||
u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
|
||||
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
|
||||
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
|
||||
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
|
||||
|
@ -433,12 +433,21 @@ struct BtShared {
|
|||
BtShared *pNext; /* Next on a list of sharable BtShared structs */
|
||||
BtLock *pLock; /* List of locks held on this shared-btree struct */
|
||||
Btree *pWriter; /* Btree with currently open write transaction */
|
||||
u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
|
||||
u8 isPending; /* If waiting for read-locks to clear */
|
||||
#endif
|
||||
u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values for BtShared.btsFlags
|
||||
*/
|
||||
#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */
|
||||
#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */
|
||||
#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */
|
||||
#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */
|
||||
#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */
|
||||
#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */
|
||||
#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */
|
||||
|
||||
/*
|
||||
** An instance of the following structure is used to hold information
|
||||
** about a cell. The parseCellPtr() function fills in this structure
|
||||
|
@ -474,7 +483,7 @@ struct CellInfo {
|
|||
** The entry is identified by its MemPage and the index in
|
||||
** MemPage.aCell[] of the entry.
|
||||
**
|
||||
** A single database file can shared by two more database connections,
|
||||
** A single database file can be shared by two more database connections,
|
||||
** but cursors cannot be shared. Each cursor is associated with a
|
||||
** particular database connection identified BtCursor.pBtree.db.
|
||||
**
|
||||
|
@ -635,7 +644,7 @@ struct IntegrityCk {
|
|||
};
|
||||
|
||||
/*
|
||||
** Read or write a two- and four-byte big-endian integer values.
|
||||
** Routines to read or write a two- and four-byte big-endian integer values.
|
||||
*/
|
||||
#define get2byte(x) ((x)[0]<<8 | (x)[1])
|
||||
#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
|
||||
|
|
22
src/build.c
22
src/build.c
|
@ -2661,19 +2661,23 @@ Index *sqlite3CreateIndex(
|
|||
nName = sqlite3Strlen30(zName);
|
||||
nCol = pList->nExpr;
|
||||
pIndex = sqlite3DbMallocZero(db,
|
||||
sizeof(Index) + /* Index structure */
|
||||
sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(int)*nCol + /* Index.aiColumn */
|
||||
sizeof(char *)*nCol + /* Index.azColl */
|
||||
sizeof(u8)*nCol + /* Index.aSortOrder */
|
||||
nName + 1 + /* Index.zName */
|
||||
nExtra /* Collation sequence names */
|
||||
ROUND8(sizeof(Index)) + /* Index structure */
|
||||
ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */
|
||||
sizeof(char *)*nCol + /* Index.azColl */
|
||||
sizeof(int)*nCol + /* Index.aiColumn */
|
||||
sizeof(u8)*nCol + /* Index.aSortOrder */
|
||||
nName + 1 + /* Index.zName */
|
||||
nExtra /* Collation sequence names */
|
||||
);
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->aiRowEst = (tRowcnt*)(&pIndex[1]);
|
||||
pIndex->azColl = (char**)(&pIndex->aiRowEst[nCol+1]);
|
||||
zExtra = (char*)pIndex;
|
||||
pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))];
|
||||
pIndex->azColl = (char**)
|
||||
((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1));
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
|
||||
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
|
||||
|
|
|
@ -148,7 +148,6 @@ Expr *sqlite3LimitWhere(
|
|||
*/
|
||||
if( pOrderBy && (pLimit == 0) ) {
|
||||
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
|
||||
pParse->parseError = 1;
|
||||
goto limit_where_cleanup_2;
|
||||
}
|
||||
|
||||
|
|
298
src/expr.c
298
src/expr.c
|
@ -870,7 +870,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
|
|||
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
|
||||
pItem->sortOrder = pOldItem->sortOrder;
|
||||
pItem->done = 0;
|
||||
pItem->iCol = pOldItem->iCol;
|
||||
pItem->iOrderByCol = pOldItem->iOrderByCol;
|
||||
pItem->iAlias = pOldItem->iAlias;
|
||||
}
|
||||
return pNew;
|
||||
|
@ -940,7 +940,7 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
|
|||
return pNew;
|
||||
}
|
||||
Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
Select *pNew;
|
||||
Select *pNew, *pPrior;
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
|
||||
if( pNew==0 ) return 0;
|
||||
|
@ -951,7 +951,9 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
|||
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
|
||||
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
|
||||
pNew->op = p->op;
|
||||
pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
|
||||
pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags);
|
||||
if( pPrior ) pPrior->pNext = pNew;
|
||||
pNew->pNext = 0;
|
||||
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
|
||||
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
|
||||
pNew->iLimit = 0;
|
||||
|
@ -1373,6 +1375,15 @@ static int isCandidateForInOpt(Select *p){
|
|||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
/*
|
||||
** Code an OP_Once instruction and allocate space for its flag. Return the
|
||||
** address of the new instruction.
|
||||
*/
|
||||
int sqlite3CodeOnce(Parse *pParse){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
return sqlite3VdbeAddOp1(v, OP_Once, pParse->nOnce++);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used by the implementation of the IN (...) operator.
|
||||
** It's job is to find or create a b-tree structure that may be used
|
||||
|
@ -1433,6 +1444,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
int eType = 0; /* Type of RHS table. IN_INDEX_* */
|
||||
int iTab = pParse->nTab++; /* Cursor of the RHS table */
|
||||
int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
|
||||
assert( pX->op==TK_IN );
|
||||
|
||||
|
@ -1443,7 +1455,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
|
||||
if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
int iCol; /* Index of column <column> */
|
||||
|
@ -1468,10 +1479,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
*/
|
||||
assert(v);
|
||||
if( iCol<0 ){
|
||||
int iMem = ++pParse->nMem;
|
||||
int iAddr;
|
||||
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
|
||||
iAddr = sqlite3CodeOnce(pParse);
|
||||
|
||||
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
|
||||
eType = IN_INDEX_ROWID;
|
||||
|
@ -1497,12 +1507,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
|
||||
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
|
||||
){
|
||||
int iMem = ++pParse->nMem;
|
||||
int iAddr;
|
||||
char *pKey;
|
||||
|
||||
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
|
||||
iAddr = sqlite3CodeOnce(pParse);
|
||||
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
|
||||
pKey,P4_KEYINFO_HANDOFF);
|
||||
|
@ -1512,6 +1521,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
sqlite3VdbeJumpHere(v, iAddr);
|
||||
if( prNotFound && !pTab->aCol[iCol].notNull ){
|
||||
*prNotFound = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1527,6 +1537,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
|||
eType = IN_INDEX_EPH;
|
||||
if( prNotFound ){
|
||||
*prNotFound = rMayHaveNull = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
|
||||
}else{
|
||||
testcase( pParse->nQueryLoop>(double)1 );
|
||||
pParse->nQueryLoop = (double)1;
|
||||
|
@ -1599,9 +1610,8 @@ int sqlite3CodeSubselect(
|
|||
** If all of the above are false, then we can run this code just once
|
||||
** save the results, and reuse the same result on subsequent invocations.
|
||||
*/
|
||||
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
|
||||
int mem = ++pParse->nMem;
|
||||
testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
|
||||
if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
|
||||
testAddr = sqlite3CodeOnce(pParse);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
|
@ -2939,6 +2949,264 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
|
|||
return inReg;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression tree.
|
||||
*/
|
||||
void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
|
||||
int op; /* The opcode being coded */
|
||||
const char *zBinOp = 0; /* Binary operator */
|
||||
const char *zUniOp = 0; /* Unary operator */
|
||||
if( pExpr==0 ){
|
||||
op = TK_NULL;
|
||||
}else{
|
||||
op = pExpr->op;
|
||||
}
|
||||
switch( op ){
|
||||
case TK_AGG_COLUMN: {
|
||||
sqlite3ExplainPrintf(pOut, "AGG{%d:%d}",
|
||||
pExpr->iTable, pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_COLUMN: {
|
||||
if( pExpr->iTable<0 ){
|
||||
/* This only happens when coding check constraints */
|
||||
sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn);
|
||||
}else{
|
||||
sqlite3ExplainPrintf(pOut, "{%d:%d}",
|
||||
pExpr->iTable, pExpr->iColumn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_INTEGER: {
|
||||
if( pExpr->flags & EP_IntValue ){
|
||||
sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue);
|
||||
}else{
|
||||
sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
case TK_FLOAT: {
|
||||
sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case TK_STRING: {
|
||||
sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
case TK_NULL: {
|
||||
sqlite3ExplainPrintf(pOut,"NULL");
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
||||
case TK_BLOB: {
|
||||
sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case TK_VARIABLE: {
|
||||
sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)",
|
||||
pExpr->u.zToken, pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_REGISTER: {
|
||||
sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable);
|
||||
break;
|
||||
}
|
||||
case TK_AS: {
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_CAST
|
||||
case TK_CAST: {
|
||||
/* Expressions of the form: CAST(pLeft AS token) */
|
||||
const char *zAff = "unk";
|
||||
switch( sqlite3AffinityType(pExpr->u.zToken) ){
|
||||
case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
|
||||
case SQLITE_AFF_NONE: zAff = "NONE"; break;
|
||||
case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
|
||||
case SQLITE_AFF_INTEGER: zAff = "INTEGER"; break;
|
||||
case SQLITE_AFF_REAL: zAff = "REAL"; break;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_CAST */
|
||||
case TK_LT: zBinOp = "LT"; break;
|
||||
case TK_LE: zBinOp = "LE"; break;
|
||||
case TK_GT: zBinOp = "GT"; break;
|
||||
case TK_GE: zBinOp = "GE"; break;
|
||||
case TK_NE: zBinOp = "NE"; break;
|
||||
case TK_EQ: zBinOp = "EQ"; break;
|
||||
case TK_IS: zBinOp = "IS"; break;
|
||||
case TK_ISNOT: zBinOp = "ISNOT"; break;
|
||||
case TK_AND: zBinOp = "AND"; break;
|
||||
case TK_OR: zBinOp = "OR"; break;
|
||||
case TK_PLUS: zBinOp = "ADD"; break;
|
||||
case TK_STAR: zBinOp = "MUL"; break;
|
||||
case TK_MINUS: zBinOp = "SUB"; break;
|
||||
case TK_REM: zBinOp = "REM"; break;
|
||||
case TK_BITAND: zBinOp = "BITAND"; break;
|
||||
case TK_BITOR: zBinOp = "BITOR"; break;
|
||||
case TK_SLASH: zBinOp = "DIV"; break;
|
||||
case TK_LSHIFT: zBinOp = "LSHIFT"; break;
|
||||
case TK_RSHIFT: zBinOp = "RSHIFT"; break;
|
||||
case TK_CONCAT: zBinOp = "CONCAT"; break;
|
||||
|
||||
case TK_UMINUS: zUniOp = "UMINUS"; break;
|
||||
case TK_UPLUS: zUniOp = "UPLUS"; break;
|
||||
case TK_BITNOT: zUniOp = "BITNOT"; break;
|
||||
case TK_NOT: zUniOp = "NOT"; break;
|
||||
case TK_ISNULL: zUniOp = "ISNULL"; break;
|
||||
case TK_NOTNULL: zUniOp = "NOTNULL"; break;
|
||||
|
||||
case TK_AGG_FUNCTION:
|
||||
case TK_CONST_FUNC:
|
||||
case TK_FUNCTION: {
|
||||
ExprList *pFarg; /* List of function arguments */
|
||||
if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
|
||||
pFarg = 0;
|
||||
}else{
|
||||
pFarg = pExpr->x.pList;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
|
||||
op==TK_AGG_FUNCTION ? "AGG_" : "",
|
||||
pExpr->u.zToken);
|
||||
if( pFarg ){
|
||||
sqlite3ExplainExprList(pOut, pFarg);
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
case TK_EXISTS: {
|
||||
sqlite3ExplainPrintf(pOut, "EXISTS(");
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
break;
|
||||
}
|
||||
case TK_SELECT: {
|
||||
sqlite3ExplainPrintf(pOut, "(");
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
case TK_IN: {
|
||||
sqlite3ExplainPrintf(pOut, "IN(");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
|
||||
}else{
|
||||
sqlite3ExplainExprList(pOut, pExpr->x.pList);
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_SUBQUERY */
|
||||
|
||||
/*
|
||||
** x BETWEEN y AND z
|
||||
**
|
||||
** This is equivalent to
|
||||
**
|
||||
** x>=y AND x<=z
|
||||
**
|
||||
** X is stored in pExpr->pLeft.
|
||||
** Y is stored in pExpr->pList->a[0].pExpr.
|
||||
** Z is stored in pExpr->pList->a[1].pExpr.
|
||||
*/
|
||||
case TK_BETWEEN: {
|
||||
Expr *pX = pExpr->pLeft;
|
||||
Expr *pY = pExpr->x.pList->a[0].pExpr;
|
||||
Expr *pZ = pExpr->x.pList->a[1].pExpr;
|
||||
sqlite3ExplainPrintf(pOut, "BETWEEN(");
|
||||
sqlite3ExplainExpr(pOut, pX);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExpr(pOut, pY);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExpr(pOut, pZ);
|
||||
sqlite3ExplainPrintf(pOut, ")");
|
||||
break;
|
||||
}
|
||||
case TK_TRIGGER: {
|
||||
/* If the opcode is TK_TRIGGER, then the expression is a reference
|
||||
** to a column in the new.* or old.* pseudo-tables available to
|
||||
** trigger programs. In this case Expr.iTable is set to 1 for the
|
||||
** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
|
||||
** is set to the column of the pseudo-table to read, or to -1 to
|
||||
** read the rowid field.
|
||||
*/
|
||||
sqlite3ExplainPrintf(pOut, "%s(%d)",
|
||||
pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
|
||||
break;
|
||||
}
|
||||
case TK_CASE: {
|
||||
sqlite3ExplainPrintf(pOut, "CASE(");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut, ",");
|
||||
sqlite3ExplainExprList(pOut, pExpr->x.pList);
|
||||
break;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
case TK_RAISE: {
|
||||
const char *zType = "unk";
|
||||
switch( pExpr->affinity ){
|
||||
case OE_Rollback: zType = "rollback"; break;
|
||||
case OE_Abort: zType = "abort"; break;
|
||||
case OE_Fail: zType = "fail"; break;
|
||||
case OE_Ignore: zType = "ignore"; break;
|
||||
}
|
||||
sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( zBinOp ){
|
||||
sqlite3ExplainPrintf(pOut,"%s(", zBinOp);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut,",");
|
||||
sqlite3ExplainExpr(pOut, pExpr->pRight);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
}else if( zUniOp ){
|
||||
sqlite3ExplainPrintf(pOut,"%s(", zUniOp);
|
||||
sqlite3ExplainExpr(pOut, pExpr->pLeft);
|
||||
sqlite3ExplainPrintf(pOut,")");
|
||||
}
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
** Generate a human-readable explanation of an expression list.
|
||||
*/
|
||||
void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
|
||||
int i;
|
||||
if( pList==0 || pList->nExpr==0 ){
|
||||
sqlite3ExplainPrintf(pOut, "(empty-list)");
|
||||
return;
|
||||
}else if( pList->nExpr==1 ){
|
||||
sqlite3ExplainExpr(pOut, pList->a[0].pExpr);
|
||||
}else{
|
||||
sqlite3ExplainPush(pOut);
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
|
||||
sqlite3ExplainPush(pOut);
|
||||
sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
|
||||
sqlite3ExplainPop(pOut);
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3ExplainNL(pOut);
|
||||
}
|
||||
}
|
||||
sqlite3ExplainPop(pOut);
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Return TRUE if pExpr is an constant expression that is appropriate
|
||||
** for factoring out of a loop. Appropriate expressions are:
|
||||
|
@ -3762,3 +4030,11 @@ void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
|
|||
pParse->iRangeReg = iReg;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Mark all temporary registers as being unavailable for reuse.
|
||||
*/
|
||||
void sqlite3ClearTempRegCache(Parse *pParse){
|
||||
pParse->nTempReg = 0;
|
||||
pParse->nRangeReg = 0;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
|
|||
500, /* nLookaside */
|
||||
{0,0,0,0,0,0,0,0}, /* m */
|
||||
{0,0,0,0,0,0,0,0,0}, /* mutex */
|
||||
{0,0,0,0,0,0,0,0,0,0,0}, /* pcache */
|
||||
{0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
|
||||
(void*)0, /* pHeap */
|
||||
0, /* nHeap */
|
||||
0, 0, /* mnHeap, mxHeap */
|
||||
|
|
88
src/insert.c
88
src/insert.c
|
@ -47,7 +47,7 @@ void sqlite3OpenTable(
|
|||
** 'd' INTEGER
|
||||
** 'e' REAL
|
||||
**
|
||||
** An extra 'b' is appended to the end of the string to cover the
|
||||
** An extra 'd' is appended to the end of the string to cover the
|
||||
** rowid that appears as the last column in every index.
|
||||
**
|
||||
** Memory for the buffer containing the column index affinity string
|
||||
|
@ -75,7 +75,7 @@ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
|
|||
for(n=0; n<pIdx->nColumn; n++){
|
||||
pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
|
||||
}
|
||||
pIdx->zColAff[n++] = SQLITE_AFF_NONE;
|
||||
pIdx->zColAff[n++] = SQLITE_AFF_INTEGER;
|
||||
pIdx->zColAff[n] = 0;
|
||||
}
|
||||
|
||||
|
@ -239,6 +239,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){
|
|||
memId = p->regCtr;
|
||||
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
|
||||
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
|
||||
addr = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
|
||||
|
@ -1106,7 +1107,7 @@ insert_cleanup:
|
|||
** cause sqlite3_exec() to return immediately
|
||||
** with SQLITE_CONSTRAINT.
|
||||
**
|
||||
** any FAIL Sqlite_exec() returns immediately with a
|
||||
** any FAIL Sqlite3_exec() returns immediately with a
|
||||
** return code of SQLITE_CONSTRAINT. The
|
||||
** transaction is not rolled back and any
|
||||
** prior changes are retained.
|
||||
|
@ -1578,31 +1579,25 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
|
|||
**
|
||||
** INSERT INTO tab1 SELECT * FROM tab2;
|
||||
**
|
||||
** This optimization is only attempted if
|
||||
** The xfer optimization transfers raw records from tab2 over to tab1.
|
||||
** Columns are not decoded and reassemblied, which greatly improves
|
||||
** performance. Raw index records are transferred in the same way.
|
||||
**
|
||||
** (1) tab1 and tab2 have identical schemas including all the
|
||||
** same indices and constraints
|
||||
** The xfer optimization is only attempted if tab1 and tab2 are compatible.
|
||||
** There are lots of rules for determining compatibility - see comments
|
||||
** embedded in the code for details.
|
||||
**
|
||||
** (2) tab1 and tab2 are different tables
|
||||
** This routine returns TRUE if the optimization is guaranteed to be used.
|
||||
** Sometimes the xfer optimization will only work if the destination table
|
||||
** is empty - a factor that can only be determined at run-time. In that
|
||||
** case, this routine generates code for the xfer optimization but also
|
||||
** does a test to see if the destination table is empty and jumps over the
|
||||
** xfer optimization code if the test fails. In that case, this routine
|
||||
** returns FALSE so that the caller will know to go ahead and generate
|
||||
** an unoptimized transfer. This routine also returns FALSE if there
|
||||
** is no chance that the xfer optimization can be applied.
|
||||
**
|
||||
** (3) There must be no triggers on tab1
|
||||
**
|
||||
** (4) The result set of the SELECT statement is "*"
|
||||
**
|
||||
** (5) The SELECT statement has no WHERE, HAVING, ORDER BY, GROUP BY,
|
||||
** or LIMIT clause.
|
||||
**
|
||||
** (6) The SELECT statement is a simple (not a compound) select that
|
||||
** contains only tab2 in its FROM clause
|
||||
**
|
||||
** This method for implementing the INSERT transfers raw records from
|
||||
** tab2 over to tab1. The columns are not decoded. Raw records from
|
||||
** the indices of tab2 are transfered to tab1 as well. In so doing,
|
||||
** the resulting tab1 has much less fragmentation.
|
||||
**
|
||||
** This routine returns TRUE if the optimization is attempted. If any
|
||||
** of the conditions above fail so that the optimization should not
|
||||
** be attempted, then this routine returns FALSE.
|
||||
** This optimization is particularly useful at making VACUUM run faster.
|
||||
*/
|
||||
static int xferOptimization(
|
||||
Parse *pParse, /* Parser context */
|
||||
|
@ -1639,10 +1634,8 @@ static int xferOptimization(
|
|||
}
|
||||
#endif
|
||||
if( onError==OE_Default ){
|
||||
onError = OE_Abort;
|
||||
}
|
||||
if( onError!=OE_Abort && onError!=OE_Rollback ){
|
||||
return 0; /* Cannot do OR REPLACE or OR IGNORE or OR FAIL */
|
||||
if( pDest->iPKey>=0 ) onError = pDest->keyConf;
|
||||
if( onError==OE_Default ) onError = OE_Abort;
|
||||
}
|
||||
assert(pSelect->pSrc); /* allocated even if there is no FROM clause */
|
||||
if( pSelect->pSrc->nSrc!=1 ){
|
||||
|
@ -1748,16 +1741,12 @@ static int xferOptimization(
|
|||
}
|
||||
#endif
|
||||
if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
|
||||
return 0;
|
||||
return 0; /* xfer opt does not play well with PRAGMA count_changes */
|
||||
}
|
||||
|
||||
/* If we get this far, it means either:
|
||||
**
|
||||
** * We can always do the transfer if the table contains an
|
||||
** an integer primary key
|
||||
**
|
||||
** * We can conditionally do the transfer if the destination
|
||||
** table is empty.
|
||||
/* If we get this far, it means that the xfer optimization is at
|
||||
** least a possibility, though it might only work if the destination
|
||||
** table (tab1) is initially empty.
|
||||
*/
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_xferopt_count++;
|
||||
|
@ -1769,16 +1758,23 @@ static int xferOptimization(
|
|||
iDest = pParse->nTab++;
|
||||
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
|
||||
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
|
||||
if( (pDest->iPKey<0 && pDest->pIndex!=0) || destHasUniqueIdx ){
|
||||
/* If tables do not have an INTEGER PRIMARY KEY and there
|
||||
** are indices to be copied and the destination is not empty,
|
||||
** we have to disallow the transfer optimization because the
|
||||
** the rowids might change which will mess up indexing.
|
||||
if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|
||||
|| destHasUniqueIdx /* (2) */
|
||||
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
|
||||
){
|
||||
/* In some circumstances, we are able to run the xfer optimization
|
||||
** only if the destination table is initially empty. This code makes
|
||||
** that determination. Conditions under which the destination must
|
||||
** be empty:
|
||||
**
|
||||
** Or if the destination has a UNIQUE index and is not empty,
|
||||
** we also disallow the transfer optimization because we cannot
|
||||
** insure that all entries in the union of DEST and SRC will be
|
||||
** unique.
|
||||
** (1) There is no INTEGER PRIMARY KEY but there are indices.
|
||||
** (If the destination is not initially empty, the rowid fields
|
||||
** of index entries might need to change.)
|
||||
**
|
||||
** (2) The destination has a unique index. (The xfer optimization
|
||||
** is unable to test uniqueness.)
|
||||
**
|
||||
** (3) onError is something other than OE_Abort and OE_Rollback.
|
||||
*/
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);
|
||||
emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
|
|
|
@ -625,6 +625,7 @@ void sqlite3_reset_auto_extension(void){
|
|||
void sqlite3AutoLoadExtensions(sqlite3 *db){
|
||||
int i;
|
||||
int go = 1;
|
||||
int rc;
|
||||
int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
|
||||
|
||||
wsdAutoextInit;
|
||||
|
@ -647,8 +648,8 @@ void sqlite3AutoLoadExtensions(sqlite3 *db){
|
|||
}
|
||||
sqlite3_mutex_leave(mutex);
|
||||
zErrmsg = 0;
|
||||
if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
|
||||
sqlite3Error(db, SQLITE_ERROR,
|
||||
if( xInit && (rc = xInit(db, &zErrmsg, &sqlite3Apis))!=0 ){
|
||||
sqlite3Error(db, rc,
|
||||
"automatic extension loading failed: %s", zErrmsg);
|
||||
go = 0;
|
||||
}
|
||||
|
|
155
src/main.c
155
src/main.c
|
@ -49,8 +49,8 @@ const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
|
|||
*/
|
||||
int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
|
||||
|
||||
/* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns
|
||||
** zero if and only if SQLite was compiled mutexing code omitted due to
|
||||
/* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns
|
||||
** zero if and only if SQLite was compiled with mutexing code omitted due to
|
||||
** the SQLITE_THREADSAFE compile-time option being set to 0.
|
||||
*/
|
||||
int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
|
||||
|
@ -239,8 +239,8 @@ int sqlite3_initialize(void){
|
|||
*/
|
||||
#ifdef SQLITE_EXTRA_INIT
|
||||
if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
|
||||
int SQLITE_EXTRA_INIT(void);
|
||||
rc = SQLITE_EXTRA_INIT();
|
||||
int SQLITE_EXTRA_INIT(const char*);
|
||||
rc = SQLITE_EXTRA_INIT(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -257,6 +257,10 @@ int sqlite3_initialize(void){
|
|||
*/
|
||||
int sqlite3_shutdown(void){
|
||||
if( sqlite3GlobalConfig.isInit ){
|
||||
#ifdef SQLITE_EXTRA_SHUTDOWN
|
||||
void SQLITE_EXTRA_SHUTDOWN(void);
|
||||
SQLITE_EXTRA_SHUTDOWN();
|
||||
#endif
|
||||
sqlite3_os_end();
|
||||
sqlite3_reset_auto_extension();
|
||||
sqlite3GlobalConfig.isInit = 0;
|
||||
|
@ -365,16 +369,25 @@ int sqlite3_config(int op, ...){
|
|||
}
|
||||
|
||||
case SQLITE_CONFIG_PCACHE: {
|
||||
/* Specify an alternative page cache implementation */
|
||||
sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
|
||||
/* no-op */
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETPCACHE: {
|
||||
/* now an error */
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_CONFIG_GETPCACHE: {
|
||||
if( sqlite3GlobalConfig.pcache.xInit==0 ){
|
||||
case SQLITE_CONFIG_PCACHE2: {
|
||||
/* Specify an alternative page cache implementation */
|
||||
sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*);
|
||||
break;
|
||||
}
|
||||
case SQLITE_CONFIG_GETPCACHE2: {
|
||||
if( sqlite3GlobalConfig.pcache2.xInit==0 ){
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
*va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache;
|
||||
*va_arg(ap, sqlite3_pcache_methods2*) = sqlite3GlobalConfig.pcache2;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -473,21 +486,21 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
|
|||
if( db->lookaside.bMalloced ){
|
||||
sqlite3_free(db->lookaside.pStart);
|
||||
}
|
||||
/* The size of a lookaside slot needs to be larger than a pointer
|
||||
** to be useful.
|
||||
/* The size of a lookaside slot after ROUNDDOWN8 needs to be larger
|
||||
** than a pointer to be useful.
|
||||
*/
|
||||
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
|
||||
if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0;
|
||||
if( cnt<0 ) cnt = 0;
|
||||
if( sz==0 || cnt==0 ){
|
||||
sz = 0;
|
||||
pStart = 0;
|
||||
}else if( pBuf==0 ){
|
||||
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
|
||||
sqlite3BeginBenignMalloc();
|
||||
pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
|
||||
sqlite3EndBenignMalloc();
|
||||
if( pStart ) cnt = sqlite3MallocSize(pStart)/sz;
|
||||
}else{
|
||||
sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
|
||||
pStart = pBuf;
|
||||
}
|
||||
db->lookaside.pStart = pStart;
|
||||
|
@ -521,6 +534,26 @@ sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){
|
|||
return db->mutex;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free up as much memory as we can from the given database
|
||||
** connection.
|
||||
*/
|
||||
int sqlite3_db_release_memory(sqlite3 *db){
|
||||
int i;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
Pager *pPager = sqlite3BtreePager(pBt);
|
||||
sqlite3PagerShrink(pPager);
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configuration settings for an individual database connection
|
||||
*/
|
||||
|
@ -1628,7 +1661,6 @@ static int createCollation(
|
|||
sqlite3* db,
|
||||
const char *zName,
|
||||
u8 enc,
|
||||
u8 collType,
|
||||
void* pCtx,
|
||||
int(*xCompare)(void*,int,const void*,int,const void*),
|
||||
void(*xDel)(void*)
|
||||
|
@ -1693,7 +1725,6 @@ static int createCollation(
|
|||
pColl->pUser = pCtx;
|
||||
pColl->xDel = xDel;
|
||||
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
|
||||
pColl->type = collType;
|
||||
sqlite3Error(db, SQLITE_OK, 0);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -2154,14 +2185,10 @@ static int openDatabase(
|
|||
** and UTF-16, so add a version for each to avoid any unnecessary
|
||||
** conversions. The only error that can occur here is a malloc() failure.
|
||||
*/
|
||||
createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0,
|
||||
binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0,
|
||||
binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0,
|
||||
binCollFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1,
|
||||
binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0);
|
||||
createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0);
|
||||
createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
|
||||
if( db->mallocFailed ){
|
||||
goto opendb_out;
|
||||
}
|
||||
|
@ -2169,8 +2196,7 @@ static int openDatabase(
|
|||
assert( db->pDfltColl!=0 );
|
||||
|
||||
/* Also add a UTF-8 case-insensitive collation sequence. */
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
|
||||
nocaseCollatingFunc, 0);
|
||||
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
|
||||
|
||||
/* Parse the filename/URI argument. */
|
||||
db->openFlags = flags;
|
||||
|
@ -2219,10 +2245,13 @@ static int openDatabase(
|
|||
/* Load automatic extensions - extensions that have been registered
|
||||
** using the sqlite3_automatic_extension() API.
|
||||
*/
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto opendb_out;
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3AutoLoadExtensions(db);
|
||||
rc = sqlite3_errcode(db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto opendb_out;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS1
|
||||
|
@ -2363,7 +2392,7 @@ int sqlite3_create_collation(
|
|||
int rc;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
|
||||
rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, 0);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
@ -2383,7 +2412,7 @@ int sqlite3_create_collation_v2(
|
|||
int rc;
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel);
|
||||
rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
|
@ -2406,7 +2435,7 @@ int sqlite3_create_collation16(
|
|||
assert( !db->mallocFailed );
|
||||
zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE);
|
||||
if( zName8 ){
|
||||
rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
|
||||
rc = createCollation(db, zName8, (u8)enc, pCtx, xCompare, 0);
|
||||
sqlite3DbFree(db, zName8);
|
||||
}
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
|
@ -2889,15 +2918,6 @@ int sqlite3_test_control(int op, ...){
|
|||
}
|
||||
#endif
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_PGHDRSZ)
|
||||
**
|
||||
** Return the size of a pcache header in bytes.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_PGHDRSZ: {
|
||||
rc = sizeof(PgHdr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
|
||||
**
|
||||
** Pass pFree into sqlite3ScratchFree().
|
||||
|
@ -2925,6 +2945,22 @@ int sqlite3_test_control(int op, ...){
|
|||
break;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
|
||||
** sqlite3_stmt*,const char**);
|
||||
**
|
||||
** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
|
||||
** a string that describes the optimized parse tree. This test-control
|
||||
** returns a pointer to that string.
|
||||
*/
|
||||
case SQLITE_TESTCTRL_EXPLAIN_STMT: {
|
||||
sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
|
||||
const char **pzRet = va_arg(ap, const char**);
|
||||
*pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
@ -2943,6 +2979,7 @@ int sqlite3_test_control(int op, ...){
|
|||
** returns a NULL pointer.
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
||||
if( zFilename==0 ) return 0;
|
||||
zFilename += sqlite3Strlen30(zFilename) + 1;
|
||||
while( zFilename[0] ){
|
||||
int x = strcmp(zFilename, zParam);
|
||||
|
@ -2952,3 +2989,41 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a boolean value for a query parameter.
|
||||
*/
|
||||
int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){
|
||||
const char *z = sqlite3_uri_parameter(zFilename, zParam);
|
||||
return z ? sqlite3GetBoolean(z) : (bDflt!=0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a 64-bit integer value for a query parameter.
|
||||
*/
|
||||
sqlite3_int64 sqlite3_uri_int64(
|
||||
const char *zFilename, /* Filename as passed to xOpen */
|
||||
const char *zParam, /* URI parameter sought */
|
||||
sqlite3_int64 bDflt /* return if parameter is missing */
|
||||
){
|
||||
const char *z = sqlite3_uri_parameter(zFilename, zParam);
|
||||
sqlite3_int64 v;
|
||||
if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){
|
||||
bDflt = v;
|
||||
}
|
||||
return bDflt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the filename of the database associated with a database
|
||||
** connection.
|
||||
*/
|
||||
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt && sqlite3StrICmp(zDbName, db->aDb[i].zName)==0 ){
|
||||
return sqlite3BtreeGetFilename(db->aDb[i].pBt);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -130,7 +130,8 @@ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
|||
sqlite3_int64 priorLimit;
|
||||
sqlite3_int64 excess;
|
||||
#ifndef SQLITE_OMIT_AUTOINIT
|
||||
sqlite3_initialize();
|
||||
int rc = sqlite3_initialize();
|
||||
if( rc ) return -1;
|
||||
#endif
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
priorLimit = mem0.alarmThreshold;
|
||||
|
|
103
src/mem1.c
103
src/mem1.c
|
@ -26,6 +26,47 @@
|
|||
*/
|
||||
#ifdef SQLITE_SYSTEM_MALLOC
|
||||
|
||||
/*
|
||||
** Windows systems have malloc_usable_size() but it is called _msize()
|
||||
*/
|
||||
#if !defined(HAVE_MALLOC_USABLE_SIZE) && SQLITE_OS_WIN
|
||||
# define HAVE_MALLOC_USABLE_SIZE 1
|
||||
# define malloc_usable_size _msize
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
/*
|
||||
** Use the zone allocator available on apple products
|
||||
*/
|
||||
#include <sys/sysctl.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
static malloc_zone_t* _sqliteZone_;
|
||||
#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
|
||||
#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
|
||||
#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
|
||||
#define SQLITE_MALLOCSIZE(x) \
|
||||
(_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
|
||||
|
||||
#else /* if not __APPLE__ */
|
||||
|
||||
/*
|
||||
** Use standard C library malloc and free on non-Apple systems.
|
||||
*/
|
||||
#define SQLITE_MALLOC(x) malloc(x)
|
||||
#define SQLITE_FREE(x) free(x)
|
||||
#define SQLITE_REALLOC(x,y) realloc((x),(y))
|
||||
|
||||
#ifdef HAVE_MALLOC_USABLE_SIZE
|
||||
#include <malloc.h>
|
||||
#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
#else
|
||||
#undef SQLITE_MALLOCSIZE
|
||||
#endif
|
||||
|
||||
#endif /* __APPLE__ or not __APPLE__ */
|
||||
|
||||
/*
|
||||
** Like malloc(), but remember the size of the allocation
|
||||
** so that we can find it later using sqlite3MemSize().
|
||||
|
@ -35,10 +76,18 @@
|
|||
** routines.
|
||||
*/
|
||||
static void *sqlite3MemMalloc(int nByte){
|
||||
#ifdef SQLITE_MALLOCSIZE
|
||||
void *p = SQLITE_MALLOC( nByte );
|
||||
if( p==0 ){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
||||
}
|
||||
return p;
|
||||
#else
|
||||
sqlite3_int64 *p;
|
||||
assert( nByte>0 );
|
||||
nByte = ROUND8(nByte);
|
||||
p = malloc( nByte+8 );
|
||||
p = SQLITE_MALLOC( nByte+8 );
|
||||
if( p ){
|
||||
p[0] = nByte;
|
||||
p++;
|
||||
|
@ -47,6 +96,7 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
|
||||
}
|
||||
return (void *)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -58,10 +108,14 @@ static void *sqlite3MemMalloc(int nByte){
|
|||
** by higher-level routines.
|
||||
*/
|
||||
static void sqlite3MemFree(void *pPrior){
|
||||
#ifdef SQLITE_MALLOCSIZE
|
||||
SQLITE_FREE(pPrior);
|
||||
#else
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
||||
assert( pPrior!=0 );
|
||||
p--;
|
||||
free(p);
|
||||
SQLITE_FREE(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -69,11 +123,15 @@ static void sqlite3MemFree(void *pPrior){
|
|||
** or xRealloc().
|
||||
*/
|
||||
static int sqlite3MemSize(void *pPrior){
|
||||
#ifdef SQLITE_MALLOCSIZE
|
||||
return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
|
||||
#else
|
||||
sqlite3_int64 *p;
|
||||
if( pPrior==0 ) return 0;
|
||||
p = (sqlite3_int64*)pPrior;
|
||||
p--;
|
||||
return (int)p[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -87,11 +145,21 @@ static int sqlite3MemSize(void *pPrior){
|
|||
** routines and redirected to xFree.
|
||||
*/
|
||||
static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
||||
#ifdef SQLITE_MALLOCSIZE
|
||||
void *p = SQLITE_REALLOC(pPrior, nByte);
|
||||
if( p==0 ){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(SQLITE_NOMEM,
|
||||
"failed memory resize %u to %u bytes",
|
||||
SQLITE_MALLOCSIZE(pPrior), nByte);
|
||||
}
|
||||
return p;
|
||||
#else
|
||||
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
|
||||
assert( pPrior!=0 && nByte>0 );
|
||||
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
|
||||
p--;
|
||||
p = realloc(p, nByte+8 );
|
||||
p = SQLITE_REALLOC(p, nByte+8 );
|
||||
if( p ){
|
||||
p[0] = nByte;
|
||||
p++;
|
||||
|
@ -102,6 +170,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
|
|||
sqlite3MemSize(pPrior), nByte);
|
||||
}
|
||||
return (void*)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -115,6 +184,34 @@ static int sqlite3MemRoundup(int n){
|
|||
** Initialize this module.
|
||||
*/
|
||||
static int sqlite3MemInit(void *NotUsed){
|
||||
#if defined(__APPLE__)
|
||||
int cpuCount;
|
||||
size_t len;
|
||||
if( _sqliteZone_ ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
len = sizeof(cpuCount);
|
||||
/* One usually wants to use hw.acctivecpu for MT decisions, but not here */
|
||||
sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
|
||||
if( cpuCount>1 ){
|
||||
/* defer MT decisions to system malloc */
|
||||
_sqliteZone_ = malloc_default_zone();
|
||||
}else{
|
||||
/* only 1 core, use our own zone to contention over global locks,
|
||||
** e.g. we have our own dedicated locks */
|
||||
bool success;
|
||||
malloc_zone_t* newzone = malloc_create_zone(4096, 0);
|
||||
malloc_set_zone_name(newzone, "Sqlite_Heap");
|
||||
do{
|
||||
success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
|
||||
(void * volatile *)&_sqliteZone_);
|
||||
}while(!_sqliteZone_);
|
||||
if( !success ){
|
||||
/* somebody registered a zone first */
|
||||
malloc_destroy_zone(newzone);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
|
|
@ -150,4 +150,4 @@ int sqlite3_mutex_notheld(sqlite3_mutex *p){
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE_MUTEX_OMIT */
|
||||
#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
||||
|
|
|
@ -202,5 +202,5 @@ sqlite3_mutex_methods const *sqlite3NoopMutex(void){
|
|||
sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
||||
return sqlite3NoopMutex();
|
||||
}
|
||||
#endif /* SQLITE_MUTEX_NOOP */
|
||||
#endif /* SQLITE_MUTEX_OMIT */
|
||||
#endif /* defined(SQLITE_MUTEX_NOOP) */
|
||||
#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
||||
|
|
|
@ -348,4 +348,4 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|||
return &sMutex;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_MUTEX_PTHREAD */
|
||||
#endif /* SQLITE_MUTEX_PTHREADS */
|
||||
|
|
29
src/os.c
29
src/os.c
|
@ -27,11 +27,18 @@
|
|||
** The following functions are instrumented for malloc() failure
|
||||
** testing:
|
||||
**
|
||||
** sqlite3OsOpen()
|
||||
** sqlite3OsRead()
|
||||
** sqlite3OsWrite()
|
||||
** sqlite3OsSync()
|
||||
** sqlite3OsFileSize()
|
||||
** sqlite3OsLock()
|
||||
** sqlite3OsCheckReservedLock()
|
||||
** sqlite3OsFileControl()
|
||||
** sqlite3OsShmMap()
|
||||
** sqlite3OsOpen()
|
||||
** sqlite3OsDelete()
|
||||
** sqlite3OsAccess()
|
||||
** sqlite3OsFullPathname()
|
||||
**
|
||||
*/
|
||||
#if defined(SQLITE_TEST)
|
||||
|
@ -90,9 +97,23 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
|
|||
DO_OS_MALLOC_TEST(id);
|
||||
return id->pMethods->xCheckReservedLock(id, pResOut);
|
||||
}
|
||||
|
||||
/*
|
||||
** Use sqlite3OsFileControl() when we are doing something that might fail
|
||||
** and we need to know about the failures. Use sqlite3OsFileControlHint()
|
||||
** when simply tossing information over the wall to the VFS and we do not
|
||||
** really care if the VFS receives and understands the information since it
|
||||
** is only a hint and can be safely ignored. The sqlite3OsFileControlHint()
|
||||
** routine has no return value since the return value would be meaningless.
|
||||
*/
|
||||
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
return id->pMethods->xFileControl(id, op, pArg);
|
||||
}
|
||||
void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
|
||||
(void)id->pMethods->xFileControl(id, op, pArg);
|
||||
}
|
||||
|
||||
int sqlite3OsSectorSize(sqlite3_file *id){
|
||||
int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
|
||||
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
|
||||
|
@ -116,6 +137,7 @@ int sqlite3OsShmMap(
|
|||
int bExtend, /* True to extend file if necessary */
|
||||
void volatile **pp /* OUT: Pointer to mapping */
|
||||
){
|
||||
DO_OS_MALLOC_TEST(id);
|
||||
return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
|
||||
}
|
||||
|
||||
|
@ -132,7 +154,7 @@ int sqlite3OsOpen(
|
|||
){
|
||||
int rc;
|
||||
DO_OS_MALLOC_TEST(0);
|
||||
/* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed
|
||||
/* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed
|
||||
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
|
||||
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
|
||||
** reaching the VFS. */
|
||||
|
@ -141,6 +163,8 @@ int sqlite3OsOpen(
|
|||
return rc;
|
||||
}
|
||||
int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
||||
DO_OS_MALLOC_TEST(0);
|
||||
assert( dirSync==0 || dirSync==1 );
|
||||
return pVfs->xDelete(pVfs, zPath, dirSync);
|
||||
}
|
||||
int sqlite3OsAccess(
|
||||
|
@ -158,6 +182,7 @@ int sqlite3OsFullPathname(
|
|||
int nPathOut,
|
||||
char *zPathOut
|
||||
){
|
||||
DO_OS_MALLOC_TEST(0);
|
||||
zPathOut[0] = 0;
|
||||
return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
|
||||
}
|
||||
|
|
34
src/os.h
34
src/os.h
|
@ -65,17 +65,6 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsCE - which has a much
|
||||
** reduced API.
|
||||
*/
|
||||
#if defined(_WIN32_WCE)
|
||||
# define SQLITE_OS_WINCE 1
|
||||
#else
|
||||
# define SQLITE_OS_WINCE 0
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Define the maximum size of a temporary filename
|
||||
*/
|
||||
|
@ -100,6 +89,25 @@
|
|||
# define SQLITE_TEMPNAME_SIZE 200
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with Windows NT.
|
||||
*/
|
||||
#if defined(_WIN32_WINNT)
|
||||
# define SQLITE_OS_WINNT 1
|
||||
#else
|
||||
# define SQLITE_OS_WINNT 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Determine if we are dealing with WindowsCE - which has a much
|
||||
** reduced API.
|
||||
*/
|
||||
#if defined(_WIN32_WCE)
|
||||
# define SQLITE_OS_WINCE 1
|
||||
#else
|
||||
# define SQLITE_OS_WINCE 0
|
||||
#endif
|
||||
|
||||
/* If the SET_FULLSYNC macro is not defined above, then make it
|
||||
** a no-op
|
||||
*/
|
||||
|
@ -111,7 +119,7 @@
|
|||
** The default size of a disk sector
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_SECTOR_SIZE
|
||||
# define SQLITE_DEFAULT_SECTOR_SIZE 512
|
||||
# define SQLITE_DEFAULT_SECTOR_SIZE 4096
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -244,6 +252,7 @@ int sqlite3OsLock(sqlite3_file*, int);
|
|||
int sqlite3OsUnlock(sqlite3_file*, int);
|
||||
int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
|
||||
int sqlite3OsFileControl(sqlite3_file*,int,void*);
|
||||
void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
|
||||
#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
|
||||
int sqlite3OsSectorSize(sqlite3_file *id);
|
||||
int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
|
||||
|
@ -252,6 +261,7 @@ int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
|
|||
void sqlite3OsShmBarrier(sqlite3_file *id);
|
||||
int sqlite3OsShmUnmap(sqlite3_file *id, int);
|
||||
|
||||
|
||||
/*
|
||||
** Functions for accessing sqlite3_vfs methods
|
||||
*/
|
||||
|
|
247
src/os_unix.c
247
src/os_unix.c
|
@ -123,6 +123,7 @@
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
# include <sys/ioctl.h>
|
||||
# if OS_VXWORKS
|
||||
|
@ -206,6 +207,7 @@ struct UnixUnusedFd {
|
|||
typedef struct unixFile unixFile;
|
||||
struct unixFile {
|
||||
sqlite3_io_methods const *pMethod; /* Always the first entry */
|
||||
sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
|
||||
unixInodeInfo *pInode; /* Info about locks on this inode */
|
||||
int h; /* The file descriptor */
|
||||
unsigned char eFileLock; /* The type of lock held on this fd */
|
||||
|
@ -223,7 +225,6 @@ struct unixFile {
|
|||
unsigned fsFlags; /* cached details from statfs() */
|
||||
#endif
|
||||
#if OS_VXWORKS
|
||||
int isDelete; /* Delete on close if true */
|
||||
struct vxworksFileId *pId; /* Unique file ID */
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
|
@ -257,6 +258,10 @@ struct unixFile {
|
|||
#else
|
||||
# define UNIXFILE_DIRSYNC 0x00
|
||||
#endif
|
||||
#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
||||
#define UNIXFILE_DELETE 0x20 /* Delete on close */
|
||||
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
|
||||
#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
|
||||
|
||||
/*
|
||||
** Include code that is common to all os_*.c files
|
||||
|
@ -407,6 +412,12 @@ static struct unix_syscall {
|
|||
{ "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
|
||||
#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
|
||||
|
||||
{ "mkdir", (sqlite3_syscall_ptr)mkdir, 0 },
|
||||
#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent)
|
||||
|
||||
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
|
||||
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
|
||||
|
||||
}; /* End of the overrideable system calls */
|
||||
|
||||
/*
|
||||
|
@ -1756,7 +1767,7 @@ static int closeUnixFile(sqlite3_file *id){
|
|||
}
|
||||
#if OS_VXWORKS
|
||||
if( pFile->pId ){
|
||||
if( pFile->isDelete ){
|
||||
if( pFile->ctrlFlags & UNIXFILE_DELETE ){
|
||||
osUnlink(pFile->pId->zCanonicalName);
|
||||
}
|
||||
vxworksReleaseFileId(pFile->pId);
|
||||
|
@ -1845,8 +1856,8 @@ static int nolockClose(sqlite3_file *id) {
|
|||
************************* Begin dot-file Locking ******************************
|
||||
**
|
||||
** The dotfile locking implementation uses the existance of separate lock
|
||||
** files in order to control access to the database. This works on just
|
||||
** about every filesystem imaginable. But there are serious downsides:
|
||||
** files (really a directory) to control access to the database. This works
|
||||
** on just about every filesystem imaginable. But there are serious downsides:
|
||||
**
|
||||
** (1) There is zero concurrency. A single reader blocks all other
|
||||
** connections from reading or writing the database.
|
||||
|
@ -1857,15 +1868,15 @@ static int nolockClose(sqlite3_file *id) {
|
|||
** Nevertheless, a dotlock is an appropriate locking mode for use if no
|
||||
** other locking strategy is available.
|
||||
**
|
||||
** Dotfile locking works by creating a file in the same directory as the
|
||||
** database and with the same name but with a ".lock" extension added.
|
||||
** The existance of a lock file implies an EXCLUSIVE lock. All other lock
|
||||
** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
|
||||
** Dotfile locking works by creating a subdirectory in the same directory as
|
||||
** the database and with the same name but with a ".lock" extension added.
|
||||
** The existance of a lock directory implies an EXCLUSIVE lock. All other
|
||||
** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The file suffix added to the data base filename in order to create the
|
||||
** lock file.
|
||||
** lock directory.
|
||||
*/
|
||||
#define DOTLOCK_SUFFIX ".lock"
|
||||
|
||||
|
@ -1932,7 +1943,6 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
|
|||
*/
|
||||
static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
int fd;
|
||||
char *zLockFile = (char *)pFile->lockingContext;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
|
@ -1952,9 +1962,9 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
|||
}
|
||||
|
||||
/* grab an exclusive lock */
|
||||
fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
|
||||
if( fd<0 ){
|
||||
/* failed to open/create the file, someone else may have stolen the lock */
|
||||
rc = osMkdir(zLockFile, 0777);
|
||||
if( rc<0 ){
|
||||
/* failed to open/create the lock directory */
|
||||
int tErrno = errno;
|
||||
if( EEXIST == tErrno ){
|
||||
rc = SQLITE_BUSY;
|
||||
|
@ -1966,7 +1976,6 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
|||
}
|
||||
return rc;
|
||||
}
|
||||
robust_close(pFile, fd, __LINE__);
|
||||
|
||||
/* got it, set the type and return ok */
|
||||
pFile->eFileLock = eFileLock;
|
||||
|
@ -1985,6 +1994,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
|
|||
static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
||||
unixFile *pFile = (unixFile*)id;
|
||||
char *zLockFile = (char *)pFile->lockingContext;
|
||||
int rc;
|
||||
|
||||
assert( pFile );
|
||||
OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
|
||||
|
@ -2006,9 +2016,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
|
|||
|
||||
/* To fully unlock the database, delete the lock file */
|
||||
assert( eFileLock==NO_LOCK );
|
||||
if( osUnlink(zLockFile) ){
|
||||
int rc = 0;
|
||||
rc = osRmdir(zLockFile);
|
||||
if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
|
||||
if( rc<0 ){
|
||||
int tErrno = errno;
|
||||
rc = 0;
|
||||
if( ENOENT != tErrno ){
|
||||
rc = SQLITE_IOERR_UNLOCK;
|
||||
}
|
||||
|
@ -2944,35 +2956,48 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
|
|||
*/
|
||||
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
|
||||
int got;
|
||||
int prior = 0;
|
||||
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
||||
i64 newOffset;
|
||||
#endif
|
||||
TIMER_START;
|
||||
do{
|
||||
#if defined(USE_PREAD)
|
||||
do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
|
||||
SimulateIOError( got = -1 );
|
||||
got = osPread(id->h, pBuf, cnt, offset);
|
||||
SimulateIOError( got = -1 );
|
||||
#elif defined(USE_PREAD64)
|
||||
do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR);
|
||||
SimulateIOError( got = -1 );
|
||||
got = osPread64(id->h, pBuf, cnt, offset);
|
||||
SimulateIOError( got = -1 );
|
||||
#else
|
||||
newOffset = lseek(id->h, offset, SEEK_SET);
|
||||
SimulateIOError( newOffset-- );
|
||||
if( newOffset!=offset ){
|
||||
if( newOffset == -1 ){
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
}else{
|
||||
((unixFile*)id)->lastErrno = 0;
|
||||
newOffset = lseek(id->h, offset, SEEK_SET);
|
||||
SimulateIOError( newOffset-- );
|
||||
if( newOffset!=offset ){
|
||||
if( newOffset == -1 ){
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
}else{
|
||||
((unixFile*)id)->lastErrno = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
|
||||
got = osRead(id->h, pBuf, cnt);
|
||||
#endif
|
||||
if( got==cnt ) break;
|
||||
if( got<0 ){
|
||||
if( errno==EINTR ){ got = 1; continue; }
|
||||
prior = 0;
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
break;
|
||||
}else if( got>0 ){
|
||||
cnt -= got;
|
||||
offset += got;
|
||||
prior += got;
|
||||
pBuf = (void*)(got + (char*)pBuf);
|
||||
}
|
||||
}while( got>0 );
|
||||
TIMER_END;
|
||||
if( got<0 ){
|
||||
((unixFile*)id)->lastErrno = errno;
|
||||
}
|
||||
OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
|
||||
return got;
|
||||
OSTRACE(("READ %-3d %5d %7lld %llu\n",
|
||||
id->h, got+prior, offset-prior, TIMER_ELAPSED));
|
||||
return got+prior;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3477,6 +3502,22 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** If *pArg is inititially negative then this is a query. Set *pArg to
|
||||
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
|
||||
**
|
||||
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
|
||||
*/
|
||||
static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
|
||||
if( *pArg<0 ){
|
||||
*pArg = (pFile->ctrlFlags & mask)!=0;
|
||||
}else if( (*pArg)==0 ){
|
||||
pFile->ctrlFlags &= ~mask;
|
||||
}else{
|
||||
pFile->ctrlFlags |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Information and control of an open file handle.
|
||||
*/
|
||||
|
@ -3503,14 +3544,15 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
|||
return rc;
|
||||
}
|
||||
case SQLITE_FCNTL_PERSIST_WAL: {
|
||||
int bPersist = *(int*)pArg;
|
||||
if( bPersist<0 ){
|
||||
*(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
|
||||
}else if( bPersist==0 ){
|
||||
pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
|
||||
}else{
|
||||
pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
|
||||
}
|
||||
unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
|
||||
unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_VFSNAME: {
|
||||
*(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
|
@ -3530,9 +3572,6 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
|||
return proxyFileControl(id,op,pArg);
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: {
|
||||
return SQLITE_OK; /* A no-op */
|
||||
}
|
||||
}
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
@ -3547,17 +3586,31 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
|||
** a database and its journal file) that the sector size will be the
|
||||
** same for both.
|
||||
*/
|
||||
static int unixSectorSize(sqlite3_file *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
static int unixSectorSize(sqlite3_file *pFile){
|
||||
(void)pFile;
|
||||
return SQLITE_DEFAULT_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the device characteristics for the file. This is always 0 for unix.
|
||||
** Return the device characteristics for the file.
|
||||
**
|
||||
** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
|
||||
** However, that choice is contraversial since technically the underlying
|
||||
** file system does not always provide powersafe overwrites. (In other
|
||||
** words, after a power-loss event, parts of the file that were never
|
||||
** written might end up being altered.) However, non-PSOW behavior is very,
|
||||
** very rare. And asserting PSOW makes a large reduction in the amount
|
||||
** of required I/O for journaling, since a lot of padding is eliminated.
|
||||
** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
|
||||
** available to turn it off and URI query parameter available to turn it off.
|
||||
*/
|
||||
static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
return 0;
|
||||
static int unixDeviceCharacteristics(sqlite3_file *id){
|
||||
unixFile *p = (unixFile*)id;
|
||||
if( p->ctrlFlags & UNIXFILE_PSOW ){
|
||||
return SQLITE_IOCAP_POWERSAFE_OVERWRITE;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
|
@ -3812,16 +3865,16 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
|||
}
|
||||
|
||||
#ifdef SQLITE_SHM_DIRECTORY
|
||||
nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
|
||||
nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31;
|
||||
#else
|
||||
nShmFilename = 5 + (int)strlen(pDbFd->zPath);
|
||||
nShmFilename = 6 + (int)strlen(pDbFd->zPath);
|
||||
#endif
|
||||
pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
|
||||
if( pShmNode==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto shm_open_err;
|
||||
}
|
||||
memset(pShmNode, 0, sizeof(*pShmNode));
|
||||
memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
|
||||
zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
|
||||
#ifdef SQLITE_SHM_DIRECTORY
|
||||
sqlite3_snprintf(nShmFilename, zShmFilename,
|
||||
|
@ -3841,10 +3894,8 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
|||
}
|
||||
|
||||
if( pInode->bProcessLock==0 ){
|
||||
const char *zRO;
|
||||
int openFlags = O_RDWR | O_CREAT;
|
||||
zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
|
||||
if( zRO && sqlite3GetBoolean(zRO) ){
|
||||
if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
|
||||
openFlags = O_RDONLY;
|
||||
pShmNode->isReadonly = 1;
|
||||
}
|
||||
|
@ -4506,12 +4557,9 @@ typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
|
|||
static int fillInUnixFile(
|
||||
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
||||
int h, /* Open file descriptor of file being opened */
|
||||
int syncDir, /* True to sync directory on first sync */
|
||||
sqlite3_file *pId, /* Write to the unixFile structure here */
|
||||
const char *zFilename, /* Name of the file being opened */
|
||||
int noLock, /* Omit locking if true */
|
||||
int isDelete, /* Delete on close if true */
|
||||
int isReadOnly /* True if the file is opened read-only */
|
||||
int ctrlFlags /* Zero or more UNIXFILE_* values */
|
||||
){
|
||||
const sqlite3_io_methods *pLockingStyle;
|
||||
unixFile *pNew = (unixFile *)pId;
|
||||
|
@ -4519,11 +4567,6 @@ static int fillInUnixFile(
|
|||
|
||||
assert( pNew->pInode==NULL );
|
||||
|
||||
/* Parameter isDelete is only used on vxworks. Express this explicitly
|
||||
** here to prevent compiler warnings about unused parameters.
|
||||
*/
|
||||
UNUSED_PARAMETER(isDelete);
|
||||
|
||||
/* Usually the path zFilename should not be a relative pathname. The
|
||||
** exception is when opening the proxy "conch" file in builds that
|
||||
** include the special Apple locking styles.
|
||||
|
@ -4536,32 +4579,30 @@ static int fillInUnixFile(
|
|||
#endif
|
||||
|
||||
/* No locking occurs in temporary files */
|
||||
assert( zFilename!=0 || noLock );
|
||||
assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
|
||||
|
||||
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
|
||||
pNew->h = h;
|
||||
pNew->pVfs = pVfs;
|
||||
pNew->zPath = zFilename;
|
||||
pNew->ctrlFlags = (u8)ctrlFlags;
|
||||
if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
|
||||
"psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
||||
pNew->ctrlFlags |= UNIXFILE_PSOW;
|
||||
}
|
||||
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
|
||||
pNew->ctrlFlags = UNIXFILE_EXCL;
|
||||
}else{
|
||||
pNew->ctrlFlags = 0;
|
||||
}
|
||||
if( isReadOnly ){
|
||||
pNew->ctrlFlags |= UNIXFILE_RDONLY;
|
||||
}
|
||||
if( syncDir ){
|
||||
pNew->ctrlFlags |= UNIXFILE_DIRSYNC;
|
||||
pNew->ctrlFlags |= UNIXFILE_EXCL;
|
||||
}
|
||||
|
||||
#if OS_VXWORKS
|
||||
pNew->pId = vxworksFindFileId(zFilename);
|
||||
if( pNew->pId==0 ){
|
||||
noLock = 1;
|
||||
ctrlFlags |= UNIXFILE_NOLOCK;
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( noLock ){
|
||||
if( ctrlFlags & UNIXFILE_NOLOCK ){
|
||||
pLockingStyle = &nolockIoMethods;
|
||||
}else{
|
||||
pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew);
|
||||
|
@ -4682,7 +4723,7 @@ static int fillInUnixFile(
|
|||
osUnlink(zFilename);
|
||||
isDelete = 0;
|
||||
}
|
||||
pNew->isDelete = isDelete;
|
||||
if( isDelete ) pNew->ctrlFlags |= UNIXFILE_DELETE;
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( h>=0 ) robust_close(pNew, h, __LINE__);
|
||||
|
@ -4747,18 +4788,19 @@ static int unixGetTempname(int nBuf, char *zBuf){
|
|||
/* Check that the output buffer is large enough for the temporary file
|
||||
** name. If it is not, return SQLITE_ERROR.
|
||||
*/
|
||||
if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){
|
||||
if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
do{
|
||||
sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
|
||||
sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
|
||||
j = (int)strlen(zBuf);
|
||||
sqlite3_randomness(15, &zBuf[j]);
|
||||
for(i=0; i<15; i++, j++){
|
||||
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
|
||||
}
|
||||
zBuf[j] = 0;
|
||||
zBuf[j+1] = 0;
|
||||
}while( osAccess(zBuf,0)==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -4879,7 +4921,7 @@ static int findCreateFileMode(
|
|||
*/
|
||||
nDb = sqlite3Strlen30(zPath) - 1;
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
|
||||
while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
|
||||
if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
|
||||
#else
|
||||
while( zPath[nDb]!='-' ){
|
||||
|
@ -4937,6 +4979,7 @@ static int unixOpen(
|
|||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||
int noLock; /* True to omit locking primitives */
|
||||
int rc = SQLITE_OK; /* Function Return Code */
|
||||
int ctrlFlags = 0; /* UNIXFILE_* flags */
|
||||
|
||||
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
|
||||
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
|
||||
|
@ -4963,7 +5006,7 @@ static int unixOpen(
|
|||
/* If argument zPath is a NULL pointer, this function is required to open
|
||||
** a temporary file. Use this buffer to store the file name in.
|
||||
*/
|
||||
char zTmpname[MAX_PATHNAME+1];
|
||||
char zTmpname[MAX_PATHNAME+2];
|
||||
const char *zName = zPath;
|
||||
|
||||
/* Check the following statements are true:
|
||||
|
@ -5006,14 +5049,24 @@ static int unixOpen(
|
|||
}
|
||||
}
|
||||
p->pUnused = pUnused;
|
||||
|
||||
/* Database filenames are double-zero terminated if they are not
|
||||
** URIs with parameters. Hence, they can always be passed into
|
||||
** sqlite3_uri_parameter(). */
|
||||
assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 );
|
||||
|
||||
}else if( !zName ){
|
||||
/* If zName is NULL, the upper layer is requesting a temp file. */
|
||||
assert(isDelete && !syncDir);
|
||||
rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
|
||||
rc = unixGetTempname(MAX_PATHNAME+2, zTmpname);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
zName = zTmpname;
|
||||
|
||||
/* Generated temporary filenames are always double-zero terminated
|
||||
** for use by sqlite3_uri_parameter(). */
|
||||
assert( zName[strlen(zName)+1]==0 );
|
||||
}
|
||||
|
||||
/* Determine the value of the flags parameter passed to POSIX function
|
||||
|
@ -5090,7 +5143,14 @@ static int unixOpen(
|
|||
((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Set up appropriate ctrlFlags */
|
||||
if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
|
||||
if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
|
||||
if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
|
||||
if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
|
||||
if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
|
||||
|
||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||
#if SQLITE_PREFER_PROXY_LOCKING
|
||||
isAutoProxy = 1;
|
||||
|
@ -5120,8 +5180,7 @@ static int unixOpen(
|
|||
useProxy = !(fsInfo.f_flags&MNT_LOCAL);
|
||||
}
|
||||
if( useProxy ){
|
||||
rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
|
||||
isDelete, isReadonly);
|
||||
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -5138,8 +5197,8 @@ static int unixOpen(
|
|||
}
|
||||
#endif
|
||||
|
||||
rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
|
||||
isDelete, isReadonly);
|
||||
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
||||
|
||||
open_finished:
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(p->pUnused);
|
||||
|
@ -5164,7 +5223,7 @@ static int unixDelete(
|
|||
return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
|
||||
}
|
||||
#ifndef SQLITE_DISABLE_DIRSYNC
|
||||
if( dirSync ){
|
||||
if( (dirSync & 1)!=0 ){
|
||||
int fd;
|
||||
rc = osOpenDirectory(zPath, &fd);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -5715,7 +5774,7 @@ static int proxyCreateLockPath(const char *lockPath){
|
|||
if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|
||||
|| (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
|
||||
buf[i]='\0';
|
||||
if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
|
||||
if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
|
||||
int err=errno;
|
||||
if( err!=EEXIST ) {
|
||||
OSTRACE(("CREATELOCKPATH FAILED creating %s, "
|
||||
|
@ -5810,7 +5869,7 @@ static int proxyCreateUnixFile(
|
|||
pUnused->flags = openFlags;
|
||||
pNew->pUnused = pUnused;
|
||||
|
||||
rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0);
|
||||
rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
*ppFile = pNew;
|
||||
return SQLITE_OK;
|
||||
|
@ -6751,7 +6810,7 @@ int sqlite3_os_init(void){
|
|||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==18 );
|
||||
assert( ArraySize(aSyscall)==20 );
|
||||
|
||||
/* Register all VFSes defined in the aVfs[] array */
|
||||
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
||||
|
|
1421
src/os_win.c
1421
src/os_win.c
File diff suppressed because it is too large
Load Diff
134
src/pager.c
134
src/pager.c
|
@ -616,6 +616,7 @@ struct Pager {
|
|||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
||||
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
|
||||
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
|
@ -786,7 +787,7 @@ static int pagerUseWal(Pager *pPager){
|
|||
#else
|
||||
# define pagerUseWal(x) 0
|
||||
# define pagerRollbackWal(x) 0
|
||||
# define pagerWalFrames(v,w,x,y,z) 0
|
||||
# define pagerWalFrames(v,w,x,y) 0
|
||||
# define pagerOpenWalIfPresent(z) SQLITE_OK
|
||||
# define pagerBeginReadTransaction(z) SQLITE_OK
|
||||
#endif
|
||||
|
@ -2485,10 +2486,9 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
|
|||
if( rc==SQLITE_OK && currentSize!=newSize ){
|
||||
if( currentSize>newSize ){
|
||||
rc = sqlite3OsTruncate(pPager->fd, newSize);
|
||||
}else{
|
||||
}else if( (currentSize+szPage)<=newSize ){
|
||||
char *pTmp = pPager->pTmpSpace;
|
||||
memset(pTmp, 0, szPage);
|
||||
testcase( (newSize-szPage) < currentSize );
|
||||
testcase( (newSize-szPage) == currentSize );
|
||||
testcase( (newSize-szPage) > currentSize );
|
||||
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
|
||||
|
@ -2514,23 +2514,36 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
|
|||
** the value returned by the xSectorSize() method rounded up to 32 if
|
||||
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it
|
||||
** is greater than MAX_SECTOR_SIZE.
|
||||
**
|
||||
** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set
|
||||
** the effective sector size to its minimum value (512). The purpose of
|
||||
** pPager->sectorSize is to define the "blast radius" of bytes that
|
||||
** might change if a crash occurs while writing to a single byte in
|
||||
** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero
|
||||
** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector
|
||||
** size. For backwards compatibility of the rollback journal file format,
|
||||
** we cannot reduce the effective sector size below 512.
|
||||
*/
|
||||
static void setSectorSize(Pager *pPager){
|
||||
assert( isOpen(pPager->fd) || pPager->tempFile );
|
||||
|
||||
if( !pPager->tempFile ){
|
||||
if( pPager->tempFile
|
||||
|| (sqlite3OsDeviceCharacteristics(pPager->fd) &
|
||||
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
|
||||
){
|
||||
/* Sector size doesn't matter for temporary files. Also, the file
|
||||
** may not have been opened yet, in which case the OsSectorSize()
|
||||
** call will segfault.
|
||||
*/
|
||||
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
|
||||
}
|
||||
if( pPager->sectorSize<32 ){
|
||||
** call will segfault. */
|
||||
pPager->sectorSize = 512;
|
||||
}
|
||||
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
|
||||
assert( MAX_SECTOR_SIZE>=512 );
|
||||
pPager->sectorSize = MAX_SECTOR_SIZE;
|
||||
}else{
|
||||
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
|
||||
if( pPager->sectorSize<32 ){
|
||||
pPager->sectorSize = 512;
|
||||
}
|
||||
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
|
||||
assert( MAX_SECTOR_SIZE>=512 );
|
||||
pPager->sectorSize = MAX_SECTOR_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2733,10 +2746,11 @@ end_playback:
|
|||
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the
|
||||
** assertion that the transaction counter was modified.
|
||||
*/
|
||||
assert(
|
||||
pPager->fd->pMethods==0 ||
|
||||
sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK
|
||||
);
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pPager->fd->pMethods ){
|
||||
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If this playback is happening automatically as a result of an IO or
|
||||
** malloc error that occurred after the change-counter was updated but
|
||||
|
@ -2955,8 +2969,7 @@ static int pagerWalFrames(
|
|||
Pager *pPager, /* Pager object */
|
||||
PgHdr *pList, /* List of frames to log */
|
||||
Pgno nTruncate, /* Database size after this commit */
|
||||
int isCommit, /* True if this is a commit */
|
||||
int syncFlags /* Flags to pass to OsSync() (or 0) */
|
||||
int isCommit /* True if this is a commit */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
|
||||
|
@ -2987,7 +3000,7 @@ static int pagerWalFrames(
|
|||
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
rc = sqlite3WalFrames(pPager->pWal,
|
||||
pPager->pageSize, pList, nTruncate, isCommit, syncFlags
|
||||
pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
|
||||
);
|
||||
if( rc==SQLITE_OK && pPager->pBackup ){
|
||||
PgHdr *p;
|
||||
|
@ -3074,10 +3087,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
|||
return rc;
|
||||
}
|
||||
}
|
||||
nPage = (Pgno)(n / pPager->pageSize);
|
||||
if( nPage==0 && n>0 ){
|
||||
nPage = 1;
|
||||
}
|
||||
nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize);
|
||||
}
|
||||
|
||||
/* If the current number of pages in the file is greater than the
|
||||
|
@ -3267,13 +3277,13 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
|
|||
*/
|
||||
if( pSavepoint ){
|
||||
u32 ii; /* Loop counter */
|
||||
i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize);
|
||||
i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize);
|
||||
|
||||
if( pagerUseWal(pPager) ){
|
||||
rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData);
|
||||
}
|
||||
for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){
|
||||
assert( offset==ii*(4+pPager->pageSize) );
|
||||
assert( offset==(i64)ii*(4+pPager->pageSize) );
|
||||
rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1);
|
||||
}
|
||||
assert( rc!=SQLITE_DONE );
|
||||
|
@ -3294,6 +3304,13 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
|
|||
sqlite3PcacheSetCachesize(pPager->pPCache, mxPage);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free as much memory as possible from the pager.
|
||||
*/
|
||||
void sqlite3PagerShrink(Pager *pPager){
|
||||
sqlite3PcacheShrink(pPager->pPCache);
|
||||
}
|
||||
|
||||
/*
|
||||
** Adjust the robustness of the database to damage due to OS crashes
|
||||
** or power failures by changing the number of syncs()s when writing
|
||||
|
@ -3360,6 +3377,10 @@ void sqlite3PagerSetSafetyLevel(
|
|||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
}
|
||||
pPager->walSyncFlags = pPager->syncFlags;
|
||||
if( pPager->fullSync ){
|
||||
pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3497,7 +3518,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
|
|||
|
||||
if( rc==SQLITE_OK ){
|
||||
pager_reset(pPager);
|
||||
pPager->dbSize = (Pgno)(nByte/pageSize);
|
||||
pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize);
|
||||
pPager->pageSize = pageSize;
|
||||
sqlite3PageFree(pPager->pTmpSpace);
|
||||
pPager->pTmpSpace = pNew;
|
||||
|
@ -4005,7 +4026,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
|||
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
|
||||
if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
|
||||
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
|
||||
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
|
||||
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
|
||||
pPager->dbHintSize = pPager->dbSize;
|
||||
}
|
||||
|
||||
|
@ -4114,7 +4135,7 @@ static int subjournalPage(PgHdr *pPg){
|
|||
** write the journal record into the file. */
|
||||
if( rc==SQLITE_OK ){
|
||||
void *pData = pPg->pData;
|
||||
i64 offset = pPager->nSubRec*(4+pPager->pageSize);
|
||||
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
|
||||
char *pData2;
|
||||
|
||||
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
|
||||
|
@ -4187,7 +4208,7 @@ static int pagerStress(void *p, PgHdr *pPg){
|
|||
rc = subjournalPage(pPg);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
|
||||
rc = pagerWalFrames(pPager, pPg, 0, 0);
|
||||
}
|
||||
}else{
|
||||
|
||||
|
@ -4346,7 +4367,8 @@ int sqlite3PagerOpen(
|
|||
z += sqlite3Strlen30(z)+1;
|
||||
z += sqlite3Strlen30(z)+1;
|
||||
}
|
||||
nUri = &z[1] - zUri;
|
||||
nUri = (int)(&z[1] - zUri);
|
||||
assert( nUri>=0 );
|
||||
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
|
||||
/* This branch is taken when the journal path required by
|
||||
** the database being opened will be more than pVfs->mxPathname
|
||||
|
@ -4380,9 +4402,9 @@ int sqlite3PagerOpen(
|
|||
ROUND8(pVfs->szOsFile) + /* The main db file */
|
||||
journalFileSize * 2 + /* The two journal files */
|
||||
nPathname + 1 + nUri + /* zFilename */
|
||||
nPathname + 8 + 1 /* zJournal */
|
||||
nPathname + 8 + 2 /* zJournal */
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
+ nPathname + 4 + 1 /* zWal */
|
||||
+ nPathname + 4 + 2 /* zWal */
|
||||
#endif
|
||||
);
|
||||
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
|
||||
|
@ -4405,12 +4427,12 @@ int sqlite3PagerOpen(
|
|||
memcpy(pPager->zFilename, zPathname, nPathname);
|
||||
memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
|
||||
memcpy(pPager->zJournal, zPathname, nPathname);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal", 8);
|
||||
memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+1);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
pPager->zWal = &pPager->zJournal[nPathname+8+1];
|
||||
memcpy(pPager->zWal, zPathname, nPathname);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal", 4);
|
||||
memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
|
||||
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
|
||||
#endif
|
||||
sqlite3_free(zPathname);
|
||||
|
@ -4526,9 +4548,17 @@ int sqlite3PagerOpen(
|
|||
pPager->readOnly = (u8)readOnly;
|
||||
assert( useJournal || pPager->tempFile );
|
||||
pPager->noSync = pPager->tempFile;
|
||||
pPager->fullSync = pPager->noSync ?0:1;
|
||||
pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
|
||||
pPager->ckptSyncFlags = pPager->syncFlags;
|
||||
if( pPager->noSync ){
|
||||
assert( pPager->fullSync==0 );
|
||||
assert( pPager->syncFlags==0 );
|
||||
assert( pPager->walSyncFlags==0 );
|
||||
assert( pPager->ckptSyncFlags==0 );
|
||||
}else{
|
||||
pPager->fullSync = 1;
|
||||
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
||||
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
|
||||
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
|
||||
}
|
||||
/* pPager->pFirst = 0; */
|
||||
/* pPager->pFirstSynced = 0; */
|
||||
/* pPager->pLast = 0; */
|
||||
|
@ -5661,7 +5691,10 @@ int sqlite3PagerSync(Pager *pPager){
|
|||
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
|
||||
}else if( isOpen(pPager->fd) ){
|
||||
assert( !MEMDB );
|
||||
sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -5758,9 +5791,7 @@ int sqlite3PagerCommitPhaseOne(
|
|||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
if( ALWAYS(pList) ){
|
||||
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
|
||||
(pPager->fullSync ? pPager->syncFlags : 0)
|
||||
);
|
||||
rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1);
|
||||
}
|
||||
sqlite3PagerUnref(pPageOne);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -6019,7 +6050,8 @@ int sqlite3PagerRollback(Pager *pPager){
|
|||
}
|
||||
|
||||
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
|
||||
assert( rc==SQLITE_OK || rc==SQLITE_FULL
|
||||
|| rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR );
|
||||
|
||||
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
|
||||
** cache. So call pager_error() on the way out to make any error persistent.
|
||||
|
@ -6660,6 +6692,15 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
|
|||
return &pPager->pBackup;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VACUUM
|
||||
/*
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
/*
|
||||
** This function is called when the user invokes "PRAGMA wal_checkpoint",
|
||||
|
@ -6836,13 +6877,6 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unless this is an in-memory or temporary database, clear the pager cache.
|
||||
*/
|
||||
void sqlite3PagerClearCache(Pager *pPager){
|
||||
if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
/*
|
||||
** This function is called by the wal module when writing page content
|
||||
|
|
|
@ -103,6 +103,7 @@ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
|
|||
int sqlite3PagerSetPagesize(Pager*, u32*, int);
|
||||
int sqlite3PagerMaxPageCount(Pager*, int);
|
||||
void sqlite3PagerSetCachesize(Pager*, int);
|
||||
void sqlite3PagerShrink(Pager*);
|
||||
void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
|
||||
int sqlite3PagerLockingMode(Pager *, int);
|
||||
int sqlite3PagerSetJournalMode(Pager *, int);
|
||||
|
|
|
@ -33,12 +33,10 @@
|
|||
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
|
||||
assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
|
||||
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
|
||||
pParse->parseError = 1;
|
||||
}
|
||||
%stack_overflow {
|
||||
UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */
|
||||
sqlite3ErrorMsg(pParse, "parser stack overflow");
|
||||
pParse->parseError = 1;
|
||||
}
|
||||
|
||||
// The name of the generated procedure that implements the parser
|
||||
|
@ -396,6 +394,9 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
|
|||
cmd ::= select(X). {
|
||||
SelectDest dest = {SRT_Output, 0, 0, 0, 0};
|
||||
sqlite3Select(pParse, X, &dest);
|
||||
sqlite3ExplainBegin(pParse->pVdbe);
|
||||
sqlite3ExplainSelect(pParse->pVdbe, X);
|
||||
sqlite3ExplainFinish(pParse->pVdbe);
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
}
|
||||
|
||||
|
|
107
src/pcache.c
107
src/pcache.c
|
@ -20,7 +20,7 @@ struct PCache {
|
|||
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
|
||||
PgHdr *pSynced; /* Last synced page in dirty page list */
|
||||
int nRef; /* Number of referenced pages */
|
||||
int nMax; /* Configured cache size */
|
||||
int szCache; /* Configured cache size */
|
||||
int szPage; /* Size of every page in this cache */
|
||||
int szExtra; /* Size of extra space for each page */
|
||||
int bPurgeable; /* True if pages are on backing store */
|
||||
|
@ -131,7 +131,7 @@ static void pcacheUnpin(PgHdr *p){
|
|||
if( p->pgno==1 ){
|
||||
pCache->pPage1 = 0;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0);
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,18 +141,18 @@ static void pcacheUnpin(PgHdr *p){
|
|||
** functions are threadsafe.
|
||||
*/
|
||||
int sqlite3PcacheInitialize(void){
|
||||
if( sqlite3GlobalConfig.pcache.xInit==0 ){
|
||||
if( sqlite3GlobalConfig.pcache2.xInit==0 ){
|
||||
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
|
||||
** built-in default page cache is used instead of the application defined
|
||||
** page cache. */
|
||||
sqlite3PCacheSetDefault();
|
||||
}
|
||||
return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
|
||||
return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
|
||||
}
|
||||
void sqlite3PcacheShutdown(void){
|
||||
if( sqlite3GlobalConfig.pcache.xShutdown ){
|
||||
if( sqlite3GlobalConfig.pcache2.xShutdown ){
|
||||
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
|
||||
sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
|
||||
sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ void sqlite3PcacheOpen(
|
|||
p->bPurgeable = bPurgeable;
|
||||
p->xStress = xStress;
|
||||
p->pStress = pStress;
|
||||
p->nMax = 100;
|
||||
p->szCache = 100;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -191,13 +191,24 @@ void sqlite3PcacheOpen(
|
|||
void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
|
||||
assert( pCache->nRef==0 && pCache->pDirty==0 );
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
|
||||
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
||||
pCache->pCache = 0;
|
||||
pCache->pPage1 = 0;
|
||||
}
|
||||
pCache->szPage = szPage;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the number of pages of cache requested.
|
||||
*/
|
||||
static int numberOfCachePages(PCache *p){
|
||||
if( p->szCache>=0 ){
|
||||
return p->szCache;
|
||||
}else{
|
||||
return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Try to obtain a page from the cache.
|
||||
*/
|
||||
|
@ -207,7 +218,8 @@ int sqlite3PcacheFetch(
|
|||
int createFlag, /* If true, create page if it does not exist already */
|
||||
PgHdr **ppPage /* Write the page here */
|
||||
){
|
||||
PgHdr *pPage = 0;
|
||||
sqlite3_pcache_page *pPage = 0;
|
||||
PgHdr *pPgHdr = 0;
|
||||
int eCreate;
|
||||
|
||||
assert( pCache!=0 );
|
||||
|
@ -219,19 +231,19 @@ int sqlite3PcacheFetch(
|
|||
*/
|
||||
if( !pCache->pCache && createFlag ){
|
||||
sqlite3_pcache *p;
|
||||
int nByte;
|
||||
nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr);
|
||||
p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable);
|
||||
p = sqlite3GlobalConfig.pcache2.xCreate(
|
||||
pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
|
||||
);
|
||||
if( !p ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax);
|
||||
sqlite3GlobalConfig.pcache2.xCachesize(p, numberOfCachePages(pCache));
|
||||
pCache->pCache = p;
|
||||
}
|
||||
|
||||
eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
|
||||
if( pCache->pCache ){
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate);
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
|
||||
}
|
||||
|
||||
if( !pPage && eCreate==1 ){
|
||||
|
@ -258,7 +270,7 @@ int sqlite3PcacheFetch(
|
|||
"spill page %d making room for %d - cache used: %d/%d",
|
||||
pPg->pgno, pgno,
|
||||
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
|
||||
pCache->nMax);
|
||||
numberOfCachePages(pCache));
|
||||
#endif
|
||||
rc = pCache->xStress(pCache->pStress, pPg);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
|
||||
|
@ -266,33 +278,36 @@ int sqlite3PcacheFetch(
|
|||
}
|
||||
}
|
||||
|
||||
pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2);
|
||||
pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
|
||||
}
|
||||
|
||||
if( pPage ){
|
||||
if( !pPage->pData ){
|
||||
memset(pPage, 0, sizeof(PgHdr));
|
||||
pPage->pData = (void *)&pPage[1];
|
||||
pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage];
|
||||
memset(pPage->pExtra, 0, pCache->szExtra);
|
||||
pPage->pCache = pCache;
|
||||
pPage->pgno = pgno;
|
||||
}
|
||||
assert( pPage->pCache==pCache );
|
||||
assert( pPage->pgno==pgno );
|
||||
assert( pPage->pData==(void *)&pPage[1] );
|
||||
assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] );
|
||||
pPgHdr = (PgHdr *)pPage->pExtra;
|
||||
|
||||
if( 0==pPage->nRef ){
|
||||
if( !pPgHdr->pPage ){
|
||||
memset(pPgHdr, 0, sizeof(PgHdr));
|
||||
pPgHdr->pPage = pPage;
|
||||
pPgHdr->pData = pPage->pBuf;
|
||||
pPgHdr->pExtra = (void *)&pPgHdr[1];
|
||||
memset(pPgHdr->pExtra, 0, pCache->szExtra);
|
||||
pPgHdr->pCache = pCache;
|
||||
pPgHdr->pgno = pgno;
|
||||
}
|
||||
assert( pPgHdr->pCache==pCache );
|
||||
assert( pPgHdr->pgno==pgno );
|
||||
assert( pPgHdr->pData==pPage->pBuf );
|
||||
assert( pPgHdr->pExtra==(void *)&pPgHdr[1] );
|
||||
|
||||
if( 0==pPgHdr->nRef ){
|
||||
pCache->nRef++;
|
||||
}
|
||||
pPage->nRef++;
|
||||
pPgHdr->nRef++;
|
||||
if( pgno==1 ){
|
||||
pCache->pPage1 = pPage;
|
||||
pCache->pPage1 = pPgHdr;
|
||||
}
|
||||
}
|
||||
*ppPage = pPage;
|
||||
return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
|
||||
*ppPage = pPgHdr;
|
||||
return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -339,7 +354,7 @@ void sqlite3PcacheDrop(PgHdr *p){
|
|||
if( p->pgno==1 ){
|
||||
pCache->pPage1 = 0;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1);
|
||||
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -397,7 +412,7 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
|||
PCache *pCache = p->pCache;
|
||||
assert( p->nRef>0 );
|
||||
assert( newPgno>0 );
|
||||
sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno);
|
||||
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
||||
p->pgno = newPgno;
|
||||
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
||||
pcacheRemoveFromDirtyList(p);
|
||||
|
@ -434,7 +449,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
|||
memset(pCache->pPage1->pData, 0, pCache->szPage);
|
||||
pgno = 1;
|
||||
}
|
||||
sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1);
|
||||
sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,7 +458,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
|
|||
*/
|
||||
void sqlite3PcacheClose(PCache *pCache){
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
|
||||
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,7 +570,7 @@ int sqlite3PcachePageRefcount(PgHdr *p){
|
|||
int sqlite3PcachePagecount(PCache *pCache){
|
||||
int nPage = 0;
|
||||
if( pCache->pCache ){
|
||||
nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache);
|
||||
nPage = sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache);
|
||||
}
|
||||
return nPage;
|
||||
}
|
||||
|
@ -565,7 +580,7 @@ int sqlite3PcachePagecount(PCache *pCache){
|
|||
** Get the suggested cache-size value.
|
||||
*/
|
||||
int sqlite3PcacheGetCachesize(PCache *pCache){
|
||||
return pCache->nMax;
|
||||
return numberOfCachePages(pCache);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -573,9 +588,19 @@ int sqlite3PcacheGetCachesize(PCache *pCache){
|
|||
** Set the suggested cache-size value.
|
||||
*/
|
||||
void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
|
||||
pCache->nMax = mxPage;
|
||||
pCache->szCache = mxPage;
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage);
|
||||
sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache,
|
||||
numberOfCachePages(pCache));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Free up as much memory as possible from the page cache.
|
||||
*/
|
||||
void sqlite3PcacheShrink(PCache *pCache){
|
||||
if( pCache->pCache ){
|
||||
sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ typedef struct PCache PCache;
|
|||
** structure.
|
||||
*/
|
||||
struct PgHdr {
|
||||
void *pData; /* Content of this page */
|
||||
sqlite3_pcache_page *pPage; /* Pcache object page handle */
|
||||
void *pData; /* Page data */
|
||||
void *pExtra; /* Extra content */
|
||||
PgHdr *pDirty; /* Transient list of dirty pages */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
|
@ -141,6 +142,9 @@ void sqlite3PcacheSetCachesize(PCache *, int);
|
|||
int sqlite3PcacheGetCachesize(PCache *);
|
||||
#endif
|
||||
|
||||
/* Free up as much memory as possible from the page cache */
|
||||
void sqlite3PcacheShrink(PCache*);
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
/* Try to return memory used by the pcache module to the main memory heap */
|
||||
int sqlite3PcacheReleaseMemory(int);
|
||||
|
|
167
src/pcache1.c
167
src/pcache1.c
|
@ -24,7 +24,6 @@ typedef struct PgHdr1 PgHdr1;
|
|||
typedef struct PgFreeslot PgFreeslot;
|
||||
typedef struct PGroup PGroup;
|
||||
|
||||
|
||||
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
|
||||
** of one or more PCaches that are able to recycle each others unpinned
|
||||
** pages when they are under memory pressure. A PGroup is an instance of
|
||||
|
@ -41,7 +40,7 @@ typedef struct PGroup PGroup;
|
|||
** Mode 1 uses more memory (since PCache instances are not able to rob
|
||||
** unused pages from other PCaches) but it also operates without a mutex,
|
||||
** and is therefore often faster. Mode 2 requires a mutex in order to be
|
||||
** threadsafe, but is able recycle pages more efficient.
|
||||
** threadsafe, but recycles pages more efficiently.
|
||||
**
|
||||
** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
|
||||
** PGroup which is the pcache1.grp global variable and its mutex is
|
||||
|
@ -49,10 +48,10 @@ typedef struct PGroup PGroup;
|
|||
*/
|
||||
struct PGroup {
|
||||
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
|
||||
int nMaxPage; /* Sum of nMax for purgeable caches */
|
||||
int nMinPage; /* Sum of nMin for purgeable caches */
|
||||
int mxPinned; /* nMaxpage + 10 - nMinPage */
|
||||
int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
|
||||
unsigned int nMinPage; /* Sum of nMin for purgeable caches */
|
||||
unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
|
||||
unsigned int nCurrentPage; /* Number of purgeable pages allocated */
|
||||
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
|
||||
};
|
||||
|
||||
|
@ -67,11 +66,12 @@ struct PGroup {
|
|||
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.
|
||||
** modified at any time by a call to the pcache1Cachesize() method.
|
||||
** The PGroup mutex must be held when accessing nMax.
|
||||
*/
|
||||
PGroup *pGroup; /* PGroup this cache belongs to */
|
||||
int szPage; /* Size of allocated pages in bytes */
|
||||
int szExtra; /* Size of extra space in bytes */
|
||||
int bPurgeable; /* True if cache is purgeable */
|
||||
unsigned int nMin; /* Minimum number of pages reserved */
|
||||
unsigned int nMax; /* Configured "cache_size" value */
|
||||
|
@ -90,11 +90,12 @@ struct PCache1 {
|
|||
|
||||
/*
|
||||
** Each cache entry is represented by an instance of the following
|
||||
** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
|
||||
** directly before this structure in memory (see the PGHDR1_TO_PAGE()
|
||||
** macro below).
|
||||
** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
|
||||
** PgHdr1.pCache->szPage bytes is allocated directly before this structure
|
||||
** in memory.
|
||||
*/
|
||||
struct PgHdr1 {
|
||||
sqlite3_pcache_page page;
|
||||
unsigned int iKey; /* Key value (page number) */
|
||||
PgHdr1 *pNext; /* Next in hash table chain */
|
||||
PCache1 *pCache; /* Cache that currently owns this page */
|
||||
|
@ -144,21 +145,6 @@ static SQLITE_WSD struct PCacheGlobal {
|
|||
*/
|
||||
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
|
||||
|
||||
/*
|
||||
** When a PgHdr1 structure is allocated, the associated PCache1.szPage
|
||||
** bytes of data are located directly before 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(pCache, X))==X );
|
||||
*/
|
||||
#define PGHDR1_TO_PAGE(p) (void*)(((char*)p) - p->pCache->szPage)
|
||||
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
|
||||
|
||||
/*
|
||||
** Macros to enter and leave the PCache LRU mutex.
|
||||
*/
|
||||
|
@ -241,8 +227,9 @@ static void *pcache1Alloc(int nByte){
|
|||
/*
|
||||
** Free an allocated buffer obtained from pcache1Alloc().
|
||||
*/
|
||||
static void pcache1Free(void *p){
|
||||
if( p==0 ) return;
|
||||
static int pcache1Free(void *p){
|
||||
int nFreed = 0;
|
||||
if( p==0 ) return 0;
|
||||
if( p>=pcache1.pStart && p<pcache1.pEnd ){
|
||||
PgFreeslot *pSlot;
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
|
@ -255,15 +242,15 @@ static void pcache1Free(void *p){
|
|||
assert( pcache1.nFreeSlot<=pcache1.nSlot );
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
}else{
|
||||
int iSize;
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
iSize = sqlite3MallocSize(p);
|
||||
nFreed = sqlite3MallocSize(p);
|
||||
sqlite3_mutex_enter(pcache1.mutex);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
|
||||
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed);
|
||||
sqlite3_mutex_leave(pcache1.mutex);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
return nFreed;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
|
@ -288,7 +275,6 @@ static int pcache1MemSize(void *p){
|
|||
** Allocate a new page object initially associated with cache pCache.
|
||||
*/
|
||||
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
||||
int nByte = sizeof(PgHdr1) + pCache->szPage;
|
||||
PgHdr1 *p = 0;
|
||||
void *pPg;
|
||||
|
||||
|
@ -297,16 +283,29 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
|
|||
** this mutex is not held. */
|
||||
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
|
||||
pcache1LeaveMutex(pCache->pGroup);
|
||||
pPg = pcache1Alloc(nByte);
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
pPg = pcache1Alloc(pCache->szPage);
|
||||
p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
|
||||
if( !pPg || !p ){
|
||||
pcache1Free(pPg);
|
||||
sqlite3_free(p);
|
||||
pPg = 0;
|
||||
}
|
||||
#else
|
||||
pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra);
|
||||
p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
|
||||
#endif
|
||||
pcache1EnterMutex(pCache->pGroup);
|
||||
|
||||
if( pPg ){
|
||||
p = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
p->page.pBuf = pPg;
|
||||
p->page.pExtra = &p[1];
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
return p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -320,7 +319,10 @@ static void pcache1FreePage(PgHdr1 *p){
|
|||
if( ALWAYS(p) ){
|
||||
PCache1 *pCache = p->pCache;
|
||||
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
|
||||
pcache1Free(PGHDR1_TO_PAGE(p));
|
||||
pcache1Free(p->page.pBuf);
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
sqlite3_free(p);
|
||||
#endif
|
||||
if( pCache->bPurgeable ){
|
||||
pCache->pGroup->nCurrentPage--;
|
||||
}
|
||||
|
@ -355,13 +357,13 @@ void sqlite3PageFree(void *p){
|
|||
** for all page cache needs and we should not need to spill the
|
||||
** allocation onto the heap.
|
||||
**
|
||||
** Or, the heap is used for all page cache memory put the heap is
|
||||
** Or, the heap is used for all page cache memory but the heap is
|
||||
** under memory pressure, then again it is desirable to avoid
|
||||
** allocating a new page cache entry in order to avoid stressing
|
||||
** the heap even further.
|
||||
*/
|
||||
static int pcache1UnderMemoryPressure(PCache1 *pCache){
|
||||
if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
|
||||
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
|
||||
return pcache1.bUnderPressure;
|
||||
}else{
|
||||
return sqlite3HeapNearlyFull();
|
||||
|
@ -552,7 +554,7 @@ static void pcache1Shutdown(void *NotUsed){
|
|||
**
|
||||
** Allocate a new cache.
|
||||
*/
|
||||
static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
||||
static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
|
||||
PCache1 *pCache; /* The newly created page cache */
|
||||
PGroup *pGroup; /* The group the new page cache will belong to */
|
||||
int sz; /* Bytes of memory required to allocate the new cache */
|
||||
|
@ -575,6 +577,9 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
|||
int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
|
||||
#endif
|
||||
|
||||
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
|
||||
assert( szExtra < 300 );
|
||||
|
||||
sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
|
||||
pCache = (PCache1 *)sqlite3_malloc(sz);
|
||||
if( pCache ){
|
||||
|
@ -587,6 +592,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
|
|||
}
|
||||
pCache->pGroup = pGroup;
|
||||
pCache->szPage = szPage;
|
||||
pCache->szExtra = szExtra;
|
||||
pCache->bPurgeable = (bPurgeable ? 1 : 0);
|
||||
if( bPurgeable ){
|
||||
pCache->nMin = 10;
|
||||
|
@ -618,6 +624,25 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xShrink method.
|
||||
**
|
||||
** Free up as much memory as possible.
|
||||
*/
|
||||
static void pcache1Shrink(sqlite3_pcache *p){
|
||||
PCache1 *pCache = (PCache1*)p;
|
||||
if( pCache->bPurgeable ){
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
int savedMaxPage;
|
||||
pcache1EnterMutex(pGroup);
|
||||
savedMaxPage = pGroup->nMaxPage;
|
||||
pGroup->nMaxPage = 0;
|
||||
pcache1EnforceMaxPage(pGroup);
|
||||
pGroup->nMaxPage = savedMaxPage;
|
||||
pcache1LeaveMutex(pGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sqlite3_pcache.xPagecount method.
|
||||
*/
|
||||
|
@ -643,7 +668,7 @@ static int pcache1Pagecount(sqlite3_pcache *p){
|
|||
** For a non-purgeable cache (a cache used as the storage for an in-memory
|
||||
** database) there is really no difference between createFlag 1 and 2. So
|
||||
** the calling function (pcache.c) will never have a createFlag of 1 on
|
||||
** a non-purgable cache.
|
||||
** a non-purgeable cache.
|
||||
**
|
||||
** 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).
|
||||
|
@ -684,8 +709,12 @@ static int pcache1Pagecount(sqlite3_pcache *p){
|
|||
**
|
||||
** 5. Otherwise, allocate and return a new page buffer.
|
||||
*/
|
||||
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
||||
int nPinned;
|
||||
static sqlite3_pcache_page *pcache1Fetch(
|
||||
sqlite3_pcache *p,
|
||||
unsigned int iKey,
|
||||
int createFlag
|
||||
){
|
||||
unsigned int nPinned;
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PGroup *pGroup;
|
||||
PgHdr1 *pPage = 0;
|
||||
|
@ -719,15 +748,14 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
|||
pGroup = pCache->pGroup;
|
||||
#endif
|
||||
|
||||
|
||||
/* Step 3: Abort if createFlag is 1 but the cache is nearly full */
|
||||
assert( pCache->nPage >= pCache->nRecyclable );
|
||||
nPinned = pCache->nPage - pCache->nRecyclable;
|
||||
assert( nPinned>=0 );
|
||||
assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
|
||||
assert( pCache->n90pct == pCache->nMax*9/10 );
|
||||
if( createFlag==1 && (
|
||||
nPinned>=pGroup->mxPinned
|
||||
|| nPinned>=(int)pCache->n90pct
|
||||
|| nPinned>=pCache->n90pct
|
||||
|| pcache1UnderMemoryPressure(pCache)
|
||||
)){
|
||||
goto fetch_out;
|
||||
|
@ -743,16 +771,24 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
|||
|| pGroup->nCurrentPage>=pGroup->nMaxPage
|
||||
|| pcache1UnderMemoryPressure(pCache)
|
||||
)){
|
||||
PCache1 *pOtherCache;
|
||||
PCache1 *pOther;
|
||||
pPage = pGroup->pLruTail;
|
||||
pcache1RemoveFromHash(pPage);
|
||||
pcache1PinPage(pPage);
|
||||
if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
|
||||
pOther = pPage->pCache;
|
||||
|
||||
/* We want to verify that szPage and szExtra are the same for pOther
|
||||
** and pCache. Assert that we can verify this by comparing sums. */
|
||||
assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 );
|
||||
assert( pCache->szExtra<512 );
|
||||
assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 );
|
||||
assert( pOther->szExtra<512 );
|
||||
|
||||
if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){
|
||||
pcache1FreePage(pPage);
|
||||
pPage = 0;
|
||||
}else{
|
||||
pGroup->nCurrentPage -=
|
||||
(pOtherCache->bPurgeable - pCache->bPurgeable);
|
||||
pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,7 +809,7 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|
|||
pPage->pCache = pCache;
|
||||
pPage->pLruPrev = 0;
|
||||
pPage->pLruNext = 0;
|
||||
*(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
|
||||
*(void **)pPage->page.pExtra = 0;
|
||||
pCache->apHash[h] = pPage;
|
||||
}
|
||||
|
||||
|
@ -782,7 +818,7 @@ fetch_out:
|
|||
pCache->iMaxKey = iKey;
|
||||
}
|
||||
pcache1LeaveMutex(pGroup);
|
||||
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
|
||||
return &pPage->page;
|
||||
}
|
||||
|
||||
|
||||
|
@ -791,9 +827,13 @@ fetch_out:
|
|||
**
|
||||
** Mark a page as unpinned (eligible for asynchronous recycling).
|
||||
*/
|
||||
static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
|
||||
static void pcache1Unpin(
|
||||
sqlite3_pcache *p,
|
||||
sqlite3_pcache_page *pPg,
|
||||
int reuseUnlikely
|
||||
){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
PgHdr1 *pPage = (PgHdr1 *)pPg;
|
||||
PGroup *pGroup = pCache->pGroup;
|
||||
|
||||
assert( pPage->pCache==pCache );
|
||||
|
@ -829,12 +869,12 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
|
|||
*/
|
||||
static void pcache1Rekey(
|
||||
sqlite3_pcache *p,
|
||||
void *pPg,
|
||||
sqlite3_pcache_page *pPg,
|
||||
unsigned int iOld,
|
||||
unsigned int iNew
|
||||
){
|
||||
PCache1 *pCache = (PCache1 *)p;
|
||||
PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
|
||||
PgHdr1 *pPage = (PgHdr1 *)pPg;
|
||||
PgHdr1 **pp;
|
||||
unsigned int h;
|
||||
assert( pPage->iKey==iOld );
|
||||
|
@ -888,7 +928,9 @@ static void pcache1Destroy(sqlite3_pcache *p){
|
|||
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
|
||||
pcache1EnterMutex(pGroup);
|
||||
pcache1TruncateUnsafe(pCache, 0);
|
||||
assert( pGroup->nMaxPage >= pCache->nMax );
|
||||
pGroup->nMaxPage -= pCache->nMax;
|
||||
assert( pGroup->nMinPage >= pCache->nMin );
|
||||
pGroup->nMinPage -= pCache->nMin;
|
||||
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
|
||||
pcache1EnforceMaxPage(pGroup);
|
||||
|
@ -903,7 +945,8 @@ static void pcache1Destroy(sqlite3_pcache *p){
|
|||
** already provided an alternative.
|
||||
*/
|
||||
void sqlite3PCacheSetDefault(void){
|
||||
static const sqlite3_pcache_methods defaultMethods = {
|
||||
static const sqlite3_pcache_methods2 defaultMethods = {
|
||||
1, /* iVersion */
|
||||
0, /* pArg */
|
||||
pcache1Init, /* xInit */
|
||||
pcache1Shutdown, /* xShutdown */
|
||||
|
@ -914,9 +957,10 @@ void sqlite3PCacheSetDefault(void){
|
|||
pcache1Unpin, /* xUnpin */
|
||||
pcache1Rekey, /* xRekey */
|
||||
pcache1Truncate, /* xTruncate */
|
||||
pcache1Destroy /* xDestroy */
|
||||
pcache1Destroy, /* xDestroy */
|
||||
pcache1Shrink /* xShrink */
|
||||
};
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
|
@ -937,7 +981,10 @@ int sqlite3PcacheReleaseMemory(int nReq){
|
|||
PgHdr1 *p;
|
||||
pcache1EnterMutex(&pcache1.grp);
|
||||
while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
|
||||
nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
|
||||
nFree += pcache1MemSize(p->page.pBuf);
|
||||
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
|
||||
nFree += sqlite3MemSize(p);
|
||||
#endif
|
||||
pcache1PinPage(p);
|
||||
pcache1RemoveFromHash(p);
|
||||
pcache1FreePage(p);
|
||||
|
@ -965,8 +1012,8 @@ void sqlite3PcacheStats(
|
|||
nRecyclable++;
|
||||
}
|
||||
*pnCurrent = pcache1.grp.nCurrentPage;
|
||||
*pnMax = pcache1.grp.nMaxPage;
|
||||
*pnMin = pcache1.grp.nMinPage;
|
||||
*pnMax = (int)pcache1.grp.nMaxPage;
|
||||
*pnMin = (int)pcache1.grp.nMinPage;
|
||||
*pnRecyclable = nRecyclable;
|
||||
}
|
||||
#endif
|
||||
|
|
40
src/pragma.c
40
src/pragma.c
|
@ -346,7 +346,7 @@ void sqlite3Pragma(
|
|||
goto pragma_out;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
|
||||
/*
|
||||
** PRAGMA [database.]default_cache_size
|
||||
** PRAGMA [database.]default_cache_size=N
|
||||
|
@ -395,7 +395,9 @@ void sqlite3Pragma(
|
|||
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
|
||||
}
|
||||
}else
|
||||
#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */
|
||||
|
||||
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
|
||||
/*
|
||||
** PRAGMA [database.]page_size
|
||||
** PRAGMA [database.]page_size=N
|
||||
|
@ -416,7 +418,7 @@ void sqlite3Pragma(
|
|||
** buffer that the pager module resizes using sqlite3_realloc().
|
||||
*/
|
||||
db->nextPagesize = sqlite3Atoi(zRight);
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
|
||||
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
}
|
||||
|
@ -456,6 +458,10 @@ void sqlite3Pragma(
|
|||
** second form attempts to change this setting. Both
|
||||
** forms return the current setting.
|
||||
**
|
||||
** The absolute value of N is used. This is undocumented and might
|
||||
** change. The only purpose is to provide an easy way to test
|
||||
** the sqlite3AbsInt32() function.
|
||||
**
|
||||
** PRAGMA [database.]page_count
|
||||
**
|
||||
** Return the number of pages in the specified database.
|
||||
|
@ -470,7 +476,8 @@ void sqlite3Pragma(
|
|||
if( sqlite3Tolower(zLeft[0])=='p' ){
|
||||
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight));
|
||||
sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg,
|
||||
sqlite3AbsInt32(sqlite3Atoi(zRight)));
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
|
@ -684,14 +691,11 @@ void sqlite3Pragma(
|
|||
** PRAGMA [database.]cache_size=N
|
||||
**
|
||||
** The first form reports the current local setting for the
|
||||
** page cache size. The local setting can be different from
|
||||
** the persistent cache size value that is stored in the database
|
||||
** file itself. The value returned is the maximum number of
|
||||
** pages in the page cache. The second form sets the local
|
||||
** page cache size value. It does not change the persistent
|
||||
** cache size stored on the disk so the cache size will revert
|
||||
** to its default value when the database is closed and reopened.
|
||||
** N should be a positive integer.
|
||||
** page cache size. The second form sets the local
|
||||
** page cache size value. If N is positive then that is the
|
||||
** number of pages in the cache. If N is negative, then the
|
||||
** number of pages is adjusted so that the cache uses -N kibibytes
|
||||
** of memory.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
|
||||
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
|
||||
|
@ -699,7 +703,7 @@ void sqlite3Pragma(
|
|||
if( !zRight ){
|
||||
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
|
||||
}else{
|
||||
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
|
||||
int size = sqlite3Atoi(zRight);
|
||||
pDb->pSchema->cache_size = size;
|
||||
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
|
||||
}
|
||||
|
@ -791,7 +795,7 @@ void sqlite3Pragma(
|
|||
Pager *pPager = sqlite3BtreePager(pDb->pBt);
|
||||
char *proxy_file_path = NULL;
|
||||
sqlite3_file *pFile = sqlite3PagerFile(pPager);
|
||||
sqlite3OsFileControl(pFile, SQLITE_GET_LOCKPROXYFILE,
|
||||
sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
|
||||
&proxy_file_path);
|
||||
|
||||
if( proxy_file_path ){
|
||||
|
@ -1434,6 +1438,16 @@ void sqlite3Pragma(
|
|||
}else
|
||||
#endif
|
||||
|
||||
/*
|
||||
** PRAGMA shrink_memory
|
||||
**
|
||||
** This pragma attempts to free as much memory as possible from the
|
||||
** current database connection.
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "shrink_memory")==0 ){
|
||||
sqlite3_db_release_memory(db);
|
||||
}else
|
||||
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
/*
|
||||
** Report the current state of file logs for all databases
|
||||
|
|
|
@ -278,9 +278,13 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
|||
pDb->pSchema->enc = ENC(db);
|
||||
|
||||
if( pDb->pSchema->cache_size==0 ){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]);
|
||||
if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; }
|
||||
pDb->pSchema->cache_size = size;
|
||||
#else
|
||||
pDb->pSchema->cache_size = SQLITE_DEFAULT_CACHE_SIZE;
|
||||
#endif
|
||||
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
|||
/*
|
||||
** Append N space characters to the given string buffer.
|
||||
*/
|
||||
static void appendSpace(StrAccum *pAccum, int N){
|
||||
void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
||||
static const char zSpaces[] = " ";
|
||||
while( N>=(int)sizeof(zSpaces)-1 ){
|
||||
sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
||||
|
@ -664,7 +664,7 @@ void sqlite3VXPrintf(
|
|||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
appendSpace(pAccum, nspace);
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
if( length>0 ){
|
||||
|
@ -674,7 +674,7 @@ void sqlite3VXPrintf(
|
|||
register int nspace;
|
||||
nspace = width-length;
|
||||
if( nspace>0 ){
|
||||
appendSpace(pAccum, nspace);
|
||||
sqlite3AppendSpace(pAccum, nspace);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zExtra);
|
||||
|
|
|
@ -799,7 +799,7 @@ static int resolveCompoundOrderBy(
|
|||
pE->pColl = pColl;
|
||||
pE->flags |= EP_IntValue | flags;
|
||||
pE->u.iValue = iCol;
|
||||
pItem->iCol = (u16)iCol;
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
pItem->done = 1;
|
||||
}else{
|
||||
moreToDo = 1;
|
||||
|
@ -848,12 +848,12 @@ int sqlite3ResolveOrderGroupBy(
|
|||
pEList = pSelect->pEList;
|
||||
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
|
||||
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
|
||||
if( pItem->iCol ){
|
||||
if( pItem->iCol>pEList->nExpr ){
|
||||
if( pItem->iOrderByCol ){
|
||||
if( pItem->iOrderByCol>pEList->nExpr ){
|
||||
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
|
||||
return 1;
|
||||
}
|
||||
resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
|
||||
resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -900,7 +900,7 @@ static int resolveOrderGroupBy(
|
|||
** a copy of the iCol-th result-set column. The subsequent call to
|
||||
** sqlite3ResolveOrderGroupBy() will convert the expression to a
|
||||
** copy of the iCol-th result-set expression. */
|
||||
pItem->iCol = (u16)iCol;
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
continue;
|
||||
}
|
||||
if( sqlite3ExprIsInteger(pE, &iCol) ){
|
||||
|
@ -911,12 +911,12 @@ static int resolveOrderGroupBy(
|
|||
resolveOutOfRangeError(pParse, zType, i+1, nResult);
|
||||
return 1;
|
||||
}
|
||||
pItem->iCol = (u16)iCol;
|
||||
pItem->iOrderByCol = (u16)iCol;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise, treat the ORDER BY term as an ordinary expression */
|
||||
pItem->iCol = 0;
|
||||
pItem->iOrderByCol = 0;
|
||||
if( sqlite3ResolveExprNames(pNC, pE) ){
|
||||
return 1;
|
||||
}
|
||||
|
|
179
src/select.c
179
src/select.c
|
@ -2219,8 +2219,8 @@ static int multiSelectOrderBy(
|
|||
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
|
||||
struct ExprList_item *pItem;
|
||||
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
|
||||
assert( pItem->iCol>0 );
|
||||
if( pItem->iCol==i ) break;
|
||||
assert( pItem->iOrderByCol>0 );
|
||||
if( pItem->iOrderByCol==i ) break;
|
||||
}
|
||||
if( j==nOrderBy ){
|
||||
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
|
||||
|
@ -2228,7 +2228,7 @@ static int multiSelectOrderBy(
|
|||
pNew->flags |= EP_IntValue;
|
||||
pNew->u.iValue = i;
|
||||
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
|
||||
pOrderBy->a[nOrderBy++].iCol = (u16)i;
|
||||
pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2244,8 +2244,8 @@ static int multiSelectOrderBy(
|
|||
if( aPermute ){
|
||||
struct ExprList_item *pItem;
|
||||
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
|
||||
assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
|
||||
aPermute[i] = pItem->iCol - 1;
|
||||
assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
|
||||
aPermute[i] = pItem->iOrderByCol - 1;
|
||||
}
|
||||
pKeyMerge =
|
||||
sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
|
||||
|
@ -2588,9 +2588,8 @@ static void substSelect(
|
|||
|
||||
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
|
||||
/*
|
||||
** This routine attempts to flatten subqueries in order to speed
|
||||
** execution. It returns 1 if it makes changes and 0 if no flattening
|
||||
** occurs.
|
||||
** This routine attempts to flatten subqueries as a performance optimization.
|
||||
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
|
||||
**
|
||||
** To understand the concept of flattening, consider the following
|
||||
** query:
|
||||
|
@ -2632,7 +2631,10 @@ static void substSelect(
|
|||
** (6) The subquery does not use aggregates or the outer query is not
|
||||
** DISTINCT.
|
||||
**
|
||||
** (7) The subquery has a FROM clause.
|
||||
** (7) The subquery has a FROM clause. TODO: For subqueries without
|
||||
** A FROM clause, consider adding a FROM close with the special
|
||||
** table sqlite_once that consists of a single row containing a
|
||||
** single NULL.
|
||||
**
|
||||
** (8) The subquery does not use LIMIT or the outer query is not a join.
|
||||
**
|
||||
|
@ -2665,11 +2667,14 @@ static void substSelect(
|
|||
**
|
||||
** * is not itself part of a compound select,
|
||||
** * is not an aggregate or DISTINCT query, and
|
||||
** * has no other tables or sub-selects in the FROM clause.
|
||||
** * is not a join
|
||||
**
|
||||
** The parent and sub-query may contain WHERE clauses. Subject to
|
||||
** rules (11), (13) and (14), they may also contain ORDER BY,
|
||||
** LIMIT and OFFSET clauses.
|
||||
** LIMIT and OFFSET clauses. The subquery cannot use any compound
|
||||
** operator other than UNION ALL because all the other compound
|
||||
** operators have an implied DISTINCT which is disallowed by
|
||||
** restriction (4).
|
||||
**
|
||||
** (18) If the sub-query is a compound select, then all terms of the
|
||||
** ORDER by clause of the parent must be simple references to
|
||||
|
@ -2681,7 +2686,7 @@ static void substSelect(
|
|||
** (20) If the sub-query is a compound select, then it must not use
|
||||
** an ORDER BY clause. Ticket #3773. We could relax this constraint
|
||||
** somewhat by saying that the terms of the ORDER BY clause must
|
||||
** appear as unmodified result columns in the outer query. But
|
||||
** appear as unmodified result columns in the outer query. But we
|
||||
** have other optimizations in mind to deal with that case.
|
||||
**
|
||||
** (21) The subquery does not use LIMIT or the outer query is not
|
||||
|
@ -2810,19 +2815,21 @@ static int flattenSubquery(
|
|||
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
|
||||
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
|
||||
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
|
||||
assert( pSub->pSrc!=0 );
|
||||
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|
||||
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
|
||||
|| NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1
|
||||
|| pSub1->pSrc->nSrc<1
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
testcase( pSub1->pSrc->nSrc>1 );
|
||||
}
|
||||
|
||||
/* Restriction 18. */
|
||||
if( p->pOrderBy ){
|
||||
int ii;
|
||||
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
|
||||
if( p->pOrderBy->a[ii].iCol==0 ) return 0;
|
||||
if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3845,12 +3852,11 @@ int sqlite3Select(
|
|||
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
|
||||
pItem->addrFillSub = topAddr+1;
|
||||
VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
|
||||
if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
|
||||
if( pItem->isCorrelated==0 ){
|
||||
/* If the subquery is no correlated and if we are not inside of
|
||||
** a trigger, then we only need to compute the value of the subquery
|
||||
** once. */
|
||||
int regOnce = ++pParse->nMem;
|
||||
onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
|
||||
onceAddr = sqlite3CodeOnce(pParse);
|
||||
}
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
|
@ -3860,7 +3866,7 @@ int sqlite3Select(
|
|||
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
|
||||
VdbeComment((v, "end %s", pItem->pTab->zName));
|
||||
sqlite3VdbeChangeP1(v, topAddr, retAddr);
|
||||
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
}
|
||||
if( /*pParse->nErr ||*/ db->mallocFailed ){
|
||||
goto select_end;
|
||||
|
@ -4155,6 +4161,7 @@ int sqlite3Select(
|
|||
VdbeComment((v, "clear abort flag"));
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
|
||||
VdbeComment((v, "indicate accumulator empty"));
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
|
||||
|
||||
/* Begin a loop that will extract all source rows in GROUP BY order.
|
||||
** This might involve two separate loops with an OP_Sort in between, or
|
||||
|
@ -4494,98 +4501,98 @@ select_end:
|
|||
return rc;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
/*
|
||||
*******************************************************************************
|
||||
** The following code is used for testing and debugging only. The code
|
||||
** that follows does not appear in normal builds.
|
||||
**
|
||||
** These routines are used to print out the content of all or part of a
|
||||
** parse structures such as Select or Expr. Such printouts are useful
|
||||
** for helping to understand what is happening inside the code generator
|
||||
** during the execution of complex SELECT statements.
|
||||
**
|
||||
** These routine are not called anywhere from within the normal
|
||||
** code base. Then are intended to be called from within the debugger
|
||||
** or from temporary "printf" statements inserted for debugging.
|
||||
** Generate a human-readable description of a the Select object.
|
||||
*/
|
||||
void sqlite3PrintExpr(Expr *p){
|
||||
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
|
||||
sqlite3DebugPrintf("(%s", p->u.zToken);
|
||||
}else{
|
||||
sqlite3DebugPrintf("(%d", p->op);
|
||||
}
|
||||
if( p->pLeft ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pLeft);
|
||||
}
|
||||
if( p->pRight ){
|
||||
sqlite3DebugPrintf(" ");
|
||||
sqlite3PrintExpr(p->pRight);
|
||||
}
|
||||
sqlite3DebugPrintf(")");
|
||||
}
|
||||
void sqlite3PrintExprList(ExprList *pList){
|
||||
int i;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
sqlite3PrintExpr(pList->a[i].pExpr);
|
||||
if( i<pList->nExpr-1 ){
|
||||
sqlite3DebugPrintf(", ");
|
||||
static void explainOneSelect(Vdbe *pVdbe, Select *p){
|
||||
sqlite3ExplainPrintf(pVdbe, "SELECT ");
|
||||
if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
|
||||
if( p->selFlags & SF_Distinct ){
|
||||
sqlite3ExplainPrintf(pVdbe, "DISTINCT ");
|
||||
}
|
||||
if( p->selFlags & SF_Aggregate ){
|
||||
sqlite3ExplainPrintf(pVdbe, "agg_flag ");
|
||||
}
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
sqlite3ExplainPrintf(pVdbe, " ");
|
||||
}
|
||||
}
|
||||
void sqlite3PrintSelect(Select *p, int indent){
|
||||
sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
|
||||
sqlite3PrintExprList(p->pEList);
|
||||
sqlite3DebugPrintf("\n");
|
||||
if( p->pSrc ){
|
||||
char *zPrefix;
|
||||
sqlite3ExplainExprList(pVdbe, p->pEList);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
if( p->pSrc && p->pSrc->nSrc ){
|
||||
int i;
|
||||
zPrefix = "FROM";
|
||||
sqlite3ExplainPrintf(pVdbe, "FROM ");
|
||||
sqlite3ExplainPush(pVdbe);
|
||||
for(i=0; i<p->pSrc->nSrc; i++){
|
||||
struct SrcList_item *pItem = &p->pSrc->a[i];
|
||||
sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
|
||||
zPrefix = "";
|
||||
sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor);
|
||||
if( pItem->pSelect ){
|
||||
sqlite3DebugPrintf("(\n");
|
||||
sqlite3PrintSelect(pItem->pSelect, indent+10);
|
||||
sqlite3DebugPrintf("%*s)", indent+8, "");
|
||||
sqlite3ExplainSelect(pVdbe, pItem->pSelect);
|
||||
if( pItem->pTab ){
|
||||
sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName);
|
||||
}
|
||||
}else if( pItem->zName ){
|
||||
sqlite3DebugPrintf("%s", pItem->zName);
|
||||
}
|
||||
if( pItem->pTab ){
|
||||
sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
|
||||
sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
|
||||
}
|
||||
if( pItem->zAlias ){
|
||||
sqlite3DebugPrintf(" AS %s", pItem->zAlias);
|
||||
sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
|
||||
}
|
||||
if( i<p->pSrc->nSrc-1 ){
|
||||
sqlite3DebugPrintf(",");
|
||||
if( pItem->jointype & JT_LEFT ){
|
||||
sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN");
|
||||
}
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
sqlite3ExplainPop(pVdbe);
|
||||
}
|
||||
if( p->pWhere ){
|
||||
sqlite3DebugPrintf("%*s WHERE ", indent, "");
|
||||
sqlite3PrintExpr(p->pWhere);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "WHERE ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pWhere);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pGroupBy ){
|
||||
sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pGroupBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "GROUPBY ");
|
||||
sqlite3ExplainExprList(pVdbe, p->pGroupBy);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pHaving ){
|
||||
sqlite3DebugPrintf("%*s HAVING ", indent, "");
|
||||
sqlite3PrintExpr(p->pHaving);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "HAVING ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pHaving);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pOrderBy ){
|
||||
sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
|
||||
sqlite3PrintExprList(p->pOrderBy);
|
||||
sqlite3DebugPrintf("\n");
|
||||
sqlite3ExplainPrintf(pVdbe, "ORDERBY ");
|
||||
sqlite3ExplainExprList(pVdbe, p->pOrderBy);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pLimit ){
|
||||
sqlite3ExplainPrintf(pVdbe, "LIMIT ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pLimit);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
if( p->pOffset ){
|
||||
sqlite3ExplainPrintf(pVdbe, "OFFSET ");
|
||||
sqlite3ExplainExpr(pVdbe, p->pOffset);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
}
|
||||
}
|
||||
void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
|
||||
if( p==0 ){
|
||||
sqlite3ExplainPrintf(pVdbe, "(null-select)");
|
||||
return;
|
||||
}
|
||||
while( p->pPrior ) p = p->pPrior;
|
||||
sqlite3ExplainPush(pVdbe);
|
||||
while( p ){
|
||||
explainOneSelect(pVdbe, p);
|
||||
p = p->pNext;
|
||||
if( p==0 ) break;
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op));
|
||||
}
|
||||
sqlite3ExplainPrintf(pVdbe, "END");
|
||||
sqlite3ExplainPop(pVdbe);
|
||||
}
|
||||
|
||||
/* End of the structure debug printing code
|
||||
*****************************************************************************/
|
||||
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
|
||||
|
|
31
src/shell.c
31
src/shell.c
|
@ -1127,6 +1127,15 @@ static int shell_exec(
|
|||
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
|
||||
}
|
||||
|
||||
/* Output TESTCTRL_EXPLAIN text of requested */
|
||||
if( pArg && pArg->mode==MODE_Explain ){
|
||||
const char *zExplain = 0;
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
|
||||
if( zExplain && zExplain[0] ){
|
||||
fprintf(pArg->out, "%s", zExplain);
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the first step. this will tell us if we
|
||||
** have a result set or not and how wide it is.
|
||||
*/
|
||||
|
@ -1396,6 +1405,7 @@ static char zHelp[] =
|
|||
" If TABLE specified, only list tables matching\n"
|
||||
" LIKE pattern TABLE.\n"
|
||||
".timeout MS Try opening locked tables for MS milliseconds\n"
|
||||
".vfsname ?AUX? Print the name of the VFS stack\n"
|
||||
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
|
||||
;
|
||||
|
||||
|
@ -2085,7 +2095,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
|||
" (SELECT sql sql, type type, tbl_name tbl_name, name name"
|
||||
" FROM sqlite_master UNION ALL"
|
||||
" SELECT sql, type, tbl_name, name FROM sqlite_temp_master) "
|
||||
"WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL "
|
||||
"WHERE lower(tbl_name) LIKE shellstatic()"
|
||||
" AND type!='meta' AND sql NOTNULL "
|
||||
"ORDER BY substr(type,2,1), name",
|
||||
callback, &data, &zErrMsg);
|
||||
zShellStatic = 0;
|
||||
|
@ -2219,7 +2230,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
|||
{ "reserve", SQLITE_TESTCTRL_RESERVE },
|
||||
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS },
|
||||
{ "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
|
||||
{ "pghdrsz", SQLITE_TESTCTRL_PGHDRSZ },
|
||||
{ "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
|
||||
};
|
||||
int testctrl = -1;
|
||||
|
@ -2264,7 +2274,6 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
|||
case SQLITE_TESTCTRL_PRNG_SAVE:
|
||||
case SQLITE_TESTCTRL_PRNG_RESTORE:
|
||||
case SQLITE_TESTCTRL_PRNG_RESET:
|
||||
case SQLITE_TESTCTRL_PGHDRSZ:
|
||||
if( nArg==2 ){
|
||||
rc = sqlite3_test_control(testctrl);
|
||||
printf("%d (0x%08x)\n", rc, rc);
|
||||
|
@ -2336,10 +2345,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
|||
}else
|
||||
|
||||
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
|
||||
printf("SQLite %s %s\n",
|
||||
printf("SQLite %s %s\n" /*extra-version-info*/,
|
||||
sqlite3_libversion(), sqlite3_sourceid());
|
||||
}else
|
||||
|
||||
if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){
|
||||
const char *zDbName = nArg==2 ? azArg[1] : "main";
|
||||
char *zVfsName = 0;
|
||||
if( p->db ){
|
||||
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
|
||||
if( zVfsName ){
|
||||
printf("%s\n", zVfsName);
|
||||
sqlite3_free(zVfsName);
|
||||
}
|
||||
}
|
||||
}else
|
||||
|
||||
if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
|
||||
int j;
|
||||
assert( nArg<=ArraySize(azArg) );
|
||||
|
@ -2933,7 +2954,7 @@ int main(int argc, char **argv){
|
|||
char *zHistory = 0;
|
||||
int nHistory;
|
||||
printf(
|
||||
"SQLite version %s %.19s\n"
|
||||
"SQLite version %s %.19s\n" /*extra-version-info*/
|
||||
"Enter \".help\" for instructions\n"
|
||||
"Enter SQL statements terminated with a \";\"\n",
|
||||
sqlite3_libversion(), sqlite3_sourceid()
|
||||
|
|
289
src/sqlite.h.in
289
src/sqlite.h.in
|
@ -172,7 +172,7 @@ const char *sqlite3_compileoption_get(int N);
|
|||
** CAPI3REF: Test To See If The Library Is Threadsafe
|
||||
**
|
||||
** ^The sqlite3_threadsafe() function returns zero if and only if
|
||||
** SQLite was compiled mutexing code omitted due to the
|
||||
** SQLite was compiled with mutexing code omitted due to the
|
||||
** [SQLITE_THREADSAFE] compile-time option being set to 0.
|
||||
**
|
||||
** SQLite can be compiled with or without mutexes. When
|
||||
|
@ -366,7 +366,7 @@ int sqlite3_exec(
|
|||
** KEYWORDS: {result code} {result codes}
|
||||
**
|
||||
** Many SQLite functions return an integer result code from the set shown
|
||||
** here in order to indicates success or failure.
|
||||
** here in order to indicate success or failure.
|
||||
**
|
||||
** New error codes may be added in future versions of SQLite.
|
||||
**
|
||||
|
@ -504,7 +504,11 @@ int sqlite3_exec(
|
|||
** first then the size of the file is extended, never the other
|
||||
** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
|
||||
** information is written to disk in the same order as calls
|
||||
** to xWrite().
|
||||
** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
|
||||
** after reboot following a crash or power loss, the only bytes in a
|
||||
** file that were written at the application level might have changed
|
||||
** and that adjacent bytes, even bytes within the same sector are
|
||||
** guaranteed to be unchanged.
|
||||
*/
|
||||
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
||||
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
||||
|
@ -518,6 +522,7 @@ int sqlite3_exec(
|
|||
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
|
||||
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
|
||||
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
|
||||
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
|
||||
|
||||
/*
|
||||
** CAPI3REF: File Locking Levels
|
||||
|
@ -739,12 +744,12 @@ struct sqlite3_io_methods {
|
|||
**
|
||||
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
|
||||
** retry counts and intervals for certain disk I/O operations for the
|
||||
** windows [VFS] in order to work to provide robustness against
|
||||
** windows [VFS] in order to provide robustness in the presence of
|
||||
** anti-virus programs. By default, the windows VFS will retry file read,
|
||||
** file write, and file delete operations up to 10 times, with a delay
|
||||
** of 25 milliseconds before the first retry and with the delay increasing
|
||||
** by an additional 25 milliseconds with each subsequent retry. This
|
||||
** opcode allows those to values (10 retries and 25 milliseconds of delay)
|
||||
** opcode allows these two values (10 retries and 25 milliseconds of delay)
|
||||
** to be adjusted. The values are changed for all database connections
|
||||
** within the same process. The argument is a pointer to an array of two
|
||||
** integers where the first integer i the new retry count and the second
|
||||
|
@ -767,22 +772,44 @@ struct sqlite3_io_methods {
|
|||
** WAL mode. If the integer is -1, then it is overwritten with the current
|
||||
** WAL persistence setting.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
|
||||
** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
|
||||
** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
|
||||
** xDeviceCharacteristics methods. The fourth parameter to
|
||||
** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
|
||||
** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
|
||||
** mode. If the integer is -1, then it is overwritten with the current
|
||||
** zero-damage mode setting.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
|
||||
** a write transaction to indicate that, unless it is rolled back for some
|
||||
** reason, the entire database file will be overwritten by the current
|
||||
** transaction. This is used by VACUUM operations.
|
||||
**
|
||||
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
|
||||
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
|
||||
** final bottom-level VFS are written into memory obtained from
|
||||
** [sqlite3_malloc()] and the result is stored in the char* variable
|
||||
** that the fourth parameter of [sqlite3_file_control()] points to.
|
||||
** The caller is responsible for freeing the memory when done. As with
|
||||
** all file-control actions, there is no guarantee that this will actually
|
||||
** do anything. Callers should initialize the char* variable to a NULL
|
||||
** pointer in case this file-control is not implemented. This file-control
|
||||
** is intended for diagnostic use only.
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
#define SQLITE_GET_LOCKPROXYFILE 2
|
||||
#define SQLITE_SET_LOCKPROXYFILE 3
|
||||
#define SQLITE_LAST_ERRNO 4
|
||||
#define SQLITE_FCNTL_SIZE_HINT 5
|
||||
#define SQLITE_FCNTL_CHUNK_SIZE 6
|
||||
#define SQLITE_FCNTL_FILE_POINTER 7
|
||||
#define SQLITE_FCNTL_SYNC_OMITTED 8
|
||||
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
|
||||
#define SQLITE_FCNTL_PERSIST_WAL 10
|
||||
#define SQLITE_FCNTL_OVERWRITE 11
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
#define SQLITE_GET_LOCKPROXYFILE 2
|
||||
#define SQLITE_SET_LOCKPROXYFILE 3
|
||||
#define SQLITE_LAST_ERRNO 4
|
||||
#define SQLITE_FCNTL_SIZE_HINT 5
|
||||
#define SQLITE_FCNTL_CHUNK_SIZE 6
|
||||
#define SQLITE_FCNTL_FILE_POINTER 7
|
||||
#define SQLITE_FCNTL_SYNC_OMITTED 8
|
||||
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
|
||||
#define SQLITE_FCNTL_PERSIST_WAL 10
|
||||
#define SQLITE_FCNTL_OVERWRITE 11
|
||||
#define SQLITE_FCNTL_VFSNAME 12
|
||||
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
|
@ -837,7 +864,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
|||
** from xFullPathname() with an optional suffix added.
|
||||
** ^If a suffix is added to the zFilename parameter, it will
|
||||
** consist of a single "-" character followed by no more than
|
||||
** 10 alphanumeric and/or "-" characters.
|
||||
** 11 alphanumeric and/or "-" characters.
|
||||
** ^SQLite further guarantees that
|
||||
** the string will be valid and unchanged until xClose() is
|
||||
** called. Because of the previous sentence,
|
||||
|
@ -1368,7 +1395,7 @@ struct sqlite3_mem_methods {
|
|||
** <dd> ^This option specifies a static memory buffer that SQLite can use for
|
||||
** the database page cache with the default page cache implementation.
|
||||
** This configuration should not be used if an application-define page
|
||||
** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
|
||||
** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option.
|
||||
** There are three arguments to this option: A pointer to 8-byte aligned
|
||||
** memory, the size of each page buffer (sz), and the number of pages (N).
|
||||
** The sz argument should be the size of the largest database page
|
||||
|
@ -1437,15 +1464,15 @@ struct sqlite3_mem_methods {
|
|||
** verb to [sqlite3_db_config()] can be used to change the lookaside
|
||||
** configuration on individual connections.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt>
|
||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to
|
||||
** an [sqlite3_pcache_methods] object. This object specifies the interface
|
||||
** an [sqlite3_pcache_methods2] object. This object specifies the interface
|
||||
** to a custom page cache implementation.)^ ^SQLite makes a copy of the
|
||||
** object and uses it for page cache memory allocations.</dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt>
|
||||
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
|
||||
** <dd> ^(This option takes a single argument which is a pointer to an
|
||||
** [sqlite3_pcache_methods] object. SQLite copies of the current
|
||||
** [sqlite3_pcache_methods2] object. SQLite copies of the current
|
||||
** page cache implementation into that object.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
||||
|
@ -1478,6 +1505,11 @@ struct sqlite3_mem_methods {
|
|||
** database connection is opened. By default, URI handling is globally
|
||||
** disabled. The default value may be changed by compiling with the
|
||||
** [SQLITE_USE_URI] symbol defined.
|
||||
**
|
||||
** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]
|
||||
** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFNIG_GETPCACHE
|
||||
** <dd> These options are obsolete and should not be used by new code.
|
||||
** They are retained for backwards compatibility but are now no-ops.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
|
@ -1493,10 +1525,12 @@ struct sqlite3_mem_methods {
|
|||
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
||||
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
||||
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
||||
#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
|
||||
#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
|
||||
#define SQLITE_CONFIG_PCACHE 14 /* no-op */
|
||||
#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
|
||||
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
||||
#define SQLITE_CONFIG_URI 17 /* int */
|
||||
#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
|
||||
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
|
@ -1981,7 +2015,7 @@ void sqlite3_free_table(char **result);
|
|||
** All of the usual printf() formatting options apply. In addition, there
|
||||
** is are "%q", "%Q", and "%z" options.
|
||||
**
|
||||
** ^(The %q option works like %s in that it substitutes a null-terminated
|
||||
** ^(The %q option works like %s in that it substitutes a nul-terminated
|
||||
** string from the argument list. But %q also doubles every '\'' character.
|
||||
** %q is designed for use inside a string literal.)^ By doubling each '\''
|
||||
** character it escapes that character and allows it to be inserted into
|
||||
|
@ -2589,21 +2623,40 @@ int sqlite3_open_v2(
|
|||
/*
|
||||
** CAPI3REF: Obtain Values For URI Parameters
|
||||
**
|
||||
** This is a utility routine, useful to VFS implementations, that checks
|
||||
** These are utility routines, useful to VFS implementations, that check
|
||||
** to see if a database file was a URI that contained a specific query
|
||||
** parameter, and if so obtains the value of the query parameter.
|
||||
** parameter, and if so obtains the value of that query parameter.
|
||||
**
|
||||
** The zFilename argument is the filename pointer passed into the xOpen()
|
||||
** method of a VFS implementation. The zParam argument is the name of the
|
||||
** query parameter we seek. This routine returns the value of the zParam
|
||||
** parameter if it exists. If the parameter does not exist, this routine
|
||||
** returns a NULL pointer.
|
||||
** If F is the database filename pointer passed into the xOpen() method of
|
||||
** a VFS implementation when the flags parameter to xOpen() has one or
|
||||
** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
|
||||
** P is the name of the query parameter, then
|
||||
** sqlite3_uri_parameter(F,P) returns the value of the P
|
||||
** parameter if it exists or a NULL pointer if P does not appear as a
|
||||
** query parameter on F. If P is a query parameter of F
|
||||
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
|
||||
** a pointer to an empty string.
|
||||
**
|
||||
** If the zFilename argument to this function is not a pointer that SQLite
|
||||
** passed into the xOpen VFS method, then the behavior of this routine
|
||||
** is undefined and probably undesirable.
|
||||
** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
|
||||
** parameter and returns true (1) or false (0) according to the value
|
||||
** of P. The value of P is true if it is "yes" or "true" or "on" or
|
||||
** a non-zero number and is false otherwise. If P is not a query parameter
|
||||
** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0).
|
||||
**
|
||||
** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
|
||||
** 64-bit signed integer and returns that integer, or D if P does not
|
||||
** exist. If the value of P is something other than an integer, then
|
||||
** zero is returned.
|
||||
**
|
||||
** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
|
||||
** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
|
||||
** is not a database file pathname pointer that SQLite passed into the xOpen
|
||||
** VFS method, then the behavior of this routine is undefined and probably
|
||||
** undesirable.
|
||||
*/
|
||||
const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
|
||||
int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
|
||||
sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -2925,6 +2978,25 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt);
|
|||
*/
|
||||
int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
|
||||
**
|
||||
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
|
||||
** [prepared statement] S has been stepped at least once using
|
||||
** [sqlite3_step(S)] but has not run to completion and/or has not
|
||||
** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
|
||||
** interface returns false if S is a NULL pointer. If S is not a
|
||||
** NULL pointer and is not a pointer to a valid [prepared statement]
|
||||
** object, then the behavior is undefined and probably undesirable.
|
||||
**
|
||||
** This interface can be used in combination [sqlite3_next_stmt()]
|
||||
** to locate all prepared statements associated with a database
|
||||
** connection that are in need of being reset. This can be used,
|
||||
** for example, in diagnostic routines to search for prepared
|
||||
** statements that are holding a transaction open.
|
||||
*/
|
||||
int sqlite3_stmt_busy(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Dynamically Typed Value Object
|
||||
** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
|
||||
|
@ -3466,7 +3538,7 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
|
|||
** bytes in the string, not the number of characters.
|
||||
**
|
||||
** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
|
||||
** even empty strings, are always zero terminated. ^The return
|
||||
** even empty strings, are always zero-terminated. ^The return
|
||||
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
|
||||
**
|
||||
** ^The object returned by [sqlite3_column_value()] is an
|
||||
|
@ -4366,6 +4438,22 @@ int sqlite3_get_autocommit(sqlite3*);
|
|||
*/
|
||||
sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Filename For A Database Connection
|
||||
**
|
||||
** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
|
||||
** associated with database N of connection D. ^The main database file
|
||||
** has the name "main". If there is no attached database N on the database
|
||||
** connection D, or if database N is a temporary or in-memory database, then
|
||||
** a NULL pointer is returned.
|
||||
**
|
||||
** ^The filename returned by this function is the output of the
|
||||
** xFullPathname method of the [VFS]. ^In other words, the filename
|
||||
** will be an absolute pathname, even if the filename used
|
||||
** to open the database originally was a URI or relative pathname.
|
||||
*/
|
||||
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Find the next prepared statement
|
||||
**
|
||||
|
@ -4401,13 +4489,15 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
|
|||
** on the same [database connection] D, or NULL for
|
||||
** the first call for each function on D.
|
||||
**
|
||||
** The commit and rollback hook callbacks are not reentrant.
|
||||
** The callback implementation must not do anything that will modify
|
||||
** the database connection that invoked the callback. Any actions
|
||||
** to modify the database connection must be deferred until after the
|
||||
** completion of the [sqlite3_step()] call that triggered the commit
|
||||
** or rollback hook in the first place.
|
||||
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
|
||||
** database connections for the meaning of "modify" in this paragraph.
|
||||
** Note that running any other SQL statements, including SELECT statements,
|
||||
** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify
|
||||
** the database connections for the meaning of "modify" in this paragraph.
|
||||
**
|
||||
** ^Registering a NULL function disables the callback.
|
||||
**
|
||||
|
@ -4520,9 +4610,24 @@ int sqlite3_enable_shared_cache(int);
|
|||
** which might be more or less than the amount requested.
|
||||
** ^The sqlite3_release_memory() routine is a no-op returning zero
|
||||
** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT].
|
||||
**
|
||||
** See also: [sqlite3_db_release_memory()]
|
||||
*/
|
||||
int sqlite3_release_memory(int);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Free Memory Used By A Database Connection
|
||||
**
|
||||
** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
|
||||
** memory as possible from database connection D. Unlike the
|
||||
** [sqlite3_release_memory()] interface, this interface is effect even
|
||||
** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
|
||||
** omitted.
|
||||
**
|
||||
** See also: [sqlite3_release_memory()]
|
||||
*/
|
||||
int sqlite3_db_release_memory(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Impose A Limit On Heap Size
|
||||
**
|
||||
|
@ -4537,7 +4642,8 @@ int sqlite3_release_memory(int);
|
|||
** is advisory only.
|
||||
**
|
||||
** ^The return value from sqlite3_soft_heap_limit64() is the size of
|
||||
** the soft heap limit prior to the call. ^If the argument N is negative
|
||||
** the soft heap limit prior to the call, or negative in the case of an
|
||||
** error. ^If the argument N is negative
|
||||
** then no change is made to the soft heap limit. Hence, the current
|
||||
** size of the soft heap limit can be determined by invoking
|
||||
** sqlite3_soft_heap_limit64() with a negative argument.
|
||||
|
@ -4553,7 +4659,7 @@ int sqlite3_release_memory(int);
|
|||
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
|
||||
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
|
||||
** <li> An alternative page cache implementation is specified using
|
||||
** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...).
|
||||
** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...).
|
||||
** <li> The page cache allocates from its own memory pool supplied
|
||||
** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than
|
||||
** from the heap.
|
||||
|
@ -5295,7 +5401,7 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|||
**
|
||||
** <ul>
|
||||
** <li> SQLITE_MUTEX_OS2
|
||||
** <li> SQLITE_MUTEX_PTHREAD
|
||||
** <li> SQLITE_MUTEX_PTHREADS
|
||||
** <li> SQLITE_MUTEX_W32
|
||||
** <li> SQLITE_MUTEX_NOOP
|
||||
** </ul>)^
|
||||
|
@ -5303,7 +5409,7 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|||
** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
|
||||
** that does no real locking and is appropriate for use in
|
||||
** a single-threaded application. ^The SQLITE_MUTEX_OS2,
|
||||
** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
|
||||
** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations
|
||||
** are appropriate for use on OS/2, Unix, and Windows.
|
||||
**
|
||||
** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
|
||||
|
@ -5493,7 +5599,7 @@ struct sqlite3_mutex_methods {
|
|||
** ^These routines should return true if the mutex in their argument
|
||||
** is held or not held, respectively, by the calling thread.
|
||||
**
|
||||
** ^The implementation is not required to provided versions of these
|
||||
** ^The implementation is not required to provide versions of these
|
||||
** routines that actually work. If the implementation does not provide working
|
||||
** versions of these routines, it should at least provide stubs that always
|
||||
** return true so that one does not get spurious assertion failures.
|
||||
|
@ -5621,9 +5727,9 @@ int sqlite3_test_control(int op, ...);
|
|||
#define SQLITE_TESTCTRL_RESERVE 14
|
||||
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
||||
#define SQLITE_TESTCTRL_ISKEYWORD 16
|
||||
#define SQLITE_TESTCTRL_PGHDRSZ 17
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 18
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
|
||||
#define SQLITE_TESTCTRL_LAST 19
|
||||
|
||||
/*
|
||||
|
@ -5926,17 +6032,33 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
|
|||
** sqlite3_pcache object except by holding and passing pointers
|
||||
** to the object.
|
||||
**
|
||||
** See [sqlite3_pcache_methods] for additional information.
|
||||
** See [sqlite3_pcache_methods2] for additional information.
|
||||
*/
|
||||
typedef struct sqlite3_pcache sqlite3_pcache;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Custom Page Cache Object
|
||||
**
|
||||
** The sqlite3_pcache_page object represents a single page in the
|
||||
** page cache. The page cache will allocate instances of this
|
||||
** object. Various methods of the page cache use pointers to instances
|
||||
** of this object as parameters or as their return value.
|
||||
**
|
||||
** See [sqlite3_pcache_methods2] for additional information.
|
||||
*/
|
||||
typedef struct sqlite3_pcache_page sqlite3_pcache_page;
|
||||
struct sqlite3_pcache_page {
|
||||
void *pBuf; /* The content of the page */
|
||||
void *pExtra; /* Extra information associated with the page */
|
||||
};
|
||||
|
||||
/*
|
||||
** CAPI3REF: Application Defined Page Cache.
|
||||
** KEYWORDS: {page cache}
|
||||
**
|
||||
** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can
|
||||
** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
|
||||
** register an alternative page cache implementation by passing in an
|
||||
** instance of the sqlite3_pcache_methods structure.)^
|
||||
** instance of the sqlite3_pcache_methods2 structure.)^
|
||||
** In many applications, most of the heap memory allocated by
|
||||
** SQLite is used for the page cache.
|
||||
** By implementing a
|
||||
|
@ -5950,7 +6072,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
|||
** extreme measure that is only needed by the most demanding applications.
|
||||
** The built-in page cache is recommended for most uses.
|
||||
**
|
||||
** ^(The contents of the sqlite3_pcache_methods structure are copied to an
|
||||
** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an
|
||||
** internal buffer by SQLite within the call to [sqlite3_config]. Hence
|
||||
** the application may discard the parameter after the call to
|
||||
** [sqlite3_config()] returns.)^
|
||||
|
@ -5959,7 +6081,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
|||
** ^(The xInit() method is called once for each effective
|
||||
** call to [sqlite3_initialize()])^
|
||||
** (usually only once during the lifetime of the process). ^(The xInit()
|
||||
** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^
|
||||
** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
|
||||
** The intent of the xInit() method is to set up global data structures
|
||||
** required by the custom page cache implementation.
|
||||
** ^(If the xInit() method is NULL, then the
|
||||
|
@ -5986,17 +6108,15 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
|||
** SQLite will typically create one cache instance for each open database file,
|
||||
** though this is not guaranteed. ^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. ^szPage
|
||||
** will the page size of the database file that is to be cached plus an
|
||||
** increment (here called "R") of less than 250. SQLite will use the
|
||||
** extra R bytes on each page to store metadata about the underlying
|
||||
** database page on disk. The value of R depends
|
||||
** be allocated by the cache. ^szPage will always a power of two. ^The
|
||||
** second parameter szExtra is a number of bytes of extra storage
|
||||
** associated with each page cache entry. ^The szExtra parameter will
|
||||
** a number less than 250. SQLite will use the
|
||||
** extra szExtra bytes on each page to store metadata about the underlying
|
||||
** database page on disk. The value passed into szExtra depends
|
||||
** on the SQLite version, the target platform, and how SQLite was compiled.
|
||||
** ^(R is constant for a particular build of SQLite. Except, there are two
|
||||
** distinct values of R when SQLite is compiled with the proprietary
|
||||
** ZIPVFS extension.)^ ^The second argument to
|
||||
** xCreate(), bPurgeable, is true if the cache being created will
|
||||
** be used to cache database pages of a file stored on disk, or
|
||||
** ^The third argument to xCreate(), bPurgeable, is true if the cache being
|
||||
** created will be used to cache database pages of 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 with the value of bPurgeable;
|
||||
** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will
|
||||
|
@ -6020,11 +6140,16 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
|||
**
|
||||
** [[the xFetch() page cache methods]]
|
||||
** The xFetch() method locates a page in the cache and returns a pointer to
|
||||
** the page, or a NULL pointer.
|
||||
** A "page", in this context, means a buffer of szPage bytes aligned at an
|
||||
** 8-byte boundary. The page to be fetched is determined by the key. ^The
|
||||
** minimum key value is 1. After it has been retrieved using xFetch, the page
|
||||
** is considered to be "pinned".
|
||||
** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
|
||||
** The pBuf element of the returned sqlite3_pcache_page object will be a
|
||||
** pointer to a buffer of szPage bytes used to store the content of a
|
||||
** single database page. The pExtra element of sqlite3_pcache_page will be
|
||||
** a pointer to the szExtra bytes of extra storage that SQLite has requested
|
||||
** for each entry in the page cache.
|
||||
**
|
||||
** The page to be fetched is determined by the key. ^The minimum 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 the page cache
|
||||
** implementation must return a pointer to the page buffer with its content
|
||||
|
@ -6077,8 +6202,37 @@ typedef struct sqlite3_pcache sqlite3_pcache;
|
|||
** ^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
|
||||
** handle invalid, and will not use it with any other sqlite3_pcache_methods2
|
||||
** functions.
|
||||
**
|
||||
** [[the xShrink() page cache method]]
|
||||
** ^SQLite invokes the xShrink() method when it wants the page cache to
|
||||
** free up as much of heap memory as possible. The page cache implementation
|
||||
** is not obligated to free any memory, but well-behaved implementations should
|
||||
** do their best.
|
||||
*/
|
||||
typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
|
||||
struct sqlite3_pcache_methods2 {
|
||||
int iVersion;
|
||||
void *pArg;
|
||||
int (*xInit)(void*);
|
||||
void (*xShutdown)(void*);
|
||||
sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
|
||||
void (*xCachesize)(sqlite3_pcache*, int nCachesize);
|
||||
int (*xPagecount)(sqlite3_pcache*);
|
||||
sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
|
||||
void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
|
||||
void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
|
||||
unsigned oldKey, unsigned newKey);
|
||||
void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
|
||||
void (*xDestroy)(sqlite3_pcache*);
|
||||
void (*xShrink)(sqlite3_pcache*);
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the obsolete pcache_methods object that has now been replaced
|
||||
** by sqlite3_pcache_methods2. This object is not used by SQLite. It is
|
||||
** retained in the header file for backwards compatibility only.
|
||||
*/
|
||||
typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
|
||||
struct sqlite3_pcache_methods {
|
||||
|
@ -6095,6 +6249,7 @@ struct sqlite3_pcache_methods {
|
|||
void (*xDestroy)(sqlite3_pcache*);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Online Backup Object
|
||||
**
|
||||
|
|
|
@ -125,6 +125,14 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Powersafe overwrite is on by default. But can be turned off using
|
||||
** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
|
||||
*/
|
||||
#ifndef SQLITE_POWERSAFE_OVERWRITE
|
||||
# define SQLITE_POWERSAFE_OVERWRITE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
|
||||
** It determines whether or not the features related to
|
||||
|
@ -346,7 +354,7 @@
|
|||
*/
|
||||
#define SQLITE_MAX_FILE_FORMAT 4
|
||||
#ifndef SQLITE_DEFAULT_FILE_FORMAT
|
||||
# define SQLITE_DEFAULT_FILE_FORMAT 1
|
||||
# define SQLITE_DEFAULT_FILE_FORMAT 4
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -1144,20 +1152,11 @@ struct Column {
|
|||
struct CollSeq {
|
||||
char *zName; /* Name of the collating sequence, UTF-8 encoded */
|
||||
u8 enc; /* Text encoding handled by xCmp() */
|
||||
u8 type; /* One of the SQLITE_COLL_... values below */
|
||||
void *pUser; /* First argument to xCmp() */
|
||||
int (*xCmp)(void*,int, const void*, int, const void*);
|
||||
void (*xDel)(void*); /* Destructor for pUser */
|
||||
};
|
||||
|
||||
/*
|
||||
** Allowed values of CollSeq.type:
|
||||
*/
|
||||
#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */
|
||||
#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */
|
||||
#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */
|
||||
#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */
|
||||
|
||||
/*
|
||||
** A sort order can be either ASC or DESC.
|
||||
*/
|
||||
|
@ -1443,7 +1442,7 @@ struct KeyInfo {
|
|||
struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
u16 flags; /* Boolean settings. UNPACKED_... below */
|
||||
u8 flags; /* Boolean settings. UNPACKED_... below */
|
||||
i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
|
||||
Mem *aMem; /* Values */
|
||||
};
|
||||
|
@ -1451,12 +1450,9 @@ struct UnpackedRecord {
|
|||
/*
|
||||
** Allowed values of UnpackedRecord.flags
|
||||
*/
|
||||
#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */
|
||||
#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */
|
||||
#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
|
||||
#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
|
||||
#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
|
||||
#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
|
||||
#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */
|
||||
#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */
|
||||
#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */
|
||||
|
||||
/*
|
||||
** Each SQL index is represented in memory by an
|
||||
|
@ -1719,10 +1715,10 @@ struct Expr {
|
|||
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
|
||||
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
|
||||
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
|
||||
#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
|
||||
#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
|
||||
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
|
||||
|
||||
/*
|
||||
** The following are the meanings of bits in the Expr.flags2 field.
|
||||
|
@ -1784,7 +1780,7 @@ struct ExprList {
|
|||
char *zSpan; /* Original text of the expression */
|
||||
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
||||
u8 done; /* A flag to indicate when processing is finished */
|
||||
u16 iCol; /* For ORDER BY, column number in result set */
|
||||
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
||||
u16 iAlias; /* Index into Parse.aAlias[] for zName */
|
||||
} *a; /* One entry for each expression */
|
||||
};
|
||||
|
@ -2084,13 +2080,13 @@ struct Select {
|
|||
** Allowed values for Select.selFlags. The "SF" prefix stands for
|
||||
** "Select Flag".
|
||||
*/
|
||||
#define SF_Distinct 0x0001 /* Output should be DISTINCT */
|
||||
#define SF_Resolved 0x0002 /* Identifiers have been resolved */
|
||||
#define SF_Aggregate 0x0004 /* Contains aggregate functions */
|
||||
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x0040 /* Sort using a sorter */
|
||||
#define SF_Distinct 0x01 /* Output should be DISTINCT */
|
||||
#define SF_Resolved 0x02 /* Identifiers have been resolved */
|
||||
#define SF_Aggregate 0x04 /* Contains aggregate functions */
|
||||
#define SF_UsesEphemeral 0x08 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x40 /* Sort using a sorter */
|
||||
|
||||
|
||||
/*
|
||||
|
@ -2205,10 +2201,8 @@ struct Parse {
|
|||
char *zErrMsg; /* An error message */
|
||||
Vdbe *pVdbe; /* An engine for executing database bytecode */
|
||||
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
|
||||
u8 nameClash; /* A permanent table name clashes with temp table name */
|
||||
u8 checkSchema; /* Causes schema cookie check after an error */
|
||||
u8 nested; /* Number of nested calls to the parser/code generator */
|
||||
u8 parseError; /* True after a parsing error. Ticket #1794 */
|
||||
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
|
||||
u8 nTempInUse; /* Number of aTempReg[] currently checked out */
|
||||
int aTempReg[8]; /* Holding area for temporary registers */
|
||||
|
@ -2218,11 +2212,12 @@ struct Parse {
|
|||
int nTab; /* Number of previously allocated VDBE cursors */
|
||||
int nMem; /* Number of memory cells used so far */
|
||||
int nSet; /* Number of sets used so far */
|
||||
int nOnce; /* Number of OP_Once instructions so far */
|
||||
int ckBase; /* Base register of data during check constraints */
|
||||
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
|
||||
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
|
||||
u8 nColCache; /* Number of entries in the column cache */
|
||||
u8 iColCache; /* Next entry of the cache to replace */
|
||||
u8 nColCache; /* Number of entries in aColCache[] */
|
||||
u8 iColCache; /* Next entry in aColCache[] to replace */
|
||||
struct yColCache {
|
||||
int iTable; /* Table cursor number */
|
||||
int iColumn; /* Table column number */
|
||||
|
@ -2264,7 +2259,6 @@ struct Parse {
|
|||
char **azVar; /* Pointers to names of parameters */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
int nAlias; /* Number of aliased result set columns */
|
||||
int nAliasAlloc; /* Number of allocated slots for aAlias[] */
|
||||
int *aAlias; /* Register used to hold aliased result */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
Token sNameToken; /* Token with unqualified schema object name */
|
||||
|
@ -2459,7 +2453,7 @@ struct Sqlite3Config {
|
|||
int nLookaside; /* Default lookaside buffer count */
|
||||
sqlite3_mem_methods m; /* Low-level memory allocation interface */
|
||||
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
|
||||
sqlite3_pcache_methods pcache; /* Low-level page-cache interface */
|
||||
sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
|
||||
void *pHeap; /* Heap storage space */
|
||||
int nHeap; /* Size of pHeap[] */
|
||||
int mnReq, mxReq; /* Min and max heap requests sizes */
|
||||
|
@ -2665,6 +2659,29 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
|
|||
#if defined(SQLITE_TEST)
|
||||
void *sqlite3TestTextToPtr(const char*);
|
||||
#endif
|
||||
|
||||
/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
void sqlite3ExplainBegin(Vdbe*);
|
||||
void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
|
||||
void sqlite3ExplainNL(Vdbe*);
|
||||
void sqlite3ExplainPush(Vdbe*);
|
||||
void sqlite3ExplainPop(Vdbe*);
|
||||
void sqlite3ExplainFinish(Vdbe*);
|
||||
void sqlite3ExplainSelect(Vdbe*, Select*);
|
||||
void sqlite3ExplainExpr(Vdbe*, Expr*);
|
||||
void sqlite3ExplainExprList(Vdbe*, ExprList*);
|
||||
const char *sqlite3VdbeExplanation(Vdbe*);
|
||||
#else
|
||||
# define sqlite3ExplainBegin(X)
|
||||
# define sqlite3ExplainSelect(A,B)
|
||||
# define sqlite3ExplainExpr(A,B)
|
||||
# define sqlite3ExplainExprList(A,B)
|
||||
# define sqlite3ExplainFinish(X)
|
||||
# define sqlite3VdbeExplanation(X) 0
|
||||
#endif
|
||||
|
||||
|
||||
void sqlite3SetString(char **, sqlite3*, const char*, ...);
|
||||
void sqlite3ErrorMsg(Parse*, const char*, ...);
|
||||
int sqlite3Dequote(char*);
|
||||
|
@ -2675,6 +2692,7 @@ int sqlite3GetTempReg(Parse*);
|
|||
void sqlite3ReleaseTempReg(Parse*,int);
|
||||
int sqlite3GetTempRange(Parse*,int);
|
||||
void sqlite3ReleaseTempRange(Parse*,int,int);
|
||||
void sqlite3ClearTempRegCache(Parse*);
|
||||
Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
|
||||
Expr *sqlite3Expr(sqlite3*,int,const char*);
|
||||
void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
|
||||
|
@ -2706,6 +2724,7 @@ void sqlite3AddCollateType(Parse*, Token*);
|
|||
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
sqlite3_vfs**,char**,char **);
|
||||
int sqlite3CodeOnce(Parse *);
|
||||
|
||||
Bitvec *sqlite3BitvecCreate(u32);
|
||||
int sqlite3BitvecTest(Bitvec*, u32);
|
||||
|
@ -3044,6 +3063,7 @@ int sqlite3OpenTempDatabase(Parse *);
|
|||
|
||||
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
|
||||
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
|
||||
void sqlite3AppendSpace(StrAccum*,int);
|
||||
char *sqlite3StrAccumFinish(StrAccum*);
|
||||
void sqlite3StrAccumReset(StrAccum*);
|
||||
void sqlite3SelectDestInit(SelectDest*,int,int);
|
||||
|
|
|
@ -3001,6 +3001,14 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
}else{
|
||||
flags &= ~SQLITE_OPEN_FULLMUTEX;
|
||||
}
|
||||
}else if( strcmp(zArg, "-uri")==0 ){
|
||||
int b;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
|
||||
if( b ){
|
||||
flags |= SQLITE_OPEN_URI;
|
||||
}else{
|
||||
flags &= ~SQLITE_OPEN_URI;
|
||||
}
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
|
||||
return TCL_ERROR;
|
||||
|
@ -3009,7 +3017,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
|
|||
if( objc<3 || (objc&1)!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv,
|
||||
"HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN?"
|
||||
" ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
" ?-key CODECKEY?"
|
||||
#endif
|
||||
|
|
145
src/test1.c
145
src/test1.c
|
@ -2330,6 +2330,33 @@ static int test_stmt_readonly(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_stmt_busy STMT
|
||||
**
|
||||
** Return true if STMT is a non-NULL pointer to a statement
|
||||
** that has been stepped but not to completion.
|
||||
*/
|
||||
static int test_stmt_busy(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
|
||||
rc = sqlite3_stmt_busy(pStmt);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: uses_stmt_journal STMT
|
||||
**
|
||||
|
@ -4593,6 +4620,54 @@ static int test_release_memory(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_db_release_memory DB
|
||||
**
|
||||
** Attempt to release memory currently held by database DB. Return the
|
||||
** result code (which in the current implementation is always zero).
|
||||
*/
|
||||
static int test_db_release_memory(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
rc = sqlite3_db_release_memory(db);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_db_filename DB DBNAME
|
||||
**
|
||||
** Return the name of a file associated with a database.
|
||||
*/
|
||||
static int test_db_filename(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zDbName;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zDbName = Tcl_GetString(objv[2]);
|
||||
Tcl_AppendResult(interp, sqlite3_db_filename(db, zDbName), (void*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_soft_heap_limit ?N?
|
||||
**
|
||||
|
@ -5160,6 +5235,71 @@ static int file_control_persist_wal(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_powersafe_overwrite DB PSOW-FLAG
|
||||
**
|
||||
** This TCL command runs the sqlite3_file_control interface with
|
||||
** the SQLITE_FCNTL_POWERSAFE_OVERWRITE opcode.
|
||||
*/
|
||||
static int file_control_powersafe_overwrite(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
int b;
|
||||
char z[100];
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB FLAG", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &b) ) return TCL_ERROR;
|
||||
rc = sqlite3_file_control(db,NULL,SQLITE_FCNTL_POWERSAFE_OVERWRITE,(void*)&b);
|
||||
sqlite3_snprintf(sizeof(z), z, "%d %d", rc, b);
|
||||
Tcl_AppendResult(interp, z, (char*)0);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: file_control_vfsname DB ?AUXDB?
|
||||
**
|
||||
** Return a string that describes the stack of VFSes.
|
||||
*/
|
||||
static int file_control_vfsname(
|
||||
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
|
||||
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||
int objc, /* Number of arguments */
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zDbName = "main";
|
||||
char *zVfsName = 0;
|
||||
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
Tcl_GetStringFromObj(objv[0], 0), " DB ?AUXDB?", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( objc==3 ){
|
||||
zDbName = Tcl_GetString(objv[2]);
|
||||
}
|
||||
sqlite3_file_control(db, zDbName, SQLITE_FCNTL_VFSNAME,(void*)&zVfsName);
|
||||
Tcl_AppendResult(interp, zVfsName, (char*)0);
|
||||
sqlite3_free(zVfsName);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_vfs_list
|
||||
|
@ -5912,9 +6052,12 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
|||
{ "sqlite3_sql", test_sql ,0 },
|
||||
{ "sqlite3_next_stmt", test_next_stmt ,0 },
|
||||
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
|
||||
{ "sqlite3_stmt_busy", test_stmt_busy ,0 },
|
||||
{ "uses_stmt_journal", uses_stmt_journal ,0 },
|
||||
|
||||
{ "sqlite3_release_memory", test_release_memory, 0},
|
||||
{ "sqlite3_db_release_memory", test_db_release_memory, 0},
|
||||
{ "sqlite3_db_filename", test_db_filename, 0},
|
||||
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
|
||||
{ "sqlite3_thread_cleanup", test_thread_cleanup, 0},
|
||||
{ "sqlite3_pager_refcounts", test_pager_refcounts, 0},
|
||||
|
@ -5982,6 +6125,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
|||
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
|
||||
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
|
||||
{ "file_control_persist_wal", file_control_persist_wal, 0 },
|
||||
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
|
||||
{ "file_control_vfsname", file_control_vfsname, 0 },
|
||||
{ "sqlite3_vfs_list", vfs_list, 0 },
|
||||
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
|
||||
|
||||
|
|
11
src/test2.c
11
src/test2.c
|
@ -537,6 +537,8 @@ static int fake_big_file(
|
|||
int rc;
|
||||
int n;
|
||||
i64 offset;
|
||||
char *zFile;
|
||||
int nFile;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" N-MEGABYTES FILE\"", 0);
|
||||
|
@ -545,17 +547,24 @@ static int fake_big_file(
|
|||
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
|
||||
|
||||
pVfs = sqlite3_vfs_find(0);
|
||||
rc = sqlite3OsOpenMalloc(pVfs, argv[2], &fd,
|
||||
nFile = strlen(argv[2]);
|
||||
zFile = sqlite3_malloc( nFile+2 );
|
||||
if( zFile==0 ) return TCL_ERROR;
|
||||
memcpy(zFile, argv[2], nFile+1);
|
||||
zFile[nFile+1] = 0;
|
||||
rc = sqlite3OsOpenMalloc(pVfs, zFile, &fd,
|
||||
(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
|
||||
);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0);
|
||||
sqlite3_free(zFile);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
offset = n;
|
||||
offset *= 1024*1024;
|
||||
rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset);
|
||||
sqlite3OsCloseFree(fd);
|
||||
sqlite3_free(zFile);
|
||||
if( rc ){
|
||||
Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
|
|
10
src/test3.c
10
src/test3.c
|
@ -66,6 +66,8 @@ static int btree_open(
|
|||
Btree *pBt;
|
||||
int rc, nCache;
|
||||
char zBuf[100];
|
||||
int n;
|
||||
char *zFilename;
|
||||
if( argc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||
" FILENAME NCACHE FLAGS\"", 0);
|
||||
|
@ -78,8 +80,14 @@ static int btree_open(
|
|||
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
sqlite3_mutex_enter(sDb.mutex);
|
||||
}
|
||||
rc = sqlite3BtreeOpen(sDb.pVfs, argv[1], &sDb, &pBt, 0,
|
||||
n = strlen(argv[1]);
|
||||
zFilename = sqlite3_malloc( n+2 );
|
||||
if( zFilename==0 ) return TCL_ERROR;
|
||||
memcpy(zFilename, argv[1], n+1);
|
||||
zFilename[n+1] = 0;
|
||||
rc = sqlite3BtreeOpen(sDb.pVfs, zFilename, &sDb, &pBt, 0,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB);
|
||||
sqlite3_free(zFilename);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
|
|
23
src/test6.c
23
src/test6.c
|
@ -705,17 +705,18 @@ static int processDevSymArgs(
|
|||
char *zName;
|
||||
int iValue;
|
||||
} aFlag[] = {
|
||||
{ "atomic", SQLITE_IOCAP_ATOMIC },
|
||||
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
|
||||
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
|
||||
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
|
||||
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
|
||||
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
|
||||
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
|
||||
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
|
||||
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "atomic", SQLITE_IOCAP_ATOMIC },
|
||||
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
|
||||
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
|
||||
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
|
||||
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
|
||||
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
|
||||
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
|
||||
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
|
||||
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,14 @@
|
|||
** procedures use this to determine when tests should be omitted.
|
||||
*/
|
||||
static void set_options(Tcl_Interp *interp){
|
||||
#ifdef HAVE_MALLOC_USABLE_SIZE
|
||||
Tcl_SetVar2(interp, "sqlite_options", "malloc_usable_size", "1",
|
||||
TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "malloc_usable_size", "0",
|
||||
TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_32BIT_ROWID
|
||||
Tcl_SetVar2(interp, "sqlite_options", "rowid32", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
|
|
@ -149,13 +149,13 @@ static void test_destructor_count(
|
|||
** arguments. It returns the text value returned by the sqlite3_errmsg16()
|
||||
** API function.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
#ifndef SQLITE_OMIT_BUILTIN_TEST
|
||||
void sqlite3BeginBenignMalloc(void);
|
||||
void sqlite3EndBenignMalloc(void);
|
||||
#else
|
||||
#define sqlite3BeginBenignMalloc()
|
||||
#define sqlite3EndBenignMalloc()
|
||||
#endif
|
||||
#else
|
||||
#define sqlite3BeginBenignMalloc()
|
||||
#define sqlite3EndBenignMalloc()
|
||||
#endif
|
||||
static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){
|
||||
}
|
||||
static void test_agg_errmsg16_final(sqlite3_context *ctx){
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
#include <tcl.h>
|
||||
|
||||
static struct Wrapped {
|
||||
sqlite3_pcache_methods pcache;
|
||||
sqlite3_mem_methods mem;
|
||||
sqlite3_mutex_methods mutex;
|
||||
sqlite3_pcache_methods2 pcache;
|
||||
sqlite3_mem_methods mem;
|
||||
sqlite3_mutex_methods mutex;
|
||||
|
||||
int mem_init; /* True if mem subsystem is initalized */
|
||||
int mem_fail; /* True to fail mem subsystem inialization */
|
||||
|
@ -123,8 +123,8 @@ static void wrPCacheShutdown(void *pArg){
|
|||
wrapped.pcache_init = 0;
|
||||
}
|
||||
|
||||
static sqlite3_pcache *wrPCacheCreate(int a, int b){
|
||||
return wrapped.pcache.xCreate(a, b);
|
||||
static sqlite3_pcache *wrPCacheCreate(int a, int b, int c){
|
||||
return wrapped.pcache.xCreate(a, b, c);
|
||||
}
|
||||
static void wrPCacheCachesize(sqlite3_pcache *p, int n){
|
||||
wrapped.pcache.xCachesize(p, n);
|
||||
|
@ -132,13 +132,18 @@ static void wrPCacheCachesize(sqlite3_pcache *p, int n){
|
|||
static int wrPCachePagecount(sqlite3_pcache *p){
|
||||
return wrapped.pcache.xPagecount(p);
|
||||
}
|
||||
static void *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){
|
||||
static sqlite3_pcache_page *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){
|
||||
return wrapped.pcache.xFetch(p, a, b);
|
||||
}
|
||||
static void wrPCacheUnpin(sqlite3_pcache *p, void *a, int b){
|
||||
static void wrPCacheUnpin(sqlite3_pcache *p, sqlite3_pcache_page *a, int b){
|
||||
wrapped.pcache.xUnpin(p, a, b);
|
||||
}
|
||||
static void wrPCacheRekey(sqlite3_pcache *p, void *a, unsigned b, unsigned c){
|
||||
static void wrPCacheRekey(
|
||||
sqlite3_pcache *p,
|
||||
sqlite3_pcache_page *a,
|
||||
unsigned b,
|
||||
unsigned c
|
||||
){
|
||||
wrapped.pcache.xRekey(p, a, b, c);
|
||||
}
|
||||
static void wrPCacheTruncate(sqlite3_pcache *p, unsigned a){
|
||||
|
@ -154,8 +159,8 @@ static void installInitWrappers(void){
|
|||
wrMutexFree, wrMutexEnter, wrMutexTry,
|
||||
wrMutexLeave, wrMutexHeld, wrMutexNotheld
|
||||
};
|
||||
sqlite3_pcache_methods pcachemethods = {
|
||||
0,
|
||||
sqlite3_pcache_methods2 pcachemethods = {
|
||||
1, 0,
|
||||
wrPCacheInit, wrPCacheShutdown, wrPCacheCreate,
|
||||
wrPCacheCachesize, wrPCachePagecount, wrPCacheFetch,
|
||||
wrPCacheUnpin, wrPCacheRekey, wrPCacheTruncate,
|
||||
|
@ -173,10 +178,10 @@ static void installInitWrappers(void){
|
|||
sqlite3_shutdown();
|
||||
sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped.mutex);
|
||||
sqlite3_config(SQLITE_CONFIG_GETMALLOC, &wrapped.mem);
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE, &wrapped.pcache);
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &wrapped.pcache);
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &mutexmethods);
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &memmethods);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &pcachemethods);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcachemethods);
|
||||
}
|
||||
|
||||
static int init_wrapper_install(
|
||||
|
@ -218,7 +223,7 @@ static int init_wrapper_uninstall(
|
|||
sqlite3_shutdown();
|
||||
sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped.mutex);
|
||||
sqlite3_config(SQLITE_CONFIG_MALLOC, &wrapped.mem);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &wrapped.pcache);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &wrapped.pcache);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -391,7 +391,7 @@ static int openTransaction(jt_file *pMain, jt_file *pJournal){
|
|||
while( rc==SQLITE_OK && iTrunk>0 ){
|
||||
u32 nLeaf;
|
||||
u32 iLeaf;
|
||||
sqlite3_int64 iOff = (iTrunk-1)*pMain->nPagesize;
|
||||
sqlite3_int64 iOff = (i64)(iTrunk-1)*pMain->nPagesize;
|
||||
rc = sqlite3OsRead(p, aData, pMain->nPagesize, iOff);
|
||||
nLeaf = decodeUint32(&aData[4]);
|
||||
for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
|
||||
|
@ -409,6 +409,7 @@ static int openTransaction(jt_file *pMain, jt_file *pJournal){
|
|||
if( iOff==PENDING_BYTE ) continue;
|
||||
rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
|
||||
pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize);
|
||||
if( ii+1==pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ) rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,7 +663,7 @@ static int jtCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|||
*/
|
||||
static int jtFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
jt_file *p = (jt_file *)pFile;
|
||||
return sqlite3OsFileControl(p->pReal, op, pArg);
|
||||
return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -81,6 +81,9 @@
|
|||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
#endif /* SQLITE_THREADSAFE==0 */
|
||||
|
||||
/* First chunk for rollback journal files */
|
||||
#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
|
||||
|
||||
|
||||
/************************ Shim Definitions ******************************/
|
||||
|
||||
|
@ -96,26 +99,16 @@
|
|||
# define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
|
||||
#endif
|
||||
|
||||
/* Default limit on number of chunks. Care should be taken
|
||||
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
|
||||
** format specifier. It may be changed by calling
|
||||
** the xFileControl() interface.
|
||||
/* This used to be the default limit on number of chunks, but
|
||||
** it is no longer enforced. There is currently no limit to the
|
||||
** number of chunks.
|
||||
**
|
||||
** May be changed by calling the xFileControl() interface.
|
||||
*/
|
||||
#ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
|
||||
# define SQLITE_MULTIPLEX_MAX_CHUNKS 32
|
||||
# define SQLITE_MULTIPLEX_MAX_CHUNKS 12
|
||||
#endif
|
||||
|
||||
/* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the
|
||||
** last SQLITE_MULTIPLEX_EXT_SZ characters of the
|
||||
** filename will be overwritten, otherwise, the
|
||||
** multiplex extension is simply appended to the filename.
|
||||
** Ex. (undefined) test.db -> test.db01
|
||||
** (defined) test.db -> test.01
|
||||
** Chunk 0 does not have a modified extension.
|
||||
*/
|
||||
#define SQLITE_MULTIPLEX_EXT_FMT "%02d"
|
||||
#define SQLITE_MULTIPLEX_EXT_SZ 2
|
||||
|
||||
/************************ Object Definitions ******************************/
|
||||
|
||||
/* Forward declaration of all object types */
|
||||
|
@ -140,7 +133,8 @@ struct multiplexGroup {
|
|||
int nName; /* Length of base filename */
|
||||
int flags; /* Flags used for original opening */
|
||||
unsigned int szChunk; /* Chunk size used for this group */
|
||||
int bEnabled; /* TRUE to use Multiplex VFS for this file */
|
||||
unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */
|
||||
unsigned char bTruncate; /* TRUE to enable truncation of databases */
|
||||
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
|
||||
};
|
||||
|
||||
|
@ -224,68 +218,58 @@ static int multiplexStrlen30(const char *z){
|
|||
}
|
||||
|
||||
/*
|
||||
** Create a temporary file name in zBuf. zBuf must be big enough to
|
||||
** hold at pOrigVfs->mxPathname characters. This function departs
|
||||
** from the traditional temporary name generation in the os_win
|
||||
** and os_unix VFS in several ways, but is necessary so that
|
||||
** the file name is known for temporary files (like those used
|
||||
** during vacuum.)
|
||||
** Generate the file-name for chunk iChunk of the group with base name
|
||||
** zBase. The file-name is written to buffer zOut before returning. Buffer
|
||||
** zOut must be allocated by the caller so that it is at least (nBase+5)
|
||||
** bytes in size, where nBase is the length of zBase, not including the
|
||||
** nul-terminator.
|
||||
**
|
||||
** N.B. This routine assumes your underlying VFS is ok with using
|
||||
** "/" as a directory seperator. This is the default for UNIXs
|
||||
** and is allowed (even mixed) for most versions of Windows.
|
||||
** If iChunk is 0 (or 400 - the number for the first journal file chunk),
|
||||
** the output is a copy of the input string. Otherwise, if
|
||||
** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
|
||||
** a "." character, then the output is a copy of the input string with the
|
||||
** three-digit zero-padded decimal representation if iChunk appended to it.
|
||||
** For example:
|
||||
**
|
||||
** zBase="test.db", iChunk=4 -> zOut="test.db004"
|
||||
**
|
||||
** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
|
||||
** a "." character, then everything after the "." is replaced by the
|
||||
** three-digit representation of iChunk.
|
||||
**
|
||||
** zBase="test.db", iChunk=4 -> zOut="test.004"
|
||||
**
|
||||
** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
|
||||
** to pass to sqlite3_uri_parameter() and similar.
|
||||
*/
|
||||
static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
|
||||
static char zChars[] =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
int i,j;
|
||||
int attempts = 0;
|
||||
int exists = 0;
|
||||
int rc = SQLITE_ERROR;
|
||||
|
||||
/* Check that the output buffer is large enough for
|
||||
** pVfs->mxPathname characters.
|
||||
*/
|
||||
if( pOrigVfs->mxPathname <= nBuf ){
|
||||
char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
|
||||
if( zTmp==0 ) return SQLITE_NOMEM;
|
||||
|
||||
/* sqlite3_temp_directory should always be less than
|
||||
** pVfs->mxPathname characters.
|
||||
*/
|
||||
sqlite3_snprintf(pOrigVfs->mxPathname,
|
||||
zTmp,
|
||||
"%s/",
|
||||
sqlite3_temp_directory ? sqlite3_temp_directory : ".");
|
||||
rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
|
||||
sqlite3_free(zTmp);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* Check that the output buffer is large enough for the temporary file
|
||||
** name.
|
||||
*/
|
||||
j = multiplexStrlen30(zBuf);
|
||||
if( (j + 8 + 1 + 3 + 1) <= nBuf ){
|
||||
/* Make 3 attempts to generate a unique name. */
|
||||
do {
|
||||
attempts++;
|
||||
sqlite3_randomness(8, &zBuf[j]);
|
||||
for(i=0; i<8; i++){
|
||||
unsigned char uc = (unsigned char)zBuf[j+i];
|
||||
zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
|
||||
}
|
||||
memcpy(&zBuf[j+i], ".tmp", 5);
|
||||
rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
|
||||
} while ( (rc==SQLITE_OK) && exists && (attempts<3) );
|
||||
if( rc==SQLITE_OK && exists ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
static void multiplexFilename(
|
||||
const char *zBase, /* Filename for chunk 0 */
|
||||
int nBase, /* Size of zBase in bytes (without \0) */
|
||||
int flags, /* Flags used to open file */
|
||||
int iChunk, /* Chunk to generate filename for */
|
||||
char *zOut /* Buffer to write generated name to */
|
||||
){
|
||||
int n = nBase;
|
||||
memcpy(zOut, zBase, n+1);
|
||||
if( iChunk!=0 && iChunk!=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
int i;
|
||||
for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
|
||||
if( i>=n-4 ) n = i+1;
|
||||
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
|
||||
/* The extensions on overflow files for main databases are 001, 002,
|
||||
** 003 and so forth. To avoid name collisions, add 400 to the
|
||||
** extensions of journal files so that they are 401, 402, 403, ....
|
||||
*/
|
||||
iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
|
||||
}
|
||||
#endif
|
||||
sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
|
||||
n += 3;
|
||||
}
|
||||
|
||||
return rc;
|
||||
assert( zOut[n]=='\0' );
|
||||
zOut[n+1] = '\0';
|
||||
}
|
||||
|
||||
/* Compute the filename for the iChunk-th chunk
|
||||
|
@ -301,50 +285,71 @@ static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
|
|||
pGroup->aReal = p;
|
||||
pGroup->nReal = iChunk+1;
|
||||
}
|
||||
if( pGroup->aReal[iChunk].z==0 ){
|
||||
if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
|
||||
char *z;
|
||||
int n = pGroup->nName;
|
||||
pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+3 );
|
||||
pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+5 );
|
||||
if( z==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memcpy(z, pGroup->zName, n+1);
|
||||
if( iChunk>0 ){
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
if( n>3 && z[n-3]=='.' ){
|
||||
n--;
|
||||
}else if( n>4 && z[n-4]=='.' ){
|
||||
n -= 2;
|
||||
}
|
||||
#endif
|
||||
sqlite3_snprintf(3,&z[n],"%02d",iChunk);
|
||||
}
|
||||
multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Translate an sqlite3_file* that is really a multiplexGroup* into
|
||||
** the sqlite3_file* for the underlying original VFS.
|
||||
**
|
||||
** For chunk 0, the pGroup->flags determines whether or not a new file
|
||||
** is created if it does not already exist. For chunks 1 and higher, the
|
||||
** file is created only if createFlag is 1.
|
||||
*/
|
||||
static sqlite3_file *multiplexSubOpen(
|
||||
multiplexGroup *pGroup,
|
||||
int iChunk,
|
||||
int *rc,
|
||||
int *pOutFlags
|
||||
multiplexGroup *pGroup, /* The multiplexor group */
|
||||
int iChunk, /* Which chunk to open. 0==original file */
|
||||
int *rc, /* Result code in and out */
|
||||
int *pOutFlags, /* Output flags */
|
||||
int createFlag /* True to create if iChunk>0 */
|
||||
){
|
||||
sqlite3_file *pSubOpen = 0;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
|
||||
#ifdef SQLITE_ENABLE_8_3_NAMES
|
||||
/* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
|
||||
** part of a database journal are named db.401, db.402, and so on. A
|
||||
** database may therefore not grow to larger than 400 chunks. Attempting
|
||||
** to open chunk 401 indicates the database is full. */
|
||||
if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
|
||||
*rc = SQLITE_FULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
*rc = multiplexSubFilename(pGroup, iChunk);
|
||||
if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
|
||||
int flags, bExists;
|
||||
flags = pGroup->flags;
|
||||
if( createFlag ){
|
||||
flags |= SQLITE_OPEN_CREATE;
|
||||
}else if( iChunk==0 ){
|
||||
/* Fall through */
|
||||
}else if( pGroup->aReal[iChunk].z==0 ){
|
||||
return 0;
|
||||
}else{
|
||||
*rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
|
||||
SQLITE_ACCESS_EXISTS, &bExists);
|
||||
if( *rc || !bExists ) return 0;
|
||||
flags &= ~SQLITE_OPEN_CREATE;
|
||||
}
|
||||
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
|
||||
if( pSubOpen==0 ){
|
||||
*rc = SQLITE_NOMEM;
|
||||
*rc = SQLITE_IOERR_NOMEM;
|
||||
return 0;
|
||||
}
|
||||
pGroup->aReal[iChunk].p = pSubOpen;
|
||||
*rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
|
||||
pGroup->flags, pOutFlags);
|
||||
if( *rc!=SQLITE_OK ){
|
||||
flags, pOutFlags);
|
||||
if( (*rc)!=SQLITE_OK ){
|
||||
sqlite3_free(pSubOpen);
|
||||
pGroup->aReal[iChunk].p = 0;
|
||||
return 0;
|
||||
|
@ -353,6 +358,26 @@ static sqlite3_file *multiplexSubOpen(
|
|||
return pSubOpen;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the size, in bytes, of chunk number iChunk. If that chunk
|
||||
** does not exist, then return 0. This function does not distingish between
|
||||
** non-existant files and zero-length files.
|
||||
*/
|
||||
static sqlite3_int64 multiplexSubSize(
|
||||
multiplexGroup *pGroup, /* The multiplexor group */
|
||||
int iChunk, /* Which chunk to open. 0==original file */
|
||||
int *rc /* Result code in and out */
|
||||
){
|
||||
sqlite3_file *pSub;
|
||||
sqlite3_int64 sz = 0;
|
||||
|
||||
if( *rc ) return 0;
|
||||
pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
|
||||
if( pSub==0 ) return 0;
|
||||
*rc = pSub->pMethods->xFileSize(pSub, &sz);
|
||||
return sz;
|
||||
}
|
||||
|
||||
/*
|
||||
** This is the implementation of the multiplex_control() SQL function.
|
||||
*/
|
||||
|
@ -420,7 +445,9 @@ static void multiplexSubClose(
|
|||
sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
|
||||
if( pSubOpen ){
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
if( pOrigVfs ) pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
|
||||
if( pOrigVfs && pGroup->aReal[iChunk].z ){
|
||||
pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
|
||||
}
|
||||
sqlite3_free(pGroup->aReal[iChunk].p);
|
||||
}
|
||||
sqlite3_free(pGroup->aReal[iChunk].z);
|
||||
|
@ -466,6 +493,7 @@ static int multiplexOpen(
|
|||
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
memset(pConn, 0, pVfs->szOsFile);
|
||||
assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
|
||||
|
||||
/* We need to create a group structure and manage
|
||||
** access to this group of files.
|
||||
|
@ -473,23 +501,9 @@ static int multiplexOpen(
|
|||
multiplexEnter();
|
||||
pMultiplexOpen = (multiplexConn*)pConn;
|
||||
|
||||
/* If the second argument to this function is NULL, generate a
|
||||
** temporary file name to use. This will be handled by the
|
||||
** original xOpen method. We just need to allocate space for
|
||||
** it.
|
||||
*/
|
||||
if( !zName ){
|
||||
zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
|
||||
if( zName==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* allocate space for group */
|
||||
nName = multiplexStrlen30(zName);
|
||||
nName = zName ? multiplexStrlen30(zName) : 0;
|
||||
sz = sizeof(multiplexGroup) /* multiplexGroup */
|
||||
+ nName + 1; /* zName */
|
||||
pGroup = sqlite3_malloc( sz );
|
||||
|
@ -499,63 +513,90 @@ static int multiplexOpen(
|
|||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0;
|
||||
/* assign pointers to extra space allocated */
|
||||
char *p = (char *)&pGroup[1];
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
memset(pGroup, 0, sz);
|
||||
pMultiplexOpen->pGroup = pGroup;
|
||||
pGroup->bEnabled = -1;
|
||||
pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
|
||||
if( flags & SQLITE_OPEN_URI ){
|
||||
const char *zChunkSize;
|
||||
zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
|
||||
if( zChunkSize ){
|
||||
unsigned int n = 0;
|
||||
int i;
|
||||
for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
|
||||
n = n*10 + zChunkSize[i] - '0';
|
||||
}
|
||||
if( n>0 ){
|
||||
pGroup->szChunk = (n+0xffff)&~0xffff;
|
||||
}else{
|
||||
/* A zero or negative chunksize disabled the multiplexor */
|
||||
pGroup->bEnabled = 0;
|
||||
}
|
||||
pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
|
||||
(flags & SQLITE_OPEN_MAIN_DB)==0);
|
||||
pGroup->szChunk = sqlite3_uri_int64(zUri, "chunksize",
|
||||
SQLITE_MULTIPLEX_CHUNK_SIZE);
|
||||
pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
|
||||
if( zName ){
|
||||
char *p = (char *)&pGroup[1];
|
||||
pGroup->zName = p;
|
||||
memcpy(pGroup->zName, zName, nName+1);
|
||||
pGroup->nName = nName;
|
||||
}
|
||||
if( pGroup->bEnabled ){
|
||||
/* Make sure that the chunksize is such that the pending byte does not
|
||||
** falls at the end of a chunk. A region of up to 64K following
|
||||
** the pending byte is never written, so if the pending byte occurs
|
||||
** near the end of a chunk, that chunk will be too small. */
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
extern int sqlite3PendingByte;
|
||||
#else
|
||||
int sqlite3PendingByte = 0x40000000;
|
||||
#endif
|
||||
while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
|
||||
pGroup->szChunk += 65536;
|
||||
}
|
||||
}
|
||||
pGroup->zName = p;
|
||||
/* save off base filename, name length, and original open flags */
|
||||
memcpy(pGroup->zName, zName, nName+1);
|
||||
pGroup->nName = nName;
|
||||
pGroup->flags = flags;
|
||||
rc = multiplexSubFilename(pGroup, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
|
||||
if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
|
||||
}
|
||||
if( pSubOpen ){
|
||||
int exists, rc2, rc3;
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_int64 sz;
|
||||
|
||||
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc2==SQLITE_OK ){
|
||||
/* If the first overflow file exists and if the size of the main file
|
||||
** is different from the chunk size, that means the chunk size is set
|
||||
** set incorrectly. So fix it.
|
||||
**
|
||||
** Or, if the first overflow file does not exist and the main file is
|
||||
** larger than the chunk size, that means the chunk size is too small.
|
||||
** But we have no way of determining the intended chunk size, so
|
||||
** just disable the multiplexor all togethre.
|
||||
*/
|
||||
rc3 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc3==SQLITE_OK && exists && sz==(sz&0xffff0000) && sz>0
|
||||
&& sz!=pGroup->szChunk ){
|
||||
pGroup->szChunk = sz;
|
||||
}else if( rc3==SQLITE_OK && !exists && sz>pGroup->szChunk ){
|
||||
pGroup->bEnabled = 0;
|
||||
rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc==SQLITE_OK && zName ){
|
||||
int bExists;
|
||||
if( sz==0 ){
|
||||
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
|
||||
/* If opening a main journal file and the first chunk is zero
|
||||
** bytes in size, delete any subsequent chunks from the
|
||||
** file-system. */
|
||||
int iChunk = 1;
|
||||
do {
|
||||
rc = pOrigVfs->xAccess(pOrigVfs,
|
||||
pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists
|
||||
);
|
||||
if( rc==SQLITE_OK && bExists ){
|
||||
rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = multiplexSubFilename(pGroup, ++iChunk);
|
||||
}
|
||||
}
|
||||
}while( rc==SQLITE_OK && bExists );
|
||||
}
|
||||
}else{
|
||||
/* If the first overflow file exists and if the size of the main file
|
||||
** is different from the chunk size, that means the chunk size is set
|
||||
** set incorrectly. So fix it.
|
||||
**
|
||||
** Or, if the first overflow file does not exist and the main file is
|
||||
** larger than the chunk size, that means the chunk size is too small.
|
||||
** But we have no way of determining the intended chunk size, so
|
||||
** just disable the multiplexor all togethre.
|
||||
*/
|
||||
rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
|
||||
SQLITE_ACCESS_EXISTS, &bExists);
|
||||
bExists = multiplexSubSize(pGroup, 1, &rc)>0;
|
||||
if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
|
||||
&& sz!=pGroup->szChunk ){
|
||||
pGroup->szChunk = sz;
|
||||
}else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
|
||||
pGroup->bEnabled = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pSubOpen->pMethods->iVersion==1 ){
|
||||
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
|
||||
}else{
|
||||
|
@ -584,8 +625,33 @@ static int multiplexDelete(
|
|||
const char *zName, /* Name of file to delete */
|
||||
int syncDir
|
||||
){
|
||||
int rc;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
return pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
|
||||
rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If the main chunk was deleted successfully, also delete any subsequent
|
||||
** chunks - starting with the last (highest numbered).
|
||||
*/
|
||||
int nName = strlen(zName);
|
||||
char *z;
|
||||
z = sqlite3_malloc(nName + 5);
|
||||
if( z==0 ){
|
||||
rc = SQLITE_IOERR_NOMEM;
|
||||
}else{
|
||||
int iChunk = 0;
|
||||
int bExists;
|
||||
do{
|
||||
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);
|
||||
rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
|
||||
}while( rc==SQLITE_OK && bExists );
|
||||
while( rc==SQLITE_OK && iChunk>1 ){
|
||||
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
|
||||
rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
|
||||
}
|
||||
}
|
||||
sqlite3_free(z);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
|
||||
|
@ -662,7 +728,7 @@ static int multiplexRead(
|
|||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_READ;
|
||||
}else{
|
||||
|
@ -671,7 +737,7 @@ static int multiplexRead(
|
|||
}else{
|
||||
while( iAmt > 0 ){
|
||||
int i = (int)(iOfst / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
|
||||
if( extra<0 ) extra = 0;
|
||||
|
@ -707,16 +773,16 @@ static int multiplexWrite(
|
|||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_WRITE;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
|
||||
}
|
||||
}else{
|
||||
while( iAmt > 0 ){
|
||||
while( rc==SQLITE_OK && iAmt>0 ){
|
||||
int i = (int)(iOfst / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
|
||||
if( pSubOpen ){
|
||||
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
|
||||
pGroup->szChunk;
|
||||
|
@ -724,13 +790,9 @@ static int multiplexWrite(
|
|||
iAmt -= extra;
|
||||
rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
|
||||
iOfst % pGroup->szChunk);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pBuf = (char *)pBuf + iAmt;
|
||||
iOfst += iAmt;
|
||||
iAmt = extra;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_WRITE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -748,28 +810,35 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
|
|||
int rc = SQLITE_OK;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
|
||||
}
|
||||
}else{
|
||||
int rc2;
|
||||
int i;
|
||||
int iBaseGroup = (int)(size / pGroup->szChunk);
|
||||
sqlite3_file *pSubOpen;
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
|
||||
/* delete the chunks above the truncate limit */
|
||||
for(i=(int)(size / pGroup->szChunk)+1; i<pGroup->nReal; i++){
|
||||
multiplexSubClose(pGroup, i, pOrigVfs);
|
||||
for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
|
||||
if( pGroup->bTruncate ){
|
||||
multiplexSubClose(pGroup, i, pOrigVfs);
|
||||
}else{
|
||||
pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
|
||||
if( pSubOpen ){
|
||||
rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->szChunk), &rc2,0);
|
||||
if( pSubOpen ){
|
||||
rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_TRUNCATE;
|
||||
if( rc==SQLITE_OK ){
|
||||
pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
|
||||
if( pSubOpen ){
|
||||
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
|
||||
}
|
||||
}
|
||||
if( rc ) rc = SQLITE_IOERR_TRUNCATE;
|
||||
}
|
||||
multiplexLeave();
|
||||
return rc;
|
||||
|
@ -801,47 +870,21 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
|||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
multiplexGroup *pGroup = p->pGroup;
|
||||
int rc = SQLITE_OK;
|
||||
int rc2;
|
||||
int i;
|
||||
multiplexEnter();
|
||||
if( !pGroup->bEnabled ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen==0 ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}else{
|
||||
rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
|
||||
}
|
||||
}else{
|
||||
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
|
||||
*pSize = 0;
|
||||
for(i=0; 1; i++){
|
||||
sqlite3_file *pSubOpen = 0;
|
||||
int exists = 0;
|
||||
rc = multiplexSubFilename(pGroup, i);
|
||||
if( rc ) break;
|
||||
rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z,
|
||||
SQLITE_ACCESS_EXISTS, &exists);
|
||||
if( rc2==SQLITE_OK && exists){
|
||||
/* if it exists, open it */
|
||||
pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
|
||||
}else{
|
||||
/* stop at first "gap" */
|
||||
break;
|
||||
}
|
||||
if( pSubOpen ){
|
||||
sqlite3_int64 sz;
|
||||
rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
|
||||
if( rc2!=SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}else{
|
||||
if( sz>pGroup->szChunk ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
*pSize += sz;
|
||||
}
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK; i++){
|
||||
sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
|
||||
if( sz==0 ) break;
|
||||
*pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
|
||||
}
|
||||
}
|
||||
multiplexLeave();
|
||||
|
@ -853,7 +896,7 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
|
|||
static int multiplexLock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xLock(pSubOpen, lock);
|
||||
}
|
||||
|
@ -865,7 +908,7 @@ static int multiplexLock(sqlite3_file *pConn, int lock){
|
|||
static int multiplexUnlock(sqlite3_file *pConn, int lock){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
|
||||
}
|
||||
|
@ -877,7 +920,7 @@ static int multiplexUnlock(sqlite3_file *pConn, int lock){
|
|||
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
|
||||
}
|
||||
|
@ -925,9 +968,12 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
|||
rc = SQLITE_OK;
|
||||
break;
|
||||
default:
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
|
||||
pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -939,8 +985,8 @@ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
|
|||
static int multiplexSectorSize(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
if( pSubOpen ){
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
|
||||
return pSubOpen->pMethods->xSectorSize(pSubOpen);
|
||||
}
|
||||
return DEFAULT_SECTOR_SIZE;
|
||||
|
@ -951,7 +997,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
|
|||
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
|
||||
}
|
||||
|
@ -969,7 +1015,7 @@ static int multiplexShmMap(
|
|||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
|
||||
}
|
||||
|
@ -986,7 +1032,7 @@ static int multiplexShmLock(
|
|||
){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
|
||||
}
|
||||
|
@ -998,7 +1044,7 @@ static int multiplexShmLock(
|
|||
static void multiplexShmBarrier(sqlite3_file *pConn){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
pSubOpen->pMethods->xShmBarrier(pSubOpen);
|
||||
}
|
||||
|
@ -1009,7 +1055,7 @@ static void multiplexShmBarrier(sqlite3_file *pConn){
|
|||
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
|
||||
multiplexConn *p = (multiplexConn*)pConn;
|
||||
int rc;
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
|
||||
sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
|
||||
if( pSubOpen ){
|
||||
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
|
||||
}
|
||||
|
@ -1191,9 +1237,13 @@ static int test_multiplex_dump(
|
|||
for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
|
||||
pGroupTerm = Tcl_NewObj();
|
||||
|
||||
pGroup->zName[pGroup->nName] = '\0';
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
if( pGroup->zName ){
|
||||
pGroup->zName[pGroup->nName] = '\0';
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewStringObj(pGroup->zName, -1));
|
||||
}else{
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewIntObj(pGroup->nName));
|
||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
|
|
|
@ -389,7 +389,11 @@ static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|||
*/
|
||||
static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
VfslogFile *p = (VfslogFile *)pFile;
|
||||
return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -100,15 +100,16 @@ static void testpcacheShutdown(void *pArg){
|
|||
typedef struct testpcache testpcache;
|
||||
struct testpcache {
|
||||
int szPage; /* Size of each page. Multiple of 8. */
|
||||
int szExtra; /* Size of extra data that accompanies each page */
|
||||
int bPurgeable; /* True if the page cache is purgeable */
|
||||
int nFree; /* Number of unused slots in a[] */
|
||||
int nPinned; /* Number of pinned slots in a[] */
|
||||
unsigned iRand; /* State of the PRNG */
|
||||
unsigned iMagic; /* Magic number for sanity checking */
|
||||
struct testpcachePage {
|
||||
sqlite3_pcache_page page; /* Base class */
|
||||
unsigned key; /* The key for this page. 0 means unallocated */
|
||||
int isPinned; /* True if the page is pinned */
|
||||
void *pData; /* Data for this page */
|
||||
} a[TESTPCACHE_NPAGE]; /* All pages in the cache */
|
||||
};
|
||||
|
||||
|
@ -129,27 +130,33 @@ static unsigned testpcacheRandom(testpcache *p){
|
|||
/*
|
||||
** Allocate a new page cache instance.
|
||||
*/
|
||||
static sqlite3_pcache *testpcacheCreate(int szPage, int bPurgeable){
|
||||
static sqlite3_pcache *testpcacheCreate(
|
||||
int szPage,
|
||||
int szExtra,
|
||||
int bPurgeable
|
||||
){
|
||||
int nMem;
|
||||
char *x;
|
||||
testpcache *p;
|
||||
int i;
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
szPage = (szPage+7)&~7;
|
||||
nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*szPage;
|
||||
nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*(szPage+szExtra);
|
||||
p = sqlite3_malloc( nMem );
|
||||
if( p==0 ) return 0;
|
||||
x = (char*)&p[1];
|
||||
p->szPage = szPage;
|
||||
p->szExtra = szExtra;
|
||||
p->nFree = TESTPCACHE_NPAGE;
|
||||
p->nPinned = 0;
|
||||
p->iRand = testpcacheGlobal.prngSeed;
|
||||
p->bPurgeable = bPurgeable;
|
||||
p->iMagic = TESTPCACHE_VALID;
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++, x += szPage){
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++, x += (szPage+szExtra)){
|
||||
p->a[i].key = 0;
|
||||
p->a[i].isPinned = 0;
|
||||
p->a[i].pData = (void*)x;
|
||||
p->a[i].page.pBuf = (void*)x;
|
||||
p->a[i].page.pExtra = (void*)&x[szPage];
|
||||
}
|
||||
testpcacheGlobal.nInstance++;
|
||||
return (sqlite3_pcache*)p;
|
||||
|
@ -161,7 +168,6 @@ static sqlite3_pcache *testpcacheCreate(int szPage, int bPurgeable){
|
|||
static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
assert( p->iMagic==TESTPCACHE_VALID );
|
||||
assert( newSize>=1 );
|
||||
assert( testpcacheGlobal.pDummy!=0 );
|
||||
assert( testpcacheGlobal.nInstance>0 );
|
||||
}
|
||||
|
@ -181,7 +187,7 @@ static int testpcachePagecount(sqlite3_pcache *pCache){
|
|||
/*
|
||||
** Fetch a page.
|
||||
*/
|
||||
static void *testpcacheFetch(
|
||||
static sqlite3_pcache_page *testpcacheFetch(
|
||||
sqlite3_pcache *pCache,
|
||||
unsigned key,
|
||||
int createFlag
|
||||
|
@ -200,7 +206,7 @@ static void *testpcacheFetch(
|
|||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
p->a[i].isPinned = 1;
|
||||
}
|
||||
return p->a[i].pData;
|
||||
return &p->a[i].page;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,11 +243,12 @@ static void *testpcacheFetch(
|
|||
if( p->a[j].key==0 ){
|
||||
p->a[j].key = key;
|
||||
p->a[j].isPinned = 1;
|
||||
memset(p->a[j].pData, 0, p->szPage);
|
||||
memset(p->a[j].page.pBuf, 0, p->szPage);
|
||||
memset(p->a[j].page.pExtra, 0, p->szExtra);
|
||||
p->nPinned++;
|
||||
p->nFree--;
|
||||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
return p->a[j].pData;
|
||||
return &p->a[j].page;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,10 +270,11 @@ static void *testpcacheFetch(
|
|||
if( p->a[j].key>0 && p->a[j].isPinned==0 ){
|
||||
p->a[j].key = key;
|
||||
p->a[j].isPinned = 1;
|
||||
memset(p->a[j].pData, 0, p->szPage);
|
||||
memset(p->a[j].page.pBuf, 0, p->szPage);
|
||||
memset(p->a[j].page.pExtra, 0, p->szExtra);
|
||||
p->nPinned++;
|
||||
assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
|
||||
return p->a[j].pData;
|
||||
return &p->a[j].page;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +288,7 @@ static void *testpcacheFetch(
|
|||
*/
|
||||
static void testpcacheUnpin(
|
||||
sqlite3_pcache *pCache,
|
||||
void *pOldPage,
|
||||
sqlite3_pcache_page *pOldPage,
|
||||
int discard
|
||||
){
|
||||
testpcache *p = (testpcache*)pCache;
|
||||
|
@ -300,7 +308,7 @@ static void testpcacheUnpin(
|
|||
}
|
||||
|
||||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].pData==pOldPage ){
|
||||
if( &p->a[i].page==pOldPage ){
|
||||
/* The pOldPage pointer always points to a pinned page */
|
||||
assert( p->a[i].isPinned );
|
||||
p->a[i].isPinned = 0;
|
||||
|
@ -325,7 +333,7 @@ static void testpcacheUnpin(
|
|||
*/
|
||||
static void testpcacheRekey(
|
||||
sqlite3_pcache *pCache,
|
||||
void *pOldPage,
|
||||
sqlite3_pcache_page *pOldPage,
|
||||
unsigned oldKey,
|
||||
unsigned newKey
|
||||
){
|
||||
|
@ -354,7 +362,7 @@ static void testpcacheRekey(
|
|||
for(i=0; i<TESTPCACHE_NPAGE; i++){
|
||||
if( p->a[i].key==oldKey ){
|
||||
/* The oldKey and pOldPage parameters match */
|
||||
assert( p->a[i].pData==pOldPage );
|
||||
assert( &p->a[i].page==pOldPage );
|
||||
/* Page to be rekeyed must be pinned */
|
||||
assert( p->a[i].isPinned );
|
||||
p->a[i].key = newKey;
|
||||
|
@ -422,7 +430,8 @@ void installTestPCache(
|
|||
unsigned prngSeed, /* Seed for the PRNG */
|
||||
unsigned highStress /* Call xStress agressively */
|
||||
){
|
||||
static const sqlite3_pcache_methods testPcache = {
|
||||
static const sqlite3_pcache_methods2 testPcache = {
|
||||
1,
|
||||
(void*)&testpcacheGlobal,
|
||||
testpcacheInit,
|
||||
testpcacheShutdown,
|
||||
|
@ -435,7 +444,7 @@ void installTestPCache(
|
|||
testpcacheTruncate,
|
||||
testpcacheDestroy,
|
||||
};
|
||||
static sqlite3_pcache_methods defaultPcache;
|
||||
static sqlite3_pcache_methods2 defaultPcache;
|
||||
static int isInstalled = 0;
|
||||
|
||||
assert( testpcacheGlobal.nInstance==0 );
|
||||
|
@ -446,12 +455,12 @@ void installTestPCache(
|
|||
testpcacheGlobal.highStress = highStress;
|
||||
if( installFlag!=isInstalled ){
|
||||
if( installFlag ){
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE, &defaultPcache);
|
||||
sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &defaultPcache);
|
||||
assert( defaultPcache.xCreate!=testpcacheCreate );
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &testPcache);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &testPcache);
|
||||
}else{
|
||||
assert( defaultPcache.xCreate!=0 );
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultPcache);
|
||||
sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultPcache);
|
||||
}
|
||||
isInstalled = installFlag;
|
||||
}
|
||||
|
|
729
src/test_quota.c
729
src/test_quota.c
|
@ -27,7 +27,7 @@
|
|||
** files within the group is less than the new quota, then the write
|
||||
** continues as if nothing had happened.
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include "test_quota.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
@ -111,6 +111,18 @@ struct quotaConn {
|
|||
/* The underlying VFS sqlite3_file is appended to this object */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following object records the state of an
|
||||
** open file. This object is opaque to all users - the internal
|
||||
** structure is only visible to the functions below.
|
||||
*/
|
||||
struct quota_FILE {
|
||||
FILE *f; /* Open stdio file pointer */
|
||||
sqlite3_int64 iOfst; /* Current offset into the file */
|
||||
quotaFile *pFile; /* The file record in the quota system */
|
||||
};
|
||||
|
||||
|
||||
/************************* Global Variables **********************************/
|
||||
/*
|
||||
** All global variables used by this file are containing within the following
|
||||
|
@ -225,9 +237,11 @@ static void quotaGroupDeref(quotaGroup *pGroup){
|
|||
**
|
||||
** [^...] Matches one character not in the enclosed list.
|
||||
**
|
||||
** / Matches "/" or "\\"
|
||||
**
|
||||
*/
|
||||
static int quotaStrglob(const char *zGlob, const char *z){
|
||||
int c, c2;
|
||||
int c, c2, cx;
|
||||
int invert;
|
||||
int seen;
|
||||
|
||||
|
@ -244,8 +258,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
|
|||
}
|
||||
return (*z)!=0;
|
||||
}
|
||||
cx = (c=='/') ? '\\' : c;
|
||||
while( (c2 = (*(z++)))!=0 ){
|
||||
while( c2!=c ){
|
||||
while( c2!=c && c2!=cx ){
|
||||
c2 = *(z++);
|
||||
if( c2==0 ) return 0;
|
||||
}
|
||||
|
@ -283,6 +298,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
|
|||
c2 = *(zGlob++);
|
||||
}
|
||||
if( c2==0 || (seen ^ invert)==0 ) return 0;
|
||||
}else if( c=='/' ){
|
||||
if( z[0]!='/' && z[0]!='\\' ) return 0;
|
||||
z++;
|
||||
}else{
|
||||
if( c!=(*(z++)) ) return 0;
|
||||
}
|
||||
|
@ -313,14 +331,131 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
|
|||
/* Find a file in a quota group and return a pointer to that file.
|
||||
** Return NULL if the file is not in the group.
|
||||
*/
|
||||
static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
|
||||
static quotaFile *quotaFindFile(
|
||||
quotaGroup *pGroup, /* Group in which to look for the file */
|
||||
const char *zName, /* Full pathname of the file */
|
||||
int createFlag /* Try to create the file if not found */
|
||||
){
|
||||
quotaFile *pFile = pGroup->pFiles;
|
||||
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
|
||||
pFile = pFile->pNext;
|
||||
}
|
||||
if( pFile==0 && createFlag ){
|
||||
int nName = strlen(zName);
|
||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
if( pFile ){
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
pFile->zFilename = (char*)&pFile[1];
|
||||
memcpy(pFile->zFilename, zName, nName+1);
|
||||
pFile->pNext = pGroup->pFiles;
|
||||
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
|
||||
pFile->ppPrev = &pGroup->pFiles;
|
||||
pGroup->pFiles = pFile;
|
||||
pFile->pGroup = pGroup;
|
||||
}
|
||||
}
|
||||
return pFile;
|
||||
}
|
||||
|
||||
/*
|
||||
** Figure out if we are dealing with Unix, Windows, or some other
|
||||
** operating system. After the following block of preprocess macros,
|
||||
** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
|
||||
** will defined to either 1 or 0. One of the four will be 1. The other
|
||||
** three will be 0.
|
||||
*/
|
||||
#if defined(SQLITE_OS_OTHER)
|
||||
# if SQLITE_OS_OTHER==1
|
||||
# undef SQLITE_OS_UNIX
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# undef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# undef SQLITE_OS_OS2
|
||||
# define SQLITE_OS_OS2 0
|
||||
# else
|
||||
# undef SQLITE_OS_OTHER
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
||||
# define SQLITE_OS_OTHER 0
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
|
||||
|| defined(__MINGW32__) || defined(__BORLANDC__)
|
||||
# define SQLITE_OS_WIN 1
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# define SQLITE_OS_OS2 0
|
||||
# elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
|
||||
|| defined(_OS2_) || defined(__OS2__)
|
||||
# define SQLITE_OS_WIN 0
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# define SQLITE_OS_OS2 1
|
||||
# else
|
||||
# define SQLITE_OS_WIN 0
|
||||
# define SQLITE_OS_UNIX 1
|
||||
# define SQLITE_OS_OS2 0
|
||||
# endif
|
||||
# else
|
||||
# define SQLITE_OS_UNIX 0
|
||||
# define SQLITE_OS_OS2 0
|
||||
# endif
|
||||
#else
|
||||
# ifndef SQLITE_OS_WIN
|
||||
# define SQLITE_OS_WIN 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if SQLITE_OS_UNIX
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
|
||||
** translated text.. Call quota_mbcs_free() to deallocate any memory
|
||||
** used to store the returned pointer when done.
|
||||
*/
|
||||
static char *quota_utf8_to_mbcs(const char *zUtf8){
|
||||
#if SQLITE_OS_WIN
|
||||
int n; /* Bytes in zUtf8 */
|
||||
int nWide; /* number of UTF-16 characters */
|
||||
int nMbcs; /* Bytes of MBCS */
|
||||
LPWSTR zTmpWide; /* The UTF16 text */
|
||||
char *zMbcs; /* The MBCS text */
|
||||
int codepage; /* Code page used by fopen() */
|
||||
|
||||
n = strlen(zUtf8);
|
||||
nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0);
|
||||
if( nWide==0 ) return 0;
|
||||
zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) );
|
||||
if( zTmpWide==0 ) return 0;
|
||||
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide);
|
||||
codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||
nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0);
|
||||
zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0;
|
||||
if( zMbcs ){
|
||||
WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0);
|
||||
}
|
||||
sqlite3_free(zTmpWide);
|
||||
return zMbcs;
|
||||
#else
|
||||
return (char*)zUtf8; /* No-op on unix */
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate any memory allocated by quota_utf8_to_mbcs().
|
||||
*/
|
||||
static void quota_mbcs_free(char *zOld){
|
||||
#if SQLITE_OS_WIN
|
||||
sqlite3_free(zOld);
|
||||
#else
|
||||
/* No-op on unix */
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************* VFS Method Wrappers *****************************/
|
||||
/*
|
||||
** This is the xOpen method used for the "quota" VFS.
|
||||
|
@ -364,25 +499,13 @@ static int quotaOpen(
|
|||
pSubOpen = quotaSubOpen(pConn);
|
||||
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
pFile = quotaFindFile(pGroup, zName);
|
||||
pFile = quotaFindFile(pGroup, zName, 1);
|
||||
if( pFile==0 ){
|
||||
int nName = strlen(zName);
|
||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
if( pFile==0 ){
|
||||
quotaLeave();
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
pFile->zFilename = (char*)&pFile[1];
|
||||
memcpy(pFile->zFilename, zName, nName+1);
|
||||
pFile->pNext = pGroup->pFiles;
|
||||
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
|
||||
pFile->ppPrev = &pGroup->pFiles;
|
||||
pGroup->pFiles = pFile;
|
||||
pFile->pGroup = pGroup;
|
||||
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
|
||||
quotaLeave();
|
||||
pSubOpen->pMethods->xClose(pSubOpen);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
|
||||
pFile->nRef++;
|
||||
pQuotaOpen->pFile = pFile;
|
||||
if( pSubOpen->pMethods->iVersion==1 ){
|
||||
|
@ -423,7 +546,7 @@ static int quotaDelete(
|
|||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zName);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zName);
|
||||
pFile = quotaFindFile(pGroup, zName, 0);
|
||||
if( pFile ){
|
||||
if( pFile->nRef ){
|
||||
pFile->deleteOnClose = 1;
|
||||
|
@ -455,7 +578,10 @@ static int quotaClose(sqlite3_file *pConn){
|
|||
pFile->nRef--;
|
||||
if( pFile->nRef==0 ){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
|
||||
if( pFile->deleteOnClose ){
|
||||
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||
quotaRemoveFile(pFile);
|
||||
}
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
quotaLeave();
|
||||
|
@ -589,7 +715,13 @@ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
|||
*/
|
||||
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||
#if defined(SQLITE_FCNTL_VFSNAME)
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pass xSectorSize requests through to the original VFS unchanged.
|
||||
|
@ -805,33 +937,256 @@ int sqlite3_quota_file(const char *zFilename){
|
|||
int rc;
|
||||
int outFlags = 0;
|
||||
sqlite3_int64 iSize;
|
||||
fd = sqlite3_malloc(gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+1);
|
||||
if( fd==0 ) return SQLITE_NOMEM;
|
||||
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
|
||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||
int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2;
|
||||
|
||||
/* Allocate space for a file-handle and the full path for file zFilename */
|
||||
fd = (sqlite3_file *)sqlite3_malloc(nAlloc);
|
||||
if( fd==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
zFull = &((char *)fd)[gQuota.sThisVfs.szOsFile];
|
||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
zFull[strlen(zFull)+1] = '\0';
|
||||
rc = quotaOpen(&gQuota.sThisVfs, zFull, fd,
|
||||
SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_DB, &outFlags);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xFileSize(fd, &iSize);
|
||||
fd->pMethods->xClose(fd);
|
||||
}else if( rc==SQLITE_CANTOPEN ){
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zFull);
|
||||
if( pFile ) quotaRemoveFile(pFile);
|
||||
if( rc==SQLITE_OK ){
|
||||
fd->pMethods->xFileSize(fd, &iSize);
|
||||
fd->pMethods->xClose(fd);
|
||||
}else if( rc==SQLITE_CANTOPEN ){
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zFull, 0);
|
||||
if( pFile ) quotaRemoveFile(pFile);
|
||||
}
|
||||
quotaLeave();
|
||||
}
|
||||
quotaLeave();
|
||||
}
|
||||
|
||||
sqlite3_free(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a potentially quotaed file for I/O.
|
||||
*/
|
||||
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
|
||||
quota_FILE *p = 0;
|
||||
char *zFull = 0;
|
||||
char *zFullTranslated;
|
||||
int rc;
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
|
||||
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
|
||||
if( zFull==0 ) return 0;
|
||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||
if( rc ) goto quota_fopen_error;
|
||||
p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
|
||||
if( p==0 ) goto quota_fopen_error;
|
||||
memset(p, 0, sizeof(*p));
|
||||
zFullTranslated = quota_utf8_to_mbcs(zFull);
|
||||
if( zFullTranslated==0 ) goto quota_fopen_error;
|
||||
p->f = fopen(zFullTranslated, zMode);
|
||||
quota_mbcs_free(zFullTranslated);
|
||||
if( p->f==0 ) goto quota_fopen_error;
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
if( pGroup ){
|
||||
pFile = quotaFindFile(pGroup, zFull, 1);
|
||||
if( pFile==0 ){
|
||||
quotaLeave();
|
||||
goto quota_fopen_error;
|
||||
}
|
||||
pFile->nRef++;
|
||||
p->pFile = pFile;
|
||||
}
|
||||
quotaLeave();
|
||||
sqlite3_free(zFull);
|
||||
return p;
|
||||
|
||||
quota_fopen_error:
|
||||
sqlite3_free(zFull);
|
||||
if( p && p->f ) fclose(p->f);
|
||||
sqlite3_free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read content from a quota_FILE
|
||||
*/
|
||||
size_t sqlite3_quota_fread(
|
||||
void *pBuf, /* Store the content here */
|
||||
size_t size, /* Size of each element */
|
||||
size_t nmemb, /* Number of elements to read */
|
||||
quota_FILE *p /* Read from this quota_FILE object */
|
||||
){
|
||||
return fread(pBuf, size, nmemb, p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Write content into a quota_FILE. Invoke the quota callback and block
|
||||
** the write if we exceed quota.
|
||||
*/
|
||||
size_t sqlite3_quota_fwrite(
|
||||
void *pBuf, /* Take content to write from here */
|
||||
size_t size, /* Size of each element */
|
||||
size_t nmemb, /* Number of elements */
|
||||
quota_FILE *p /* Write to this quota_FILE objecct */
|
||||
){
|
||||
sqlite3_int64 iOfst;
|
||||
sqlite3_int64 iEnd;
|
||||
sqlite3_int64 szNew;
|
||||
quotaFile *pFile;
|
||||
|
||||
iOfst = ftell(p->f);
|
||||
iEnd = iOfst + size*nmemb;
|
||||
pFile = p->pFile;
|
||||
if( pFile && pFile->iSize<iEnd ){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
quotaEnter();
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
if( pGroup->xCallback ){
|
||||
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||
pGroup->pArg);
|
||||
}
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
|
||||
nmemb = (iEnd - iOfst)/size;
|
||||
iEnd = iOfst + size*nmemb;
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
}
|
||||
}
|
||||
pGroup->iSize = szNew;
|
||||
pFile->iSize = iEnd;
|
||||
quotaLeave();
|
||||
}
|
||||
return fwrite(pBuf, size, nmemb, p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Close an open quota_FILE stream.
|
||||
*/
|
||||
int sqlite3_quota_fclose(quota_FILE *p){
|
||||
int rc;
|
||||
quotaFile *pFile;
|
||||
rc = fclose(p->f);
|
||||
pFile = p->pFile;
|
||||
if( pFile ){
|
||||
quotaEnter();
|
||||
pFile->nRef--;
|
||||
if( pFile->nRef==0 ){
|
||||
quotaGroup *pGroup = pFile->pGroup;
|
||||
if( pFile->deleteOnClose ){
|
||||
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||
quotaRemoveFile(pFile);
|
||||
}
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
quotaLeave();
|
||||
}
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Flush memory buffers for a quota_FILE to disk.
|
||||
*/
|
||||
int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
|
||||
int rc;
|
||||
rc = fflush(p->f);
|
||||
if( rc==0 && doFsync ){
|
||||
#if SQLITE_OS_UNIX
|
||||
rc = fsync(fileno(p->f));
|
||||
#endif
|
||||
#if SQLITE_OS_WIN
|
||||
rc = _commit(_fileno(p->f));
|
||||
#endif
|
||||
}
|
||||
return rc!=0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Seek on a quota_FILE stream.
|
||||
*/
|
||||
int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
|
||||
return fseek(p->f, offset, whence);
|
||||
}
|
||||
|
||||
/*
|
||||
** rewind a quota_FILE stream.
|
||||
*/
|
||||
void sqlite3_quota_rewind(quota_FILE *p){
|
||||
rewind(p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Tell the current location of a quota_FILE stream.
|
||||
*/
|
||||
long sqlite3_quota_ftell(quota_FILE *p){
|
||||
return ftell(p->f);
|
||||
}
|
||||
|
||||
/*
|
||||
** Remove a managed file. Update quotas accordingly.
|
||||
*/
|
||||
int sqlite3_quota_remove(const char *zFilename){
|
||||
char *zFull; /* Full pathname for zFilename */
|
||||
int nFull; /* Number of bytes in zFilename */
|
||||
int rc; /* Result code */
|
||||
quotaGroup *pGroup; /* Group containing zFilename */
|
||||
quotaFile *pFile; /* A file in the group */
|
||||
quotaFile *pNextFile; /* next file in the group */
|
||||
int diff; /* Difference between filenames */
|
||||
char c; /* First character past end of pattern */
|
||||
|
||||
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
|
||||
if( zFull==0 ) return SQLITE_NOMEM;
|
||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||
if( rc ){
|
||||
sqlite3_free(zFull);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Figure out the length of the full pathname. If the name ends with
|
||||
** / (or \ on windows) then remove the trailing /.
|
||||
*/
|
||||
nFull = strlen(zFull);
|
||||
if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
|
||||
nFull--;
|
||||
zFull[nFull] = 0;
|
||||
}
|
||||
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
if( pGroup ){
|
||||
for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
|
||||
pNextFile = pFile->pNext;
|
||||
diff = memcmp(zFull, pFile->zFilename, nFull);
|
||||
if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
|
||||
if( pFile->nRef ){
|
||||
pFile->deleteOnClose = 1;
|
||||
}else{
|
||||
rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||
quotaRemoveFile(pFile);
|
||||
quotaGroupDeref(pGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
quotaLeave();
|
||||
sqlite3_free(zFull);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***************************** Test Code ***********************************/
|
||||
#ifdef SQLITE_TEST
|
||||
|
@ -1060,9 +1415,13 @@ static int test_quota_dump(
|
|||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||
Tcl_NewWideIntObj(pGroup->iSize));
|
||||
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
|
||||
int i;
|
||||
char zTemp[1000];
|
||||
pFileTerm = Tcl_NewObj();
|
||||
sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename);
|
||||
for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; }
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewStringObj(pFile->zFilename, -1));
|
||||
Tcl_NewStringObj(zTemp, -1));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
Tcl_NewWideIntObj(pFile->iSize));
|
||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||
|
@ -1078,6 +1437,272 @@ static int test_quota_dump(
|
|||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fopen FILENAME MODE
|
||||
*/
|
||||
static int test_quota_fopen(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zFilename; /* File pattern to configure */
|
||||
const char *zMode; /* Mode string */
|
||||
quota_FILE *p; /* Open string object */
|
||||
char zReturn[50]; /* Name of pointer to return */
|
||||
|
||||
/* Process arguments */
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFilename = Tcl_GetString(objv[1]);
|
||||
zMode = Tcl_GetString(objv[2]);
|
||||
p = sqlite3_quota_fopen(zFilename, zMode);
|
||||
sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p);
|
||||
Tcl_SetResult(interp, zReturn, TCL_VOLATILE);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/* Defined in test1.c */
|
||||
extern void *sqlite3TestTextToPtr(const char*);
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
|
||||
*/
|
||||
static int test_quota_fread(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
char *zBuf;
|
||||
int sz;
|
||||
int nElem;
|
||||
int got;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
|
||||
zBuf = (char*)sqlite3_malloc( sz*nElem + 1 );
|
||||
if( zBuf==0 ){
|
||||
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
got = sqlite3_quota_fread(zBuf, sz, nElem, p);
|
||||
if( got<0 ) got = 0;
|
||||
zBuf[got*sz] = 0;
|
||||
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
||||
sqlite3_free(zBuf);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
|
||||
*/
|
||||
static int test_quota_fwrite(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
char *zBuf;
|
||||
int sz;
|
||||
int nElem;
|
||||
int got;
|
||||
|
||||
if( objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
|
||||
zBuf = Tcl_GetString(objv[4]);
|
||||
got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fclose HANDLE
|
||||
*/
|
||||
static int test_quota_fclose(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
rc = sqlite3_quota_fclose(p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
|
||||
*/
|
||||
static int test_quota_fflush(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
int rc;
|
||||
int doSync = 0;
|
||||
|
||||
if( objc!=2 && objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
if( objc==3 ){
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_quota_fflush(p, doSync);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
|
||||
*/
|
||||
static int test_quota_fseek(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
int ofst;
|
||||
const char *zWhence;
|
||||
int whence;
|
||||
int rc;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR;
|
||||
zWhence = Tcl_GetString(objv[3]);
|
||||
if( strcmp(zWhence, "SEEK_SET")==0 ){
|
||||
whence = SEEK_SET;
|
||||
}else if( strcmp(zWhence, "SEEK_CUR")==0 ){
|
||||
whence = SEEK_CUR;
|
||||
}else if( strcmp(zWhence, "SEEK_END")==0 ){
|
||||
whence = SEEK_END;
|
||||
}else{
|
||||
Tcl_AppendResult(interp,
|
||||
"WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_quota_fseek(p, ofst, whence);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_rewind HANDLE
|
||||
*/
|
||||
static int test_quota_rewind(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
sqlite3_quota_rewind(p);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_ftell HANDLE
|
||||
*/
|
||||
static int test_quota_ftell(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
quota_FILE *p;
|
||||
sqlite3_int64 x;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||
x = sqlite3_quota_ftell(p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_remove FILENAME
|
||||
*/
|
||||
static int test_quota_remove(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zFilename; /* File pattern to configure */
|
||||
int rc;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zFilename = Tcl_GetString(objv[1]);
|
||||
rc = sqlite3_quota_remove(zFilename);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: sqlite3_quota_glob PATTERN TEXT
|
||||
**
|
||||
** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
|
||||
** and return 0 if it does not.
|
||||
*/
|
||||
static int test_quota_glob(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zPattern; /* The glob pattern */
|
||||
const char *zText; /* Text to compare agains the pattern */
|
||||
int rc;
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zPattern = Tcl_GetString(objv[1]);
|
||||
zText = Tcl_GetString(objv[2]);
|
||||
rc = quotaStrglob(zPattern, zText);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine registers the custom TCL commands defined in this
|
||||
** module. This should be the only procedure visible from outside
|
||||
|
@ -1089,10 +1714,20 @@ int Sqlitequota_Init(Tcl_Interp *interp){
|
|||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_file", test_quota_file },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_file", test_quota_file },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
{ "sqlite3_quota_fopen", test_quota_fopen },
|
||||
{ "sqlite3_quota_fread", test_quota_fread },
|
||||
{ "sqlite3_quota_fwrite", test_quota_fwrite },
|
||||
{ "sqlite3_quota_fclose", test_quota_fclose },
|
||||
{ "sqlite3_quota_fflush", test_quota_fflush },
|
||||
{ "sqlite3_quota_fseek", test_quota_fseek },
|
||||
{ "sqlite3_quota_rewind", test_quota_rewind },
|
||||
{ "sqlite3_quota_ftell", test_quota_ftell },
|
||||
{ "sqlite3_quota_remove", test_quota_remove },
|
||||
{ "sqlite3_quota_glob", test_quota_glob },
|
||||
};
|
||||
int i;
|
||||
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
** 2011 December 1
|
||||
**
|
||||
** 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 contains the interface definition for the quota a VFS shim.
|
||||
**
|
||||
** This particular shim enforces a quota system on files. One or more
|
||||
** database files are in a "quota group" that is defined by a GLOB
|
||||
** pattern. A quota is set for the combined size of all files in the
|
||||
** the group. A quota of zero means "no limit". If the total size
|
||||
** of all files in the quota group is greater than the limit, then
|
||||
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
|
||||
**
|
||||
** However, before returning SQLITE_FULL, the write requests invoke
|
||||
** a callback function that is configurable for each quota group.
|
||||
** This callback has the opportunity to enlarge the quota. If the
|
||||
** callback does enlarge the quota such that the total size of all
|
||||
** files within the group is less than the new quota, then the write
|
||||
** continues as if nothing had happened.
|
||||
*/
|
||||
#ifndef _QUOTA_H_
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/* Make this callable from C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The quota VFS shim is named "quota". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
|
||||
|
||||
/*
|
||||
** Shutdown the quota system.
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining quota groups.
|
||||
*/
|
||||
int sqlite3_quota_shutdown(void);
|
||||
|
||||
/*
|
||||
** Create or destroy a quota group.
|
||||
**
|
||||
** The quota group is defined by the zPattern. When calling this routine
|
||||
** with a zPattern for a quota group that already exists, this routine
|
||||
** merely updates the iLimit, xCallback, and pArg values for that quota
|
||||
** group. If zPattern is new, then a new quota group is created.
|
||||
**
|
||||
** The zPattern is always compared against the full pathname of the file.
|
||||
** Even if APIs are called with relative pathnames, SQLite converts the
|
||||
** name to a full pathname before comparing it against zPattern. zPattern
|
||||
** is a glob pattern with the following matching rules:
|
||||
**
|
||||
** '*' Matches any sequence of zero or more characters.
|
||||
**
|
||||
** '?' Matches exactly one character.
|
||||
**
|
||||
** [...] Matches one character from the enclosed list of
|
||||
** characters. "]" can be part of the list if it is
|
||||
** the first character. Within the list "X-Y" matches
|
||||
** characters X or Y or any character in between the
|
||||
** two. Ex: "[0-9]" matches any digit.
|
||||
**
|
||||
** [^...] Matches one character not in the enclosed list.
|
||||
**
|
||||
** / Matches either / or \. This allows glob patterns
|
||||
** containing / to work on both unix and windows.
|
||||
**
|
||||
** Note that, unlike unix shell globbing, the directory separator "/"
|
||||
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
|
||||
** matches any files anywhere in the directory hierarchy beneath
|
||||
** /abc/xyz.
|
||||
**
|
||||
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
|
||||
** matched as if each byte were a separate character.
|
||||
**
|
||||
** If the iLimit for a quota group is set to zero, then the quota group
|
||||
** is disabled and will be deleted when the last database connection using
|
||||
** the quota group is closed.
|
||||
**
|
||||
** Calling this routine on a zPattern that does not exist and with a
|
||||
** zero iLimit is a no-op.
|
||||
**
|
||||
** A quota group must exist with a non-zero iLimit prior to opening
|
||||
** database connections if those connections are to participate in the
|
||||
** quota group. Creating a quota group does not affect database connections
|
||||
** that are already open.
|
||||
**
|
||||
** The patterns that define the various quota groups should be distinct.
|
||||
** If the same filename matches more than one quota group pattern, then
|
||||
** the behavior of this package is undefined.
|
||||
*/
|
||||
int sqlite3_quota_set(
|
||||
const char *zPattern, /* The filename pattern */
|
||||
sqlite3_int64 iLimit, /* New quota to set for this quota group */
|
||||
void (*xCallback)( /* Callback invoked when going over quota */
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
),
|
||||
void *pArg, /* client data passed thru to callback */
|
||||
void (*xDestroy)(void*) /* Optional destructor for pArg */
|
||||
);
|
||||
|
||||
/*
|
||||
** Bring the named file under quota management, assuming its name matches
|
||||
** the glob pattern of some quota group. Or if it is already under
|
||||
** management, update its size. If zFilename does not match the glob
|
||||
** pattern of any quota group, this routine is a no-op.
|
||||
*/
|
||||
int sqlite3_quota_file(const char *zFilename);
|
||||
|
||||
/*
|
||||
** The following object serves the same role as FILE in the standard C
|
||||
** library. It represents an open connection to a file on disk for I/O.
|
||||
**
|
||||
** A single quota_FILE should not be used by two or more threads at the
|
||||
** same time. Multiple threads can be using different quota_FILE objects
|
||||
** simultaneously, but not the same quota_FILE object.
|
||||
*/
|
||||
typedef struct quota_FILE quota_FILE;
|
||||
|
||||
/*
|
||||
** Create a new quota_FILE object used to read and/or write to the
|
||||
** file zFilename. The zMode parameter is as with standard library zMode.
|
||||
*/
|
||||
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
|
||||
|
||||
/*
|
||||
** Perform I/O against a quota_FILE object. When doing writes, the
|
||||
** quota mechanism may result in a short write, in order to prevent
|
||||
** the sum of sizes of all files from going over quota.
|
||||
*/
|
||||
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
|
||||
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
|
||||
|
||||
/*
|
||||
** Flush all written content held in memory buffers out to disk.
|
||||
** This is the equivalent of fflush() in the standard library.
|
||||
**
|
||||
** If the hardSync parameter is true (non-zero) then this routine
|
||||
** also forces OS buffers to disk - the equivalent of fsync().
|
||||
**
|
||||
** This routine return zero on success and non-zero if something goes
|
||||
** wrong.
|
||||
*/
|
||||
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
|
||||
|
||||
/*
|
||||
** Close a quota_FILE object and free all associated resources. The
|
||||
** file remains under quota management.
|
||||
*/
|
||||
int sqlite3_quota_fclose(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Move the read/write pointer for a quota_FILE object. Or tell the
|
||||
** current location of the read/write pointer.
|
||||
*/
|
||||
int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||
void sqlite3_quota_rewind(quota_FILE*);
|
||||
long sqlite3_quota_ftell(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Delete a file from the disk, if that file is under quota management.
|
||||
** Adjust quotas accordingly.
|
||||
**
|
||||
** If zFilename is the name of a directory that matches one of the
|
||||
** quota glob patterns, then all files under quota management that
|
||||
** are contained within that directory are deleted.
|
||||
**
|
||||
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
|
||||
** When deleting a directory of files, if the deletion of any one
|
||||
** file fails (for example due to an I/O error), then this routine
|
||||
** returns immediately, with the error code, and does not try to
|
||||
** delete any of the other files in the specified directory.
|
||||
**
|
||||
** All files are removed from quota management and deleted from disk.
|
||||
** However, no attempt is made to remove empty directories.
|
||||
**
|
||||
** This routine is a no-op for files that are not under quota management.
|
||||
*/
|
||||
int sqlite3_quota_remove(const char *zFilename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _QUOTA_H_ */
|
|
@ -369,7 +369,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
|
|||
|
||||
/* The default page size and offset */
|
||||
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
|
||||
pCsr->iOffset = pCsr->szPage * (pCsr->iPageno - 1);
|
||||
pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
|
||||
|
||||
/* If connected to a ZIPVFS backend, override the page size and
|
||||
** offset with actual values obtained from ZIPVFS.
|
||||
|
|
|
@ -988,7 +988,7 @@ static int testvfs_obj_cmd(
|
|||
switch( aSubcmd[i].eCmd ){
|
||||
case CMD_SHM: {
|
||||
Tcl_Obj *pObj;
|
||||
int i;
|
||||
int i, rc;
|
||||
TestvfsBuffer *pBuffer;
|
||||
char *zName;
|
||||
if( objc!=3 && objc!=4 ){
|
||||
|
@ -996,10 +996,16 @@ static int testvfs_obj_cmd(
|
|||
return TCL_ERROR;
|
||||
}
|
||||
zName = ckalloc(p->pParent->mxPathname);
|
||||
p->pParent->xFullPathname(
|
||||
rc = p->pParent->xFullPathname(
|
||||
p->pParent, Tcl_GetString(objv[2]),
|
||||
p->pParent->mxPathname, zName
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, "failed to get full path: ",
|
||||
Tcl_GetString(objv[2]), 0);
|
||||
ckfree(zName);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
|
||||
if( 0==strcmp(pBuffer->zFile, zName) ) break;
|
||||
}
|
||||
|
@ -1156,18 +1162,19 @@ static int testvfs_obj_cmd(
|
|||
int iValue;
|
||||
} aFlag[] = {
|
||||
{ "default", -1 },
|
||||
{ "atomic", SQLITE_IOCAP_ATOMIC },
|
||||
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
|
||||
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
|
||||
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
|
||||
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
|
||||
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
|
||||
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
|
||||
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
|
||||
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "atomic", SQLITE_IOCAP_ATOMIC },
|
||||
{ "atomic512", SQLITE_IOCAP_ATOMIC512 },
|
||||
{ "atomic1k", SQLITE_IOCAP_ATOMIC1K },
|
||||
{ "atomic2k", SQLITE_IOCAP_ATOMIC2K },
|
||||
{ "atomic4k", SQLITE_IOCAP_ATOMIC4K },
|
||||
{ "atomic8k", SQLITE_IOCAP_ATOMIC8K },
|
||||
{ "atomic16k", SQLITE_IOCAP_ATOMIC16K },
|
||||
{ "atomic32k", SQLITE_IOCAP_ATOMIC32K },
|
||||
{ "atomic64k", SQLITE_IOCAP_ATOMIC64K },
|
||||
{ "sequential", SQLITE_IOCAP_SEQUENTIAL },
|
||||
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
|
||||
{ "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
|
||||
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
Tcl_Obj *pRet;
|
||||
|
@ -1201,7 +1208,7 @@ static int testvfs_obj_cmd(
|
|||
iNew |= aFlag[idx].iValue;
|
||||
}
|
||||
|
||||
p->iDevchar = iNew;
|
||||
p->iDevchar = iNew| 0x10000000;
|
||||
}
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
|
|
|
@ -471,6 +471,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|||
}
|
||||
case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break;
|
||||
case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break;
|
||||
case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY"; break;
|
||||
case SQLITE_FCNTL_PERSIST_WAL: zOp = "PERSIST_WAL"; break;
|
||||
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
|
||||
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
|
||||
case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
|
||||
default: {
|
||||
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
|
||||
|
@ -482,6 +486,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|||
pInfo->zVfsName, p->zFName, zOp);
|
||||
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
|
||||
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
|
||||
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
|
||||
pInfo->zVfsName, *(char**)pArg);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
|||
}
|
||||
case '-': {
|
||||
if( z[1]=='-' ){
|
||||
/* IMP: R-15891-05542 -- syntax diagram for comments */
|
||||
/* IMP: R-50417-27976 -- syntax diagram for comments */
|
||||
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
|
||||
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
|
||||
return i;
|
||||
|
@ -156,7 +156,7 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
|
|||
*tokenType = TK_SLASH;
|
||||
return 1;
|
||||
}
|
||||
/* IMP: R-15891-05542 -- syntax diagram for comments */
|
||||
/* IMP: R-50417-27976 -- syntax diagram for comments */
|
||||
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
|
||||
if( c ) i++;
|
||||
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
|
||||
|
|
|
@ -904,6 +904,7 @@ static TriggerPrg *codeRowTrigger(
|
|||
}
|
||||
pProgram->nMem = pSubParse->nMem;
|
||||
pProgram->nCsr = pSubParse->nTab;
|
||||
pProgram->nOnce = pSubParse->nOnce;
|
||||
pProgram->token = (void *)pTrigger;
|
||||
pPrg->aColmask[0] = pSubParse->oldmask;
|
||||
pPrg->aColmask[1] = pSubParse->newmask;
|
||||
|
|
11
src/update.c
11
src/update.c
|
@ -126,8 +126,8 @@ void sqlite3Update(
|
|||
int regRowCount = 0; /* A count of rows changed */
|
||||
int regOldRowid; /* The old rowid */
|
||||
int regNewRowid; /* The new rowid */
|
||||
int regNew;
|
||||
int regOld = 0;
|
||||
int regNew; /* Content of the NEW.* table in triggers */
|
||||
int regOld = 0; /* Content of OLD.* table in triggers */
|
||||
int regRowSet = 0; /* Rowset of rows to be updated */
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
|
@ -276,6 +276,7 @@ void sqlite3Update(
|
|||
#endif
|
||||
|
||||
/* Allocate required registers. */
|
||||
regRowSet = ++pParse->nMem;
|
||||
regOldRowid = regNewRowid = ++pParse->nMem;
|
||||
if( pTrigger || hasFK ){
|
||||
regOld = pParse->nMem + 1;
|
||||
|
@ -310,7 +311,7 @@ void sqlite3Update(
|
|||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
|
||||
);
|
||||
|
@ -321,7 +322,6 @@ void sqlite3Update(
|
|||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
regRowSet = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
|
||||
|
@ -425,9 +425,10 @@ void sqlite3Update(
|
|||
newmask = sqlite3TriggerColmask(
|
||||
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
|
||||
);
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
||||
/*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
|
||||
}else{
|
||||
j = aXRef[i];
|
||||
if( j>=0 ){
|
||||
|
|
|
@ -1169,18 +1169,17 @@ int sqlite3AbsInt32(int x){
|
|||
** test.db-journal => test.nal
|
||||
** test.db-wal => test.wal
|
||||
** test.db-shm => test.shm
|
||||
** test.db-mj7f3319fa => test.9fa
|
||||
*/
|
||||
void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
|
||||
#if SQLITE_ENABLE_8_3_NAMES<2
|
||||
const char *zOk;
|
||||
zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
|
||||
if( zOk && sqlite3GetBoolean(zOk) )
|
||||
if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) )
|
||||
#endif
|
||||
{
|
||||
int i, sz;
|
||||
sz = sqlite3Strlen30(z);
|
||||
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
|
||||
if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4);
|
||||
if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
90
src/vdbe.c
90
src/vdbe.c
|
@ -52,7 +52,7 @@
|
|||
** not misused.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M)
|
||||
# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M)
|
||||
#else
|
||||
# define memAboutToChange(P,M)
|
||||
#endif
|
||||
|
@ -70,8 +70,8 @@ int sqlite3_search_count = 0;
|
|||
|
||||
/*
|
||||
** When this global variable is positive, it gets decremented once before
|
||||
** each instruction in the VDBE. When reaches zero, the u1.isInterrupted
|
||||
** field of the sqlite3 structure is set in order to simulate and interrupt.
|
||||
** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted
|
||||
** field of the sqlite3 structure is set in order to simulate an interrupt.
|
||||
**
|
||||
** This facility is used for testing purposes only. It does not function
|
||||
** in an ordinary build.
|
||||
|
@ -151,12 +151,6 @@ int sqlite3_found_count = 0;
|
|||
if( ((P)->flags&MEM_Ephem)!=0 \
|
||||
&& sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
|
||||
|
||||
/*
|
||||
** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
|
||||
** P if required.
|
||||
*/
|
||||
#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
|
||||
|
||||
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
|
||||
#ifdef SQLITE_OMIT_MERGE_SORT
|
||||
# define isSorter(x) 0
|
||||
|
@ -196,7 +190,7 @@ static VdbeCursor *allocateCursor(
|
|||
Vdbe *p, /* The virtual machine */
|
||||
int iCur, /* Index of the new VdbeCursor */
|
||||
int nField, /* Number of fields in the table or index */
|
||||
int iDb, /* When database the cursor belongs to, or -1 */
|
||||
int iDb, /* Database the cursor belongs to, or -1 */
|
||||
int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */
|
||||
){
|
||||
/* Find the memory cell that will be used to store the blob of memory
|
||||
|
@ -478,7 +472,7 @@ static void registerTrace(FILE *out, int iReg, Mem *p){
|
|||
**
|
||||
** This macro added to every instruction that does a jump in order to
|
||||
** implement a loop. This test used to be on every single instruction,
|
||||
** but that meant we more testing that we needed. By only testing the
|
||||
** but that meant we more testing than we needed. By only testing the
|
||||
** flag on jump instructions, we get a (small) speed improvement.
|
||||
*/
|
||||
#define CHECK_FOR_INTERRUPT \
|
||||
|
@ -673,7 +667,7 @@ int sqlite3VdbeExec(
|
|||
assert( pOp->p2<=p->nMem );
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
MemReleaseExt(pOut);
|
||||
VdbeMemRelease(pOut);
|
||||
pOut->flags = MEM_Int;
|
||||
}
|
||||
|
||||
|
@ -764,7 +758,8 @@ case OP_Goto: { /* jump */
|
|||
** Write the current address onto register P1
|
||||
** and then jump to address P2.
|
||||
*/
|
||||
case OP_Gosub: { /* jump, in1 */
|
||||
case OP_Gosub: { /* jump */
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nMem );
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( (pIn1->flags & MEM_Dyn)==0 );
|
||||
memAboutToChange(p, pIn1);
|
||||
|
@ -961,12 +956,25 @@ case OP_String: { /* out2-prerelease */
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Null * P2 * * *
|
||||
/* Opcode: Null * P2 P3 * *
|
||||
**
|
||||
** Write a NULL into register P2.
|
||||
** Write a NULL into registers P2. If P3 greater than P2, then also write
|
||||
** NULL into register P3 and ever register in between P2 and P3. If P3
|
||||
** is less than P2 (typically P3 is zero) then only register P2 is
|
||||
** set to NULL
|
||||
*/
|
||||
case OP_Null: { /* out2-prerelease */
|
||||
int cnt;
|
||||
cnt = pOp->p3-pOp->p2;
|
||||
assert( pOp->p3<=p->nMem );
|
||||
pOut->flags = MEM_Null;
|
||||
while( cnt>0 ){
|
||||
pOut++;
|
||||
memAboutToChange(p, pOut);
|
||||
VdbeMemRelease(pOut);
|
||||
pOut->flags = MEM_Null;
|
||||
cnt--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1138,7 +1146,7 @@ case OP_ResultRow: {
|
|||
|
||||
/* Make sure the results of the current row are \000 terminated
|
||||
** and have an assigned type. The results are de-ephemeralized as
|
||||
** as side effect.
|
||||
** a side effect.
|
||||
*/
|
||||
pMem = p->pResultSet = &aMem[pOp->p1];
|
||||
for(i=0; i<pOp->p2; i++){
|
||||
|
@ -2023,27 +2031,33 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
|
|||
|
||||
/* Opcode: Once P1 P2 * * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is a not null or zero. If
|
||||
** the value is NULL or zero, fall through and change the P1 register
|
||||
** to an integer 1.
|
||||
** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
|
||||
** set the flag and fall through to the next instruction.
|
||||
**
|
||||
** When P1 is not used otherwise in a program, this opcode falls through
|
||||
** once and jumps on all subsequent invocations. It is the equivalent
|
||||
** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
|
||||
** See also: JumpOnce
|
||||
*/
|
||||
case OP_Once: { /* jump */
|
||||
assert( pOp->p1<p->nOnceFlag );
|
||||
if( p->aOnceFlag[pOp->p1] ){
|
||||
pc = pOp->p2-1;
|
||||
}else{
|
||||
p->aOnceFlag[pOp->p1] = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: If P1 P2 P3 * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is true. The value
|
||||
** is considered true if it is numeric and non-zero. If the value
|
||||
** in P1 is NULL then take the jump if P3 is true.
|
||||
** in P1 is NULL then take the jump if P3 is non-zero.
|
||||
*/
|
||||
/* Opcode: IfNot P1 P2 P3 * *
|
||||
**
|
||||
** Jump to P2 if the value in register P1 is False. The value
|
||||
** is considered true if it has a numeric value of zero. If the value
|
||||
** in P1 is NULL then take the jump if P3 is true.
|
||||
** is considered false if it has a numeric value of zero. If the value
|
||||
** in P1 is NULL then take the jump if P3 is zero.
|
||||
*/
|
||||
case OP_Once: /* jump, in1 */
|
||||
case OP_If: /* jump, in1 */
|
||||
case OP_IfNot: { /* jump, in1 */
|
||||
int c;
|
||||
|
@ -2060,12 +2074,6 @@ case OP_IfNot: { /* jump, in1 */
|
|||
}
|
||||
if( c ){
|
||||
pc = pOp->p2-1;
|
||||
}else if( pOp->opcode==OP_Once ){
|
||||
assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
|
||||
memAboutToChange(p, pIn1);
|
||||
pIn1->flags = MEM_Int;
|
||||
pIn1->u.i = 1;
|
||||
REGISTER_TRACE(pOp->p1, pIn1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2361,7 +2369,7 @@ case OP_Column: {
|
|||
if( aOffset[p2] ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( zRec ){
|
||||
MemReleaseExt(pDest);
|
||||
VdbeMemRelease(pDest);
|
||||
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
|
||||
}else{
|
||||
len = sqlite3VdbeSerialTypeLen(aType[p2]);
|
||||
|
@ -4616,9 +4624,9 @@ case OP_IdxGE: { /* jump */
|
|||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp->p4.i;
|
||||
if( pOp->p5 ){
|
||||
r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID;
|
||||
r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH;
|
||||
}else{
|
||||
r.flags = UNPACKED_IGNORE_ROWID;
|
||||
r.flags = UNPACKED_PREFIX_MATCH;
|
||||
}
|
||||
r.aMem = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
|
@ -5071,7 +5079,6 @@ case OP_Program: { /* jump */
|
|||
|
||||
pProgram = pOp->p4.pProgram;
|
||||
pRt = &aMem[pOp->p3];
|
||||
assert( memIsValid(pRt) );
|
||||
assert( pProgram->nOp>0 );
|
||||
|
||||
/* If the p5 flag is clear, then recursive invocation of triggers is
|
||||
|
@ -5110,7 +5117,8 @@ case OP_Program: { /* jump */
|
|||
nMem = pProgram->nMem + pProgram->nCsr;
|
||||
nByte = ROUND8(sizeof(VdbeFrame))
|
||||
+ nMem * sizeof(Mem)
|
||||
+ pProgram->nCsr * sizeof(VdbeCursor *);
|
||||
+ pProgram->nCsr * sizeof(VdbeCursor *)
|
||||
+ pProgram->nOnce * sizeof(u8);
|
||||
pFrame = sqlite3DbMallocZero(db, nByte);
|
||||
if( !pFrame ){
|
||||
goto no_mem;
|
||||
|
@ -5130,10 +5138,12 @@ case OP_Program: { /* jump */
|
|||
pFrame->aOp = p->aOp;
|
||||
pFrame->nOp = p->nOp;
|
||||
pFrame->token = pProgram->token;
|
||||
pFrame->aOnceFlag = p->aOnceFlag;
|
||||
pFrame->nOnceFlag = p->nOnceFlag;
|
||||
|
||||
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
|
||||
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
|
||||
pMem->flags = MEM_Null;
|
||||
pMem->flags = MEM_Invalid;
|
||||
pMem->db = db;
|
||||
}
|
||||
}else{
|
||||
|
@ -5155,7 +5165,11 @@ case OP_Program: { /* jump */
|
|||
p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
|
||||
p->aOp = aOp = pProgram->aOp;
|
||||
p->nOp = pProgram->nOp;
|
||||
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
|
||||
p->nOnceFlag = pProgram->nOnce;
|
||||
p->nOp = pProgram->nOp;
|
||||
pc = -1;
|
||||
memset(p->aOnceFlag, 0, p->nOnceFlag);
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ struct SubProgram {
|
|||
int nOp; /* Elements in aOp[] */
|
||||
int nMem; /* Number of memory cells required */
|
||||
int nCsr; /* Number of cursors required */
|
||||
int nOnce; /* Number of OP_Once instructions */
|
||||
void *token; /* id that may be used to recursive triggers */
|
||||
SubProgram *pNext; /* Next sub-program already visited */
|
||||
};
|
||||
|
|
|
@ -33,6 +33,9 @@ typedef unsigned char Bool;
|
|||
/* Opaque type used by code in vdbesort.c */
|
||||
typedef struct VdbeSorter VdbeSorter;
|
||||
|
||||
/* Opaque type used by the explainer */
|
||||
typedef struct Explain Explain;
|
||||
|
||||
/*
|
||||
** A cursor is a pointer into a single BTree within a database file.
|
||||
** The cursor can seek to a BTree entry with a particular key, or
|
||||
|
@ -117,6 +120,8 @@ struct VdbeFrame {
|
|||
int nOp; /* Size of aOp array */
|
||||
Mem *aMem; /* Array of memory cells for parent frame */
|
||||
int nMem; /* Number of entries in aMem */
|
||||
u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
|
||||
int nOnceFlag; /* Number of entries in aOnceFlag */
|
||||
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
|
||||
u16 nCursor; /* Number of entries in apCsr */
|
||||
void *token; /* Copy of SubProgram.token */
|
||||
|
@ -255,6 +260,18 @@ struct sqlite3_context {
|
|||
CollSeq *pColl; /* Collating sequence */
|
||||
};
|
||||
|
||||
/*
|
||||
** An Explain object accumulates indented output which is helpful
|
||||
** in describing recursive data structures.
|
||||
*/
|
||||
struct Explain {
|
||||
Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
|
||||
StrAccum str; /* The string being accumulated */
|
||||
int nIndent; /* Number of elements in aIndent */
|
||||
u16 aIndent[100]; /* Levels of indentation */
|
||||
char zBase[100]; /* Initial space */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the virtual machine. This structure contains the complete
|
||||
** state of the virtual machine.
|
||||
|
@ -320,12 +337,18 @@ struct Vdbe {
|
|||
void *pFree; /* Free this when deleting the vdbe */
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_TREE_EXPLAIN
|
||||
Explain *pExplain; /* The explainer */
|
||||
char *zExplain; /* Explanation of data structures */
|
||||
#endif
|
||||
VdbeFrame *pFrame; /* Parent frame */
|
||||
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
|
||||
int nFrame; /* Number of frames in pFrame list */
|
||||
u32 expmask; /* Binding to these vars invalidates VM */
|
||||
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
|
||||
int nOnceFlag; /* Size of array aOnceFlag[] */
|
||||
u8 *aOnceFlag; /* Flags for OP_Once */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -385,7 +408,7 @@ int sqlite3VdbeMemNumerify(Mem*);
|
|||
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
|
||||
void sqlite3VdbeMemRelease(Mem *p);
|
||||
void sqlite3VdbeMemReleaseExternal(Mem *p);
|
||||
#define MemReleaseExt(X) \
|
||||
#define VdbeMemRelease(X) \
|
||||
if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
|
||||
sqlite3VdbeMemReleaseExternal(X);
|
||||
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
|
@ -424,7 +447,7 @@ int sqlite3VdbeSorterCompare(VdbeCursor *, Mem *, int *);
|
|||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
|
||||
void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
|
@ -442,8 +465,10 @@ int sqlite3VdbeMemHandleBom(Mem *pMem);
|
|||
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
int sqlite3VdbeMemExpandBlob(Mem *);
|
||||
#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
|
||||
#else
|
||||
#define sqlite3VdbeMemExpandBlob(x) SQLITE_OK
|
||||
#define ExpandBlob(P) SQLITE_OK
|
||||
#endif
|
||||
|
||||
#endif /* !defined(_VDBEINT_H_) */
|
||||
|
|
|
@ -354,7 +354,7 @@ static int sqlite3Step(Vdbe *p){
|
|||
**
|
||||
** Nevertheless, some published applications that were originally written
|
||||
** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
|
||||
** returns, and the so were broken by the automatic-reset change. As a
|
||||
** returns, and those were broken by the automatic-reset change. As a
|
||||
** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
|
||||
** legacy behavior of returning SQLITE_MISUSE for cases where the
|
||||
** previous sqlite3_step() returned something other than a SQLITE_LOCKED
|
||||
|
@ -700,13 +700,13 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
|
|||
/* If the value passed as the second argument is out of range, return
|
||||
** a pointer to the following static Mem object which contains the
|
||||
** value SQL NULL. Even though the Mem structure contains an element
|
||||
** of type i64, on certain architecture (x86) with certain compiler
|
||||
** of type i64, on certain architectures (x86) with certain compiler
|
||||
** switches (-Os), gcc may align this Mem object on a 4-byte boundary
|
||||
** instead of an 8-byte one. This all works fine, except that when
|
||||
** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
|
||||
** that a Mem structure is located on an 8-byte boundary. To prevent
|
||||
** this assert() from failing, when building with SQLITE_DEBUG defined
|
||||
** using gcc, force nullMem to be 8-byte aligned using the magical
|
||||
** these assert()s from failing, when building with SQLITE_DEBUG defined
|
||||
** using gcc, we force nullMem to be 8-byte aligned using the magical
|
||||
** __attribute__((aligned(8))) macro. */
|
||||
static const Mem nullMem
|
||||
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
|
||||
|
@ -1277,6 +1277,14 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
|
|||
return pStmt ? ((Vdbe*)pStmt)->readOnly : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the prepared statement is in need of being reset.
|
||||
*/
|
||||
int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
return v!=0 && v->pc>0 && v->magic==VDBE_MAGIC_RUN;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a pointer to the next prepared statement after pStmt associated
|
||||
** with database connection pDb. If pStmt is NULL, return the first
|
||||
|
|
|
@ -196,7 +196,8 @@ int sqlite3VdbeAddOp4(
|
|||
|
||||
/*
|
||||
** Add an OP_ParseSchema opcode. This routine is broken out from
|
||||
** sqlite3VdbeAddOp4() since it needs to also local all btrees.
|
||||
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
|
||||
** as having been used.
|
||||
**
|
||||
** The zWhere string must have been obtained from sqlite3_malloc().
|
||||
** This routine will take ownership of the allocated memory.
|
||||
|
@ -913,13 +914,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
|||
}
|
||||
case P4_MEM: {
|
||||
Mem *pMem = pOp->p4.pMem;
|
||||
assert( (pMem->flags & MEM_Null)==0 );
|
||||
if( pMem->flags & MEM_Str ){
|
||||
zP4 = pMem->z;
|
||||
}else if( pMem->flags & MEM_Int ){
|
||||
sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
|
||||
}else if( pMem->flags & MEM_Real ){
|
||||
sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
|
||||
}else if( pMem->flags & MEM_Null ){
|
||||
sqlite3_snprintf(nTemp, zTemp, "NULL");
|
||||
}else{
|
||||
assert( pMem->flags & MEM_Blob );
|
||||
zP4 = "(blob)";
|
||||
|
@ -962,8 +964,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
|||
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
|
||||
**
|
||||
** The prepared statements need to know in advance the complete set of
|
||||
** attached databases that they will be using. A mask of these databases
|
||||
** is maintained in p->btreeMask and is used for locking and other purposes.
|
||||
** attached databases that will be use. A mask of these databases
|
||||
** is maintained in p->btreeMask. The p->lockMask value is the subset of
|
||||
** p->btreeMask of databases that will require a lock.
|
||||
*/
|
||||
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
|
||||
assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 );
|
||||
|
@ -1094,7 +1097,7 @@ static void releaseMemArray(Mem *p, int N){
|
|||
p->zMalloc = 0;
|
||||
}
|
||||
|
||||
p->flags = MEM_Null;
|
||||
p->flags = MEM_Invalid;
|
||||
}
|
||||
db->mallocFailed = malloc_failed;
|
||||
}
|
||||
|
@ -1469,6 +1472,7 @@ void sqlite3VdbeMakeReady(
|
|||
int nMem; /* Number of VM memory registers */
|
||||
int nCursor; /* Number of cursors required */
|
||||
int nArg; /* Number of arguments in subprograms */
|
||||
int nOnce; /* Number of OP_Once instructions */
|
||||
int n; /* Loop counter */
|
||||
u8 *zCsr; /* Memory available for allocation */
|
||||
u8 *zEnd; /* First byte past allocated memory */
|
||||
|
@ -1484,6 +1488,8 @@ void sqlite3VdbeMakeReady(
|
|||
nMem = pParse->nMem;
|
||||
nCursor = pParse->nTab;
|
||||
nArg = pParse->nMaxArg;
|
||||
nOnce = pParse->nOnce;
|
||||
if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */
|
||||
|
||||
/* For each cursor required, also allocate a memory cell. Memory
|
||||
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
|
||||
|
@ -1530,6 +1536,7 @@ void sqlite3VdbeMakeReady(
|
|||
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
|
||||
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
|
||||
&zCsr, zEnd, &nByte);
|
||||
p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
|
||||
if( nByte ){
|
||||
p->pFree = sqlite3DbMallocZero(db, nByte);
|
||||
}
|
||||
|
@ -1538,6 +1545,7 @@ void sqlite3VdbeMakeReady(
|
|||
}while( nByte && !db->mallocFailed );
|
||||
|
||||
p->nCursor = (u16)nCursor;
|
||||
p->nOnceFlag = nOnce;
|
||||
if( p->aVar ){
|
||||
p->nVar = (ynVar)nVar;
|
||||
for(n=0; n<nVar; n++){
|
||||
|
@ -1554,7 +1562,7 @@ void sqlite3VdbeMakeReady(
|
|||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
for(n=1; n<=nMem; n++){
|
||||
p->aMem[n].flags = MEM_Null;
|
||||
p->aMem[n].flags = MEM_Invalid;
|
||||
p->aMem[n].db = db;
|
||||
}
|
||||
}
|
||||
|
@ -1596,6 +1604,8 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
|
|||
*/
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
|
||||
Vdbe *v = pFrame->v;
|
||||
v->aOnceFlag = pFrame->aOnceFlag;
|
||||
v->nOnceFlag = pFrame->nOnceFlag;
|
||||
v->aOp = pFrame->aOp;
|
||||
v->nOp = pFrame->nOp;
|
||||
v->aMem = pFrame->aMem;
|
||||
|
@ -1658,8 +1668,10 @@ static void Cleanup(Vdbe *p){
|
|||
/* Execute assert() statements to ensure that the Vdbe.apCsr[] and
|
||||
** Vdbe.aMem[] arrays have already been cleaned up. */
|
||||
int i;
|
||||
for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
|
||||
for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
|
||||
if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
|
||||
if( p->aMem ){
|
||||
for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
|
@ -1824,16 +1836,31 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
|
|||
sqlite3_file *pMaster = 0;
|
||||
i64 offset = 0;
|
||||
int res;
|
||||
int retryCount = 0;
|
||||
int nMainFile;
|
||||
|
||||
/* Select a master journal file name */
|
||||
nMainFile = sqlite3Strlen30(zMainFile);
|
||||
zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile);
|
||||
if( zMaster==0 ) return SQLITE_NOMEM;
|
||||
do {
|
||||
u32 iRandom;
|
||||
sqlite3DbFree(db, zMaster);
|
||||
sqlite3_randomness(sizeof(iRandom), &iRandom);
|
||||
zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, iRandom&0x7fffffff);
|
||||
if( !zMaster ){
|
||||
return SQLITE_NOMEM;
|
||||
if( retryCount ){
|
||||
if( retryCount>100 ){
|
||||
sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
|
||||
sqlite3OsDelete(pVfs, zMaster, 0);
|
||||
break;
|
||||
}else if( retryCount==1 ){
|
||||
sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
|
||||
}
|
||||
}
|
||||
retryCount++;
|
||||
sqlite3_randomness(sizeof(iRandom), &iRandom);
|
||||
sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
|
||||
(iRandom>>8)&0xffffff, iRandom&0xff);
|
||||
/* The antipenultimate character of the master journal name must
|
||||
** be "9" to avoid name collisions when using 8+3 filenames. */
|
||||
assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' );
|
||||
sqlite3FileSuffix3(zMainFile, zMaster);
|
||||
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
|
||||
}while( rc==SQLITE_OK && res );
|
||||
|
@ -2127,6 +2154,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
|||
if( p->db->mallocFailed ){
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
|
||||
closeAllCursors(p);
|
||||
if( p->magic!=VDBE_MAGIC_RUN ){
|
||||
return SQLITE_OK;
|
||||
|
@ -2464,6 +2492,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
|
|||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
sqlite3DbFree(db, p->zExplain);
|
||||
sqlite3DbFree(db, p->pExplain);
|
||||
#endif
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
|
@ -2943,15 +2975,6 @@ void sqlite3VdbeRecordUnpack(
|
|||
** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
|
||||
** equal, then the keys are considered to be equal and
|
||||
** the parts beyond the common prefix are ignored.
|
||||
**
|
||||
** If the UNPACKED_IGNORE_ROWID flag is set, then the last byte of
|
||||
** the header of pKey1 is ignored. It is assumed that pKey1 is
|
||||
** an index key, and thus ends with a rowid value. The last byte
|
||||
** of the header will therefore be the serial type of the rowid:
|
||||
** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types.
|
||||
** The serial type of the final rowid will always be a single byte.
|
||||
** By ignoring this last byte of the header, we force the comparison
|
||||
** to ignore the rowid at the end of key1.
|
||||
*/
|
||||
int sqlite3VdbeRecordCompare(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
|
@ -2984,9 +3007,6 @@ int sqlite3VdbeRecordCompare(
|
|||
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
d1 = szHdr1;
|
||||
if( pPKey2->flags & UNPACKED_IGNORE_ROWID ){
|
||||
szHdr1--;
|
||||
}
|
||||
nField = pKeyInfo->nField;
|
||||
while( idx1<szHdr1 && i<pPKey2->nField ){
|
||||
u32 serial_type1;
|
||||
|
@ -3166,7 +3186,7 @@ int sqlite3VdbeIdxKeyCompare(
|
|||
if( rc ){
|
||||
return rc;
|
||||
}
|
||||
assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID );
|
||||
assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH );
|
||||
*res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
|
||||
sqlite3VdbeMemRelease(&m);
|
||||
return SQLITE_OK;
|
||||
|
|
|
@ -18,12 +18,6 @@
|
|||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
/*
|
||||
** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
|
||||
** P if required.
|
||||
*/
|
||||
#define expandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
|
||||
|
||||
/*
|
||||
** If pMem is an object with a valid string representation, this routine
|
||||
** ensures the internal encoding for the string representation is
|
||||
|
@ -123,7 +117,7 @@ int sqlite3VdbeMemMakeWriteable(Mem *pMem){
|
|||
int f;
|
||||
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
|
||||
assert( (pMem->flags&MEM_RowSet)==0 );
|
||||
expandBlob(pMem);
|
||||
ExpandBlob(pMem);
|
||||
f = pMem->flags;
|
||||
if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){
|
||||
if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
|
||||
|
@ -292,7 +286,7 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
|
|||
** (Mem.type==SQLITE_TEXT).
|
||||
*/
|
||||
void sqlite3VdbeMemRelease(Mem *p){
|
||||
MemReleaseExt(p);
|
||||
VdbeMemRelease(p);
|
||||
sqlite3DbFree(p->db, p->zMalloc);
|
||||
p->z = 0;
|
||||
p->zMalloc = 0;
|
||||
|
@ -588,7 +582,7 @@ int sqlite3VdbeMemTooBig(Mem *p){
|
|||
** This is used for testing and debugging only - to make sure shallow
|
||||
** copies are not misused.
|
||||
*/
|
||||
void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
|
||||
int i;
|
||||
Mem *pX;
|
||||
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
|
||||
|
@ -614,7 +608,7 @@ void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
|
|||
*/
|
||||
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
MemReleaseExt(pTo);
|
||||
VdbeMemRelease(pTo);
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
pTo->xDel = 0;
|
||||
if( (pFrom->flags&MEM_Static)==0 ){
|
||||
|
@ -632,7 +626,7 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
|
|||
int rc = SQLITE_OK;
|
||||
|
||||
assert( (pFrom->flags & MEM_RowSet)==0 );
|
||||
MemReleaseExt(pTo);
|
||||
VdbeMemRelease(pTo);
|
||||
memcpy(pTo, pFrom, MEMCELLSIZE);
|
||||
pTo->flags &= ~MEM_Dyn;
|
||||
|
||||
|
@ -960,7 +954,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
|
|||
}
|
||||
assert( (MEM_Blob>>3) == MEM_Str );
|
||||
pVal->flags |= (pVal->flags & MEM_Blob)>>3;
|
||||
expandBlob(pVal);
|
||||
ExpandBlob(pVal);
|
||||
if( pVal->flags&MEM_Str ){
|
||||
sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
|
||||
if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){
|
||||
|
@ -969,7 +963,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */
|
||||
sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */
|
||||
}else{
|
||||
assert( (pVal->flags&MEM_Blob)==0 );
|
||||
sqlite3VdbeMemStringify(pVal, enc);
|
||||
|
|
117
src/vdbetrace.c
117
src/vdbetrace.c
|
@ -12,6 +12,8 @@
|
|||
**
|
||||
** This file contains code used to insert the values of host parameters
|
||||
** (aka "wildcards") into the SQL text output by sqlite3_trace().
|
||||
**
|
||||
** The Vdbe parse-tree explainer is also found here.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
@ -152,3 +154,118 @@ char *sqlite3VdbeExpandSql(
|
|||
}
|
||||
|
||||
#endif /* #ifndef SQLITE_OMIT_TRACE */
|
||||
|
||||
/*****************************************************************************
|
||||
** The following code implements the data-structure explaining logic
|
||||
** for the Vdbe.
|
||||
*/
|
||||
|
||||
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
|
||||
|
||||
/*
|
||||
** Allocate a new Explain object
|
||||
*/
|
||||
void sqlite3ExplainBegin(Vdbe *pVdbe){
|
||||
if( pVdbe ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
Explain *p = sqlite3_malloc( sizeof(Explain) );
|
||||
if( p ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->pVdbe = pVdbe;
|
||||
sqlite3_free(pVdbe->pExplain);
|
||||
pVdbe->pExplain = p;
|
||||
sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase),
|
||||
SQLITE_MAX_LENGTH);
|
||||
p->str.useMalloc = 2;
|
||||
}else{
|
||||
sqlite3EndBenignMalloc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if the Explain ends with a new-line.
|
||||
*/
|
||||
static int endsWithNL(Explain *p){
|
||||
return p && p->str.zText && p->str.nChar
|
||||
&& p->str.zText[p->str.nChar-1]=='\n';
|
||||
}
|
||||
|
||||
/*
|
||||
** Append text to the indentation
|
||||
*/
|
||||
void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 ){
|
||||
va_list ap;
|
||||
if( p->nIndent && endsWithNL(p) ){
|
||||
int n = p->nIndent;
|
||||
if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent);
|
||||
sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
|
||||
}
|
||||
va_start(ap, zFormat);
|
||||
sqlite3VXPrintf(&p->str, 1, zFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Append a '\n' if there is not already one.
|
||||
*/
|
||||
void sqlite3ExplainNL(Vdbe *pVdbe){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){
|
||||
sqlite3StrAccumAppend(&p->str, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Push a new indentation level. Subsequent lines will be indented
|
||||
** so that they begin at the current cursor position.
|
||||
*/
|
||||
void sqlite3ExplainPush(Vdbe *pVdbe){
|
||||
Explain *p;
|
||||
if( pVdbe && (p = pVdbe->pExplain)!=0 ){
|
||||
if( p->str.zText && p->nIndent<ArraySize(p->aIndent) ){
|
||||
const char *z = p->str.zText;
|
||||
int i = p->str.nChar-1;
|
||||
int x;
|
||||
while( i>=0 && z[i]!='\n' ){ i--; }
|
||||
x = (p->str.nChar - 1) - i;
|
||||
if( p->nIndent && x<p->aIndent[p->nIndent-1] ){
|
||||
x = p->aIndent[p->nIndent-1];
|
||||
}
|
||||
p->aIndent[p->nIndent] = x;
|
||||
}
|
||||
p->nIndent++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the indentation stack by one level.
|
||||
*/
|
||||
void sqlite3ExplainPop(Vdbe *p){
|
||||
if( p && p->pExplain ) p->pExplain->nIndent--;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free the indentation structure
|
||||
*/
|
||||
void sqlite3ExplainFinish(Vdbe *pVdbe){
|
||||
if( pVdbe && pVdbe->pExplain ){
|
||||
sqlite3_free(pVdbe->zExplain);
|
||||
sqlite3ExplainNL(pVdbe);
|
||||
pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str);
|
||||
sqlite3_free(pVdbe->pExplain);
|
||||
pVdbe->pExplain = 0;
|
||||
sqlite3EndBenignMalloc();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the explanation of a virtual machine.
|
||||
*/
|
||||
const char *sqlite3VdbeExplanation(Vdbe *pVdbe){
|
||||
return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_DEBUG) */
|
||||
|
|
289
src/wal.c
289
src/wal.c
|
@ -414,13 +414,18 @@ struct Wal {
|
|||
u32 iCallback; /* Value to pass to log callback (or 0) */
|
||||
i64 mxWalSize; /* Truncate WAL to this size upon reset */
|
||||
int nWiData; /* Size of array apWiData */
|
||||
int szFirstBlock; /* Size of first block written to WAL file */
|
||||
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
|
||||
u32 szPage; /* Database page size */
|
||||
i16 readLock; /* Which read lock is being held. -1 for none */
|
||||
u8 syncFlags; /* Flags to use to sync header writes */
|
||||
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
|
||||
u8 writeLock; /* True if in a write transaction */
|
||||
u8 ckptLock; /* True if holding a checkpoint lock */
|
||||
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
|
||||
u8 truncateOnCommit; /* True to truncate WAL file on commit */
|
||||
u8 syncHeader; /* Fsync the WAL header if true */
|
||||
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
|
||||
WalIndexHdr hdr; /* Wal-index header for current transaction */
|
||||
const char *zWalName; /* Name of WAL file */
|
||||
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
|
||||
|
@ -1093,6 +1098,7 @@ static int walIndexRecover(Wal *pWal){
|
|||
int szPage; /* Page size according to the log */
|
||||
u32 magic; /* Magic value read from WAL header */
|
||||
u32 version; /* Magic value read from WAL header */
|
||||
int isValid; /* True if this frame is valid */
|
||||
|
||||
/* Read in the WAL header. */
|
||||
rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
|
||||
|
@ -1151,14 +1157,14 @@ static int walIndexRecover(Wal *pWal){
|
|||
for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
|
||||
u32 pgno; /* Database page number for frame */
|
||||
u32 nTruncate; /* dbsize field from frame header */
|
||||
int isValid; /* True if this frame is valid */
|
||||
|
||||
/* Read and decode the next log frame. */
|
||||
iFrame++;
|
||||
rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
|
||||
if( !isValid ) break;
|
||||
rc = walIndexAppend(pWal, ++iFrame, pgno);
|
||||
rc = walIndexAppend(pWal, iFrame, pgno);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
/* If nTruncate is non-zero, this is a commit record. */
|
||||
|
@ -1281,6 +1287,8 @@ int sqlite3WalOpen(
|
|||
pRet->readLock = -1;
|
||||
pRet->mxWalSize = mxWalSize;
|
||||
pRet->zWalName = zWalName;
|
||||
pRet->syncHeader = 1;
|
||||
pRet->padToSectorBoundary = 1;
|
||||
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
|
||||
|
||||
/* Open file handle on the write-ahead log file. */
|
||||
|
@ -1295,6 +1303,11 @@ int sqlite3WalOpen(
|
|||
sqlite3OsClose(pRet->pWalFd);
|
||||
sqlite3_free(pRet);
|
||||
}else{
|
||||
int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
|
||||
if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
|
||||
if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
|
||||
pRet->padToSectorBoundary = 0;
|
||||
}
|
||||
*ppWal = pRet;
|
||||
WALTRACE(("WAL%d: opened\n", pRet));
|
||||
}
|
||||
|
@ -1714,7 +1727,7 @@ static int walCheckpoint(
|
|||
i64 nReq = ((i64)mxPage * szPage);
|
||||
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
|
||||
if( rc==SQLITE_OK && nSize<nReq ){
|
||||
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
|
||||
sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1781,6 +1794,24 @@ static int walCheckpoint(
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the WAL file is currently larger than nMax bytes in size, truncate
|
||||
** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
|
||||
*/
|
||||
static void walLimitSize(Wal *pWal, i64 nMax){
|
||||
i64 sz;
|
||||
int rx;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
|
||||
if( rx==SQLITE_OK && (sz > nMax ) ){
|
||||
rx = sqlite3OsTruncate(pWal->pWalFd, nMax);
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rx ){
|
||||
sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a connection to a log file.
|
||||
*/
|
||||
|
@ -1804,23 +1835,40 @@ int sqlite3WalClose(
|
|||
*/
|
||||
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
|
||||
if( rc==SQLITE_OK ){
|
||||
int bPersistWal = -1;
|
||||
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
|
||||
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
||||
}
|
||||
rc = sqlite3WalCheckpoint(
|
||||
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
|
||||
);
|
||||
sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
|
||||
if( rc==SQLITE_OK && bPersistWal!=1 ){
|
||||
isDelete = 1;
|
||||
if( rc==SQLITE_OK ){
|
||||
int bPersist = -1;
|
||||
sqlite3OsFileControlHint(
|
||||
pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist
|
||||
);
|
||||
if( bPersist!=1 ){
|
||||
/* Try to delete the WAL file if the checkpoint completed and
|
||||
** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
|
||||
** mode (!bPersist) */
|
||||
isDelete = 1;
|
||||
}else if( pWal->mxWalSize>=0 ){
|
||||
/* Try to truncate the WAL file to zero bytes if the checkpoint
|
||||
** completed and fsynced (rc==SQLITE_OK) and we are in persistent
|
||||
** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
|
||||
** non-negative value (pWal->mxWalSize>=0). Note that we truncate
|
||||
** to zero bytes as truncating to the journal_size_limit might
|
||||
** leave a corrupt WAL file on disk. */
|
||||
walLimitSize(pWal, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walIndexClose(pWal, isDelete);
|
||||
sqlite3OsClose(pWal->pWalFd);
|
||||
if( isDelete ){
|
||||
sqlite3BeginBenignMalloc();
|
||||
sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
|
||||
sqlite3EndBenignMalloc();
|
||||
}
|
||||
WALTRACE(("WAL%p: closed\n", pWal));
|
||||
sqlite3_free((void *)pWal->apWiData);
|
||||
|
@ -2310,7 +2358,7 @@ int sqlite3WalRead(
|
|||
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
|
||||
u32 iFrame = aHash[iKey] + iZero;
|
||||
if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
|
||||
assert( iFrame>iRead );
|
||||
/* assert( iFrame>iRead ); -- not true if there is corruption */
|
||||
iRead = iFrame;
|
||||
}
|
||||
if( (nCollide--)==0 ){
|
||||
|
@ -2422,6 +2470,7 @@ int sqlite3WalEndWriteTransaction(Wal *pWal){
|
|||
if( pWal->writeLock ){
|
||||
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
|
||||
pWal->writeLock = 0;
|
||||
pWal->truncateOnCommit = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -2518,6 +2567,7 @@ int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
|
|||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called just before writing a set of frames to the log
|
||||
** file (see sqlite3WalFrames()). It checks to see if, instead of appending
|
||||
|
@ -2555,23 +2605,6 @@ static int walRestartLog(Wal *pWal){
|
|||
int i; /* Loop counter */
|
||||
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
|
||||
|
||||
/* Limit the size of WAL file if the journal_size_limit PRAGMA is
|
||||
** set to a non-negative value. Log errors encountered
|
||||
** during the truncation attempt. */
|
||||
if( pWal->mxWalSize>=0 ){
|
||||
i64 sz;
|
||||
int rx;
|
||||
sqlite3BeginBenignMalloc();
|
||||
rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
|
||||
if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
|
||||
rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
|
||||
}
|
||||
sqlite3EndBenignMalloc();
|
||||
if( rx ){
|
||||
sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
|
||||
}
|
||||
}
|
||||
|
||||
pWal->nCkpt++;
|
||||
pWal->hdr.mxFrame = 0;
|
||||
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
|
||||
|
@ -2600,6 +2633,74 @@ static int walRestartLog(Wal *pWal){
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Information about the current state of the WAL file and where
|
||||
** the next fsync should occur - passed from sqlite3WalFrames() into
|
||||
** walWriteToLog().
|
||||
*/
|
||||
typedef struct WalWriter {
|
||||
Wal *pWal; /* The complete WAL information */
|
||||
sqlite3_file *pFd; /* The WAL file to which we write */
|
||||
sqlite3_int64 iSyncPoint; /* Fsync at this offset */
|
||||
int syncFlags; /* Flags for the fsync */
|
||||
int szPage; /* Size of one page */
|
||||
} WalWriter;
|
||||
|
||||
/*
|
||||
** Write iAmt bytes of content into the WAL file beginning at iOffset.
|
||||
** Do a sync when crossing the p->iSyncPoint boundary.
|
||||
**
|
||||
** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
|
||||
** first write the part before iSyncPoint, then sync, then write the
|
||||
** rest.
|
||||
*/
|
||||
static int walWriteToLog(
|
||||
WalWriter *p, /* WAL to write to */
|
||||
void *pContent, /* Content to be written */
|
||||
int iAmt, /* Number of bytes to write */
|
||||
sqlite3_int64 iOffset /* Start writing at this offset */
|
||||
){
|
||||
int rc;
|
||||
if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
|
||||
int iFirstAmt = (int)(p->iSyncPoint - iOffset);
|
||||
rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
|
||||
if( rc ) return rc;
|
||||
iOffset += iFirstAmt;
|
||||
iAmt -= iFirstAmt;
|
||||
pContent = (void*)(iFirstAmt + (char*)pContent);
|
||||
assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
|
||||
rc = sqlite3OsSync(p->pFd, p->syncFlags);
|
||||
if( iAmt==0 || rc ) return rc;
|
||||
}
|
||||
rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write out a single frame of the WAL
|
||||
*/
|
||||
static int walWriteOneFrame(
|
||||
WalWriter *p, /* Where to write the frame */
|
||||
PgHdr *pPage, /* The page of the frame to be written */
|
||||
int nTruncate, /* The commit flag. Usually 0. >0 for commit */
|
||||
sqlite3_int64 iOffset /* Byte offset at which to write */
|
||||
){
|
||||
int rc; /* Result code from subfunctions */
|
||||
void *pData; /* Data actually written */
|
||||
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
|
||||
#else
|
||||
pData = pPage->pData;
|
||||
#endif
|
||||
walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
|
||||
rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
|
||||
if( rc ) return rc;
|
||||
/* Write the page data */
|
||||
rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a set of frames to the log. The caller must hold the write-lock
|
||||
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
|
||||
|
@ -2614,14 +2715,20 @@ int sqlite3WalFrames(
|
|||
){
|
||||
int rc; /* Used to catch return codes */
|
||||
u32 iFrame; /* Next frame address */
|
||||
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
|
||||
PgHdr *p; /* Iterator to run through pList with. */
|
||||
PgHdr *pLast = 0; /* Last frame in list */
|
||||
int nLast = 0; /* Number of extra copies of last page */
|
||||
int nExtra = 0; /* Number of extra copies of last page */
|
||||
int szFrame; /* The size of a single frame */
|
||||
i64 iOffset; /* Next byte to write in WAL file */
|
||||
WalWriter w; /* The writer */
|
||||
|
||||
assert( pList );
|
||||
assert( pWal->writeLock );
|
||||
|
||||
/* If this frame set completes a transaction, then nTruncate>0. If
|
||||
** nTruncate==0 then this frame set does not complete the transaction. */
|
||||
assert( (isCommit!=0)==(nTruncate!=0) );
|
||||
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
||||
{ int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
|
||||
WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
|
||||
|
@ -2649,7 +2756,7 @@ int sqlite3WalFrames(
|
|||
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
|
||||
sqlite3Put4byte(&aWalHdr[8], szPage);
|
||||
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
|
||||
sqlite3_randomness(8, pWal->hdr.aSalt);
|
||||
if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
|
||||
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
|
||||
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
|
||||
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
|
||||
|
@ -2659,77 +2766,89 @@ int sqlite3WalFrames(
|
|||
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
|
||||
pWal->hdr.aFrameCksum[0] = aCksum[0];
|
||||
pWal->hdr.aFrameCksum[1] = aCksum[1];
|
||||
pWal->truncateOnCommit = 1;
|
||||
|
||||
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
|
||||
WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
|
||||
** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
|
||||
** an out-of-order write following a WAL restart could result in
|
||||
** database corruption. See the ticket:
|
||||
**
|
||||
** http://localhost:591/sqlite/info/ff5be73dee
|
||||
*/
|
||||
if( pWal->syncHeader && sync_flags ){
|
||||
rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}
|
||||
assert( (int)pWal->szPage==szPage );
|
||||
|
||||
/* Write the log file. */
|
||||
for(p=pList; p; p=p->pDirty){
|
||||
u32 nDbsize; /* Db-size field for frame header */
|
||||
i64 iOffset; /* Write offset in log file */
|
||||
void *pData;
|
||||
|
||||
iOffset = walFrameOffset(++iFrame, szPage);
|
||||
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
|
||||
|
||||
/* Populate and write the frame header */
|
||||
nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0;
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
|
||||
#else
|
||||
pData = p->pData;
|
||||
#endif
|
||||
walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame);
|
||||
rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
/* Setup information needed to write frames into the WAL */
|
||||
w.pWal = pWal;
|
||||
w.pFd = pWal->pWalFd;
|
||||
w.iSyncPoint = 0;
|
||||
w.syncFlags = sync_flags;
|
||||
w.szPage = szPage;
|
||||
iOffset = walFrameOffset(iFrame+1, szPage);
|
||||
szFrame = szPage + WAL_FRAME_HDRSIZE;
|
||||
|
||||
/* Write the page data */
|
||||
rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame));
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
/* Write all frames into the log file exactly once */
|
||||
for(p=pList; p; p=p->pDirty){
|
||||
int nDbSize; /* 0 normally. Positive == commit flag */
|
||||
iFrame++;
|
||||
assert( iOffset==walFrameOffset(iFrame, szPage) );
|
||||
nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
|
||||
rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
|
||||
if( rc ) return rc;
|
||||
pLast = p;
|
||||
iOffset += szFrame;
|
||||
}
|
||||
|
||||
/* Sync the log file if the 'isSync' flag was specified. */
|
||||
if( sync_flags ){
|
||||
i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd);
|
||||
i64 iOffset = walFrameOffset(iFrame+1, szPage);
|
||||
|
||||
assert( isCommit );
|
||||
assert( iSegment>0 );
|
||||
|
||||
iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment);
|
||||
while( iOffset<iSegment ){
|
||||
void *pData;
|
||||
#if defined(SQLITE_HAS_CODEC)
|
||||
if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
|
||||
#else
|
||||
pData = pLast->pData;
|
||||
#endif
|
||||
walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
|
||||
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
|
||||
rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
/* If this is the end of a transaction, then we might need to pad
|
||||
** the transaction and/or sync the WAL file.
|
||||
**
|
||||
** Padding and syncing only occur if this set of frames complete a
|
||||
** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
|
||||
** or synchonous==OFF, then no padding or syncing are needed.
|
||||
**
|
||||
** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
|
||||
** needed and only the sync is done. If padding is needed, then the
|
||||
** final frame is repeated (with its commit mark) until the next sector
|
||||
** boundary is crossed. Only the part of the WAL prior to the last
|
||||
** sector boundary is synced; the part of the last frame that extends
|
||||
** past the sector boundary is written after the sync.
|
||||
*/
|
||||
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
|
||||
if( pWal->padToSectorBoundary ){
|
||||
int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
|
||||
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
|
||||
while( iOffset<w.iSyncPoint ){
|
||||
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
|
||||
if( rc ) return rc;
|
||||
iOffset += szFrame;
|
||||
nExtra++;
|
||||
}
|
||||
iOffset += WAL_FRAME_HDRSIZE;
|
||||
rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
nLast++;
|
||||
iOffset += szPage;
|
||||
}else{
|
||||
rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
|
||||
/* If this frame set completes the first transaction in the WAL and
|
||||
** if PRAGMA journal_size_limit is set, then truncate the WAL to the
|
||||
** journal size limit, if possible.
|
||||
*/
|
||||
if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
|
||||
i64 sz = pWal->mxWalSize;
|
||||
if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
|
||||
sz = walFrameOffset(iFrame+nExtra+1, szPage);
|
||||
}
|
||||
walLimitSize(pWal, sz);
|
||||
pWal->truncateOnCommit = 0;
|
||||
}
|
||||
|
||||
/* Append data to the wal-index. It is not necessary to lock the
|
||||
|
@ -2742,9 +2861,9 @@ int sqlite3WalFrames(
|
|||
iFrame++;
|
||||
rc = walIndexAppend(pWal, iFrame, p->pgno);
|
||||
}
|
||||
while( nLast>0 && rc==SQLITE_OK ){
|
||||
while( rc==SQLITE_OK && nExtra>0 ){
|
||||
iFrame++;
|
||||
nLast--;
|
||||
nExtra--;
|
||||
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/* Additional values that can be added to the sync_flags argument of
|
||||
** sqlite3WalFrames():
|
||||
*/
|
||||
#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
|
||||
#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
|
||||
|
||||
#ifdef SQLITE_OMIT_WAL
|
||||
# define sqlite3WalOpen(x,y,z) 0
|
||||
# define sqlite3WalLimit(x,y)
|
||||
|
|
46
src/where.c
46
src/where.c
|
@ -604,7 +604,7 @@ static WhereTerm *findTerm(
|
|||
&& pTerm->u.leftColumn==iColumn
|
||||
&& (pTerm->eOperator & op)!=0
|
||||
){
|
||||
if( pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){
|
||||
Expr *pX = pTerm->pExpr;
|
||||
CollSeq *pColl;
|
||||
char idxaff;
|
||||
|
@ -2005,7 +2005,6 @@ static void constructAutomaticIndex(
|
|||
int nByte; /* Byte of memory needed for pIdx */
|
||||
Index *pIdx; /* Object describing the transient index */
|
||||
Vdbe *v; /* Prepared statement under construction */
|
||||
int regIsInit; /* Register set by initialization */
|
||||
int addrInit; /* Address of the initialization bypass jump */
|
||||
Table *pTable; /* The table being indexed */
|
||||
KeyInfo *pKeyinfo; /* Key information for the index */
|
||||
|
@ -2022,8 +2021,7 @@ static void constructAutomaticIndex(
|
|||
** transient index on 2nd and subsequent iterations of the loop. */
|
||||
v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
regIsInit = ++pParse->nMem;
|
||||
addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
|
||||
addrInit = sqlite3CodeOnce(pParse);
|
||||
|
||||
/* Count the number of columns that will be added to the index
|
||||
** and used to match WHERE clause constraints */
|
||||
|
@ -3052,10 +3050,24 @@ static void bestBtreeIndex(
|
|||
#endif
|
||||
used |= pTerm->prereqRight;
|
||||
}
|
||||
|
||||
/* Determine the value of rangeDiv */
|
||||
if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
|
||||
int j = pProbe->aiColumn[nEq];
|
||||
|
||||
/* If the index being considered is UNIQUE, and there is an equality
|
||||
** constraint for all columns in the index, then this search will find
|
||||
** at most a single row. In this case set the WHERE_UNIQUE flag to
|
||||
** indicate this to the caller.
|
||||
**
|
||||
** Otherwise, if the search may find more than one row, test to see if
|
||||
** there is a range constraint on indexed column (nEq+1) that can be
|
||||
** optimized using the index.
|
||||
*/
|
||||
if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
|
||||
testcase( wsFlags & WHERE_COLUMN_IN );
|
||||
testcase( wsFlags & WHERE_COLUMN_NULL );
|
||||
if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
|
||||
wsFlags |= WHERE_UNIQUE;
|
||||
}
|
||||
}else if( pProbe->bUnordered==0 ){
|
||||
int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]);
|
||||
if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
|
||||
WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
|
||||
WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
|
||||
|
@ -3074,12 +3086,6 @@ static void bestBtreeIndex(
|
|||
}
|
||||
wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
|
||||
}
|
||||
}else if( pProbe->onError!=OE_None ){
|
||||
testcase( wsFlags & WHERE_COLUMN_IN );
|
||||
testcase( wsFlags & WHERE_COLUMN_NULL );
|
||||
if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
|
||||
wsFlags |= WHERE_UNIQUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is an ORDER BY clause and the index being considered will
|
||||
|
@ -3690,10 +3696,12 @@ static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
|
|||
|
||||
j = i;
|
||||
if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
|
||||
explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">");
|
||||
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
explainAppendTerm(&txt, i++, z, ">");
|
||||
}
|
||||
if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
|
||||
explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<");
|
||||
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
explainAppendTerm(&txt, i, z, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(&txt, ")", 1);
|
||||
return sqlite3StrAccumFinish(&txt);
|
||||
|
@ -4051,7 +4059,7 @@ static Bitmask codeOneLoopStart(
|
|||
|
||||
pIdx = pLevel->plan.u.pIdx;
|
||||
iIdxCur = pLevel->iIdxCur;
|
||||
k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */
|
||||
k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]);
|
||||
|
||||
/* If this loop satisfies a sort order (pOrderBy) request that
|
||||
** was passed to this function to implement a "SELECT min(x) ..."
|
||||
|
@ -4097,7 +4105,9 @@ static Bitmask codeOneLoopStart(
|
|||
** a forward order scan on a descending index, interchange the
|
||||
** start and end terms (pRangeStart and pRangeEnd).
|
||||
*/
|
||||
if( nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC) ){
|
||||
if( (nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
|
||||
|| (bRev && pIdx->nColumn==nEq)
|
||||
){
|
||||
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,25 @@ do_test attach-1.3 {
|
|||
SELECT * FROM two.t2;
|
||||
}
|
||||
} {1 x 2 y}
|
||||
|
||||
# Tests for the sqlite3_db_filename interface
|
||||
#
|
||||
do_test attach-1.3.1 {
|
||||
file tail [sqlite3_db_filename db main]
|
||||
} {test.db}
|
||||
do_test attach-1.3.2 {
|
||||
file tail [sqlite3_db_filename db MAIN]
|
||||
} {test.db}
|
||||
do_test attach-1.3.3 {
|
||||
file tail [sqlite3_db_filename db temp]
|
||||
} {}
|
||||
do_test attach-1.3.4 {
|
||||
file tail [sqlite3_db_filename db two]
|
||||
} {test2.db}
|
||||
do_test attach-1.3.5 {
|
||||
file tail [sqlite3_db_filename db three]
|
||||
} {}
|
||||
|
||||
do_test attach-1.4 {
|
||||
execsql {
|
||||
SELECT * FROM t2;
|
||||
|
|
|
@ -142,21 +142,18 @@ do_test backup2-9 {
|
|||
# Try to restore from an unreadable file.
|
||||
#
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
do_test backup2-10 {
|
||||
forcedelete bu3.db
|
||||
file mkdir bu3.db
|
||||
set rc [catch {db restore temp bu3.db} res]
|
||||
lappend rc $res
|
||||
} {1 {cannot open source database: unable to open database file}}
|
||||
}
|
||||
if {$tcl_platform(platform)!="windows"} {
|
||||
do_test backup2-10 {
|
||||
forcedelete bu3.db
|
||||
file mkdir bu3.db
|
||||
set rc [catch {db restore temp bu3.db} res]
|
||||
lappend rc $res
|
||||
} {1 {cannot open source database: disk I/O error}}
|
||||
set msg {cannot open source database: unable to open database file}
|
||||
} elseif {$tcl_platform(os)=="OpenBSD"} {
|
||||
set msg {restore failed: file is encrypted or is not a database}
|
||||
} else {
|
||||
set msg {cannot open source database: disk I/O error}
|
||||
}
|
||||
do_test backup2-10 {
|
||||
forcedelete bu3.db
|
||||
file mkdir bu3.db
|
||||
set rc [catch {db restore temp bu3.db} res]
|
||||
lappend rc $res
|
||||
} [list 1 $msg]
|
||||
|
||||
# Try to restore from something that is not a database file.
|
||||
#
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# 2011 December 20
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this script testing the ability of SQLite to handle database
|
||||
# files larger than 4GB.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix bigfile2
|
||||
|
||||
# Create a small database.
|
||||
#
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
|
||||
# Pad the file out to 4GB in size. Then clear the file-size field in the
|
||||
# db header. This will cause SQLite to assume that the first 4GB of pages
|
||||
# are actually in use and new pages will be appended to the file.
|
||||
#
|
||||
db close
|
||||
if {[catch {fake_big_file 4096 [pwd]/test.db} msg]} {
|
||||
puts "**** Unable to create a file larger than 4096 MB. *****"
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
hexio_write test.db 28 00000000
|
||||
|
||||
do_test 1.2 {
|
||||
file size test.db
|
||||
} [expr 14 + 4096 * (1<<20)]
|
||||
|
||||
# Now insert a large row. The overflow pages will be located past the 4GB
|
||||
# boundary. Then, after opening and closing the database, test that the row
|
||||
# can be read back in.
|
||||
#
|
||||
set str [string repeat k 30000]
|
||||
do_test 1.3 {
|
||||
sqlite3 db test.db
|
||||
execsql { INSERT INTO t1 VALUES(3, $str) }
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
db one { SELECT b FROM t1 WHERE a = 3 }
|
||||
} $str
|
||||
|
||||
db close
|
||||
file delete test.db
|
||||
|
||||
finish_test
|
|
@ -11,7 +11,7 @@
|
|||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file is devoted to testing the sqlite3_next_stmt and
|
||||
# sqlite3_stmt_readonly interfaces.
|
||||
# sqlite3_stmt_readonly and sqlite3_stmt_busy interfaces.
|
||||
#
|
||||
# $Id: capi3d.test,v 1.2 2008/07/14 15:11:20 drh Exp $
|
||||
#
|
||||
|
@ -112,5 +112,29 @@ do_test capi3-2.99 {
|
|||
sqlite3_stmt_readonly 0
|
||||
} 1
|
||||
|
||||
# Tests for sqlite3_stmt_busy
|
||||
#
|
||||
do_test capi3d-3.1 {
|
||||
db eval {INSERT INTO t1 VALUES(6); INSERT INTO t1 VALUES(7);}
|
||||
set STMT [sqlite3_prepare db {SELECT * FROM t1} -1 TAIL]
|
||||
sqlite3_stmt_busy $STMT
|
||||
} {0}
|
||||
do_test capi3d-3.2 {
|
||||
sqlite3_step $STMT
|
||||
sqlite3_stmt_busy $STMT
|
||||
} {1}
|
||||
do_test capi3d-3.3 {
|
||||
sqlite3_step $STMT
|
||||
sqlite3_stmt_busy $STMT
|
||||
} {1}
|
||||
do_test capi3d-3.4 {
|
||||
sqlite3_reset $STMT
|
||||
sqlite3_stmt_busy $STMT
|
||||
} {0}
|
||||
|
||||
do_test capi3d-3.99 {
|
||||
sqlite3_finalize $STMT
|
||||
sqlite3_stmt_busy 0
|
||||
} {0}
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
# 2012 January 12
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix corruptF
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts (using the [hexio_write] command).
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
proc str {i} { format %08d $i }
|
||||
|
||||
# Create a 6 page database containing a single table - t1. Table t1
|
||||
# consists of page 2 (the root page) and pages 5 and 6 (leaf pages).
|
||||
# Database pages 3 and 4 are on the free list.
|
||||
#
|
||||
proc create_test_db {} {
|
||||
catch { db close }
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
db func str str
|
||||
execsql {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(x); /* root page = 2 */
|
||||
CREATE TABLE t2(x); /* root page = 3 */
|
||||
CREATE TABLE t3(x); /* root page = 4 */
|
||||
|
||||
INSERT INTO t1 VALUES(str(1));
|
||||
INSERT INTO t1 SELECT str(rowid+1) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+2) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+4) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+8) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+16) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+32) FROM t1;
|
||||
INSERT INTO t1 SELECT str(rowid+64) FROM t1;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t3;
|
||||
}
|
||||
db close
|
||||
}
|
||||
|
||||
do_test 1.1 { create_test_db } {}
|
||||
|
||||
# Check the db is as we expect. 6 pages in total, with 3 and 4 on the free
|
||||
# list. Page 3 is the free list trunk and page 4 is a leaf.
|
||||
#
|
||||
do_test 1.2 { file size test.db } [expr 6*1024]
|
||||
do_test 1.3 { hexio_read test.db 32 4 } 00000003
|
||||
do_test 1.4 { hexio_read test.db [expr 2*1024] 12 } 000000000000000100000004
|
||||
|
||||
# Change the free-list entry to page 6 and reopen the db file.
|
||||
do_test 1.5 {
|
||||
hexio_write test.db [expr 2*1024 + 8] 00000006
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
|
||||
# Now create a new table in the database file. The root of the new table
|
||||
# is page 6, which is also the right-most leaf page in table t1.
|
||||
#
|
||||
do_execsql_test 1.6 {
|
||||
CREATE TABLE t4(x);
|
||||
SELECT * FROM sqlite_master;
|
||||
} {
|
||||
table t1 t1 2 {CREATE TABLE t1(x)}
|
||||
table t4 t4 6 {CREATE TABLE t4(x)}
|
||||
}
|
||||
|
||||
# At one point this was causing an assert to fail.
|
||||
#
|
||||
# This statement opens a cursor on table t1 and does a full table scan. As
|
||||
# each row is visited, it is copied into table t4. There is no temporary
|
||||
# table.
|
||||
#
|
||||
# When the t1 cursor reaches page 6 (which is both the right-most leaf of
|
||||
# t1 and the root of t4), it continues to iterate through the keys within
|
||||
# it (which at this point are keys that have been inserted into t4). And
|
||||
# for each row visited, another row is inserted into page 6 - it being the
|
||||
# root page of t4. Eventually, page 6 becomes full and the height of the
|
||||
# b-tree for table t4 increased. From the point of view of the t1 cursor,
|
||||
# this unexpectedly reduces the number of keys on page 6 in the middle of
|
||||
# its iteration, which causes an assert() to fail.
|
||||
#
|
||||
db_save_and_close
|
||||
if 1 {
|
||||
for {set i 0} {$i < 128} {incr i} {
|
||||
db_restore_and_reopen
|
||||
do_test 1.7.$i {
|
||||
set res [
|
||||
catchsql { INSERT INTO t4 SELECT x FROM t1 WHERE rowid>$i }
|
||||
]
|
||||
if {$res == "0 {}" || $res == "1 {database disk image is malformed}"} {
|
||||
set res ""
|
||||
}
|
||||
set res
|
||||
} {}
|
||||
}
|
||||
}
|
||||
|
||||
do_test 2.1 { create_test_db } {}
|
||||
do_test 2.2 { file size test.db } [expr 6*1024]
|
||||
do_test 2.3 { hexio_read test.db 32 4 } 00000003
|
||||
do_test 2.4 { hexio_read test.db [expr 2*1024] 12 } 000000000000000100000004
|
||||
|
||||
# Change the free-list entry to page 5 and reopen the db file.
|
||||
do_test 2.5 {
|
||||
hexio_write test.db [expr 2*1024 + 8] 00000005
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
|
||||
# Now create a new table in the database file. The root of the new table
|
||||
# is page 5, which is also the right-most leaf page in table t1.
|
||||
#
|
||||
do_execsql_test 2.6 {
|
||||
CREATE TABLE t4(x);
|
||||
SELECT * FROM sqlite_master;
|
||||
} {
|
||||
table t1 t1 2 {CREATE TABLE t1(x)}
|
||||
table t4 t4 5 {CREATE TABLE t4(x)}
|
||||
}
|
||||
|
||||
db_save_and_close
|
||||
for {set i 127} {$i >= 0} {incr i -1} {
|
||||
db_restore_and_reopen
|
||||
do_test 2.7.$i {
|
||||
set res [
|
||||
catchsql {
|
||||
INSERT INTO t4 SELECT x FROM t1 WHERE rowid<$i ORDER BY rowid DESC
|
||||
}
|
||||
]
|
||||
if {$res == "0 {}" || $res == "1 {database disk image is malformed}"} {
|
||||
set res ""
|
||||
}
|
||||
set res
|
||||
} {}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
|
@ -62,6 +62,11 @@ ifcapable stat3 {
|
|||
set STAT3 0
|
||||
}
|
||||
|
||||
ifcapable malloc_usable_size {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the dbstatus-2 and dbstatus-3 tests with several of different
|
||||
# lookaside buffer sizes.
|
||||
|
@ -207,8 +212,13 @@ foreach ::lookaside_buffer_size {0 64 120} {
|
|||
# Some of the memory used for sqlite_stat3 is unaccounted for by
|
||||
# dbstatus.
|
||||
#
|
||||
# Finally, on osx the estimate of memory used by the schema may be
|
||||
# slightly low.
|
||||
#
|
||||
if {[string match *x $tn] || $AUTOVACUUM
|
||||
|| ([string match *y $tn] && $STAT3)} {
|
||||
|| ([string match *y $tn] && $STAT3)
|
||||
|| ($::tcl_platform(os) == "Darwin")
|
||||
} {
|
||||
do_test dbstatus-2.$tn.ax { expr {($nSchema1-$nSchema2)<=$nFree} } 1
|
||||
} else {
|
||||
do_test dbstatus-2.$tn.a { expr {$nSchema1-$nSchema2} } $nFree
|
||||
|
|
|
@ -58,7 +58,7 @@ proc table_list {} {
|
|||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-25262-01881 -- syntax diagram type-name
|
||||
# EVIDENCE-OF: R-47266-09114 -- syntax diagram type-name
|
||||
#
|
||||
do_createtable_tests 0.1.1 -repair {
|
||||
drop_all_tables
|
||||
|
@ -79,12 +79,7 @@ do_createtable_tests 0.1.2 -error {
|
|||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-18762-12428 -- syntax diagram column-constraint
|
||||
#
|
||||
# Note: Not shown in the syntax diagram is the "NULL" constraint. This
|
||||
# is the opposite of "NOT NULL" - it implies that the column may
|
||||
# take a NULL value. This is the default anyway, so this type of
|
||||
# constraint is rarely used.
|
||||
# EVIDENCE-OF: R-60689-48779 -- syntax diagram column-constraint
|
||||
#
|
||||
do_createtable_tests 0.2.1 -repair {
|
||||
drop_all_tables
|
||||
|
@ -131,7 +126,7 @@ do_createtable_tests 0.2.1 -repair {
|
|||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-17905-31923 -- syntax diagram table-constraint
|
||||
# EVIDENCE-OF: R-58169-51804 -- syntax diagram table-constraint
|
||||
#
|
||||
do_createtable_tests 0.3.1 -repair {
|
||||
drop_all_tables
|
||||
|
@ -150,7 +145,7 @@ do_createtable_tests 0.3.1 -repair {
|
|||
4.1 "CREATE TABLE t1(c1, c2, FOREIGN KEY(c1) REFERENCES t2)" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-18765-31171 -- syntax diagram column-def
|
||||
# EVIDENCE-OF: R-44826-22243 -- syntax diagram column-def
|
||||
#
|
||||
do_createtable_tests 0.4.1 -repair {
|
||||
drop_all_tables
|
||||
|
@ -165,7 +160,7 @@ do_createtable_tests 0.4.1 -repair {
|
|||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-59573-11075 -- syntax diagram create-table-stmt
|
||||
# EVIDENCE-OF: R-45698-45677 -- syntax diagram create-table-stmt
|
||||
#
|
||||
do_createtable_tests 0.5.1 -repair {
|
||||
drop_all_tables
|
||||
|
@ -190,7 +185,7 @@ do_createtable_tests 0.5.1 -repair {
|
|||
15 "CREATE TABLE t1 AS SELECT count(*), max(b), min(a) FROM t2" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-32138-02228 -- syntax diagram foreign-key-clause
|
||||
# EVIDENCE-OF: R-24369-11919 -- syntax diagram foreign-key-clause
|
||||
#
|
||||
# 1: Explicit parent-key columns.
|
||||
# 2: Implicit child-key columns.
|
||||
|
|
|
@ -24,9 +24,9 @@ do_execsql_test e_delete-0.0 {
|
|||
CREATE INDEX i1 ON t1(a);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-24177-52883 -- syntax diagram delete-stmt
|
||||
# EVIDENCE-OF: R-62077-19799 -- syntax diagram delete-stmt
|
||||
#
|
||||
# EVIDENCE-OF: R-12802-60464 -- syntax diagram qualified-table-name
|
||||
# EVIDENCE-OF: R-60796-31013 -- syntax diagram qualified-table-name
|
||||
#
|
||||
do_delete_tests e_delete-0.1 {
|
||||
1 "DELETE FROM t1" {}
|
||||
|
@ -287,7 +287,7 @@ do_delete_tests e_delete-2.5 -error { near "%s": syntax error } {
|
|||
# of the DELETE statement is extended by the addition of optional ORDER
|
||||
# BY and LIMIT clauses:
|
||||
#
|
||||
# EVIDENCE-OF: R-45897-01670 -- syntax diagram delete-stmt-limited
|
||||
# EVIDENCE-OF: R-52694-53361 -- syntax diagram delete-stmt-limited
|
||||
#
|
||||
do_delete_tests e_delete-3.1 {
|
||||
1 "DELETE FROM t1 LIMIT 5" {}
|
||||
|
|
|
@ -69,7 +69,7 @@ proc droptrigger_reopen_db {{event INSERT}} {
|
|||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-52650-16855 -- syntax diagram drop-trigger-stmt
|
||||
# EVIDENCE-OF: R-27975-10951 -- syntax diagram drop-trigger-stmt
|
||||
#
|
||||
do_droptrigger_tests 1.1 -repair {
|
||||
droptrigger_reopen_db
|
||||
|
|
|
@ -70,7 +70,7 @@ proc do_dropview_tests {nm args} {
|
|||
uplevel do_select_tests $nm $args
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-21739-51207 -- syntax diagram drop-view-stmt
|
||||
# EVIDENCE-OF: R-53136-36436 -- syntax diagram drop-view-stmt
|
||||
#
|
||||
# All paths in the syntax diagram for DROP VIEW are tested by tests 1.*.
|
||||
#
|
||||
|
|
|
@ -627,7 +627,7 @@ do_test e_expr-11.7.1 { sqlite3_finalize $stmt } SQLITE_OK
|
|||
#-------------------------------------------------------------------------
|
||||
# "Test" the syntax diagrams in lang_expr.html.
|
||||
#
|
||||
# EVIDENCE-OF: R-62067-43884 -- syntax diagram signed-number
|
||||
# EVIDENCE-OF: R-02989-21050 -- syntax diagram signed-number
|
||||
#
|
||||
do_execsql_test e_expr-12.1.1 { SELECT 0, +0, -0 } {0 0 0}
|
||||
do_execsql_test e_expr-12.1.2 { SELECT 1, +1, -1 } {1 1 -1}
|
||||
|
@ -642,7 +642,7 @@ do_execsql_test e_expr-12.1.6 {
|
|||
SELECT 0.0001, +0.0001, -0.0001
|
||||
} {0.0001 0.0001 -0.0001}
|
||||
|
||||
# EVIDENCE-OF: R-21258-25489 -- syntax diagram literal-value
|
||||
# EVIDENCE-OF: R-43188-60852 -- syntax diagram literal-value
|
||||
#
|
||||
set sqlite_current_time 1
|
||||
do_execsql_test e_expr-12.2.1 {SELECT 123} {123}
|
||||
|
@ -655,7 +655,7 @@ do_execsql_test e_expr-12.2.7 {SELECT CURRENT_DATE} {1970-01-01}
|
|||
do_execsql_test e_expr-12.2.8 {SELECT CURRENT_TIMESTAMP} {{1970-01-01 00:00:01}}
|
||||
set sqlite_current_time 0
|
||||
|
||||
# EVIDENCE-OF: R-57598-59332 -- syntax diagram expr
|
||||
# EVIDENCE-OF: R-50544-32159 -- syntax diagram expr
|
||||
#
|
||||
forcedelete test.db2
|
||||
execsql {
|
||||
|
@ -812,7 +812,7 @@ foreach {tn expr} {
|
|||
}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-49462-56079 -- syntax diagram raise-function
|
||||
# EVIDENCE-OF: R-39820-63916 -- syntax diagram raise-function
|
||||
#
|
||||
foreach {tn raiseexpr} {
|
||||
1 "RAISE(IGNORE)"
|
||||
|
|
|
@ -45,7 +45,7 @@ proc do_insert_tests {args} {
|
|||
uplevel do_select_tests $args
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-41448-54465 -- syntax diagram insert-stmt
|
||||
# EVIDENCE-OF: R-55375-41353 -- syntax diagram insert-stmt
|
||||
#
|
||||
do_insert_tests e_insert-0 {
|
||||
1 "INSERT INTO a1 DEFAULT VALUES" {}
|
||||
|
|
|
@ -26,7 +26,7 @@ do_execsql_test e_reindex-0.0 {
|
|||
CREATE INDEX i2 ON t1(b, a);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-57021-15304 -- syntax diagram reindex-stmt
|
||||
# EVIDENCE-OF: R-51477-38549 -- syntax diagram reindex-stmt
|
||||
#
|
||||
do_reindex_tests e_reindex-0.1 {
|
||||
1 "REINDEX" {}
|
||||
|
@ -34,8 +34,8 @@ do_reindex_tests e_reindex-0.1 {
|
|||
3 "REINDEX binary" {}
|
||||
4 "REINDEX t1" {}
|
||||
5 "REINDEX main.t1" {}
|
||||
4 "REINDEX i1" {}
|
||||
5 "REINDEX main.i1" {}
|
||||
6 "REINDEX i1" {}
|
||||
7 "REINDEX main.i1" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-52173-44778 The REINDEX command is used to delete and
|
||||
|
|
|
@ -78,7 +78,7 @@ proc do_join_test {tn select res} {
|
|||
# The following tests check that all paths on the syntax diagrams on
|
||||
# the lang_select.html page may be taken.
|
||||
#
|
||||
# EVIDENCE-OF: R-18428-22111 -- syntax diagram join-constraint
|
||||
# EVIDENCE-OF: R-11353-33501 -- syntax diagram join-constraint
|
||||
#
|
||||
do_join_test e_select-0.1.1 {
|
||||
SELECT count(*) FROM t1 %JOIN% t2 ON (t1.a=t2.a)
|
||||
|
@ -96,7 +96,7 @@ do_catchsql_test e_select-0.1.5 {
|
|||
SELECT count(*) FROM t1, t2 USING (a) ON (t1.a=t2.a)
|
||||
} {1 {near "ON": syntax error}}
|
||||
|
||||
# EVIDENCE-OF: R-44854-11739 -- syntax diagram select-core
|
||||
# EVIDENCE-OF: R-40919-40941 -- syntax diagram select-core
|
||||
#
|
||||
# 0: SELECT ...
|
||||
# 1: SELECT DISTINCT ...
|
||||
|
@ -221,7 +221,7 @@ do_select_tests e_select-0.2 {
|
|||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-23316-20169 -- syntax diagram result-column
|
||||
# EVIDENCE-OF: R-41378-26734 -- syntax diagram result-column
|
||||
#
|
||||
do_select_tests e_select-0.3 {
|
||||
1 "SELECT * FROM t1" {a one b two c three}
|
||||
|
@ -231,9 +231,9 @@ do_select_tests e_select-0.3 {
|
|||
5 "SELECT 'x'||a||'x' AS alias FROM t1" {xax xbx xcx}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-41233-21397 -- syntax diagram join-source
|
||||
# EVIDENCE-OF: R-43129-35648 -- syntax diagram join-source
|
||||
#
|
||||
# EVIDENCE-OF: R-45040-11121 -- syntax diagram join-op
|
||||
# EVIDENCE-OF: R-36683-37460 -- syntax diagram join-op
|
||||
#
|
||||
do_select_tests e_select-0.4 {
|
||||
1 "SELECT t1.rowid FROM t1" {1 2 3}
|
||||
|
@ -258,7 +258,7 @@ do_select_tests e_select-0.4 {
|
|||
16 "SELECT t1.rowid FROM t1 CROSS JOIN t3" {1 1 2 2 3 3}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-56911-63533 -- syntax diagram compound-operator
|
||||
# EVIDENCE-OF: R-28308-37813 -- syntax diagram compound-operator
|
||||
#
|
||||
do_select_tests e_select-0.5 {
|
||||
1 "SELECT rowid FROM t1 UNION ALL SELECT rowid+2 FROM t4" {1 2 3 3 4}
|
||||
|
@ -267,7 +267,7 @@ do_select_tests e_select-0.5 {
|
|||
4 "SELECT rowid FROM t1 EXCEPT SELECT rowid+2 FROM t4" {1 2}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-60388-27458 -- syntax diagram ordering-term
|
||||
# EVIDENCE-OF: R-06480-34950 -- syntax diagram ordering-term
|
||||
#
|
||||
do_select_tests e_select-0.6 {
|
||||
1 "SELECT b||a FROM t1 ORDER BY b||a" {onea threec twob}
|
||||
|
@ -276,7 +276,7 @@ do_select_tests e_select-0.6 {
|
|||
4 "SELECT b||a FROM t1 ORDER BY (b||a) DESC" {twob threec onea}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-36494-33519 -- syntax diagram select-stmt
|
||||
# EVIDENCE-OF: R-23926-36668 -- syntax diagram select-stmt
|
||||
#
|
||||
do_select_tests e_select-0.7 {
|
||||
1 "SELECT * FROM t1" {a one b two c three}
|
||||
|
|
|
@ -49,7 +49,7 @@ proc do_update_tests {args} {
|
|||
uplevel do_select_tests $args
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-05685-44205 -- syntax diagram update-stmt
|
||||
# EVIDENCE-OF: R-62337-45828 -- syntax diagram update-stmt
|
||||
#
|
||||
do_update_tests e_update-0 {
|
||||
1 "UPDATE t1 SET a=10" {}
|
||||
|
@ -495,7 +495,7 @@ do_update_tests e_update-2.5 -error {
|
|||
# of the UPDATE statement is extended with optional ORDER BY and LIMIT
|
||||
# clauses
|
||||
#
|
||||
# EVIDENCE-OF: R-08948-01887 -- syntax diagram update-stmt-limited
|
||||
# EVIDENCE-OF: R-45169-39597 -- syntax diagram update-stmt-limited
|
||||
#
|
||||
do_update_tests e_update-3.0 {
|
||||
1 "UPDATE t1 SET a=b LIMIT 5" {}
|
||||
|
|
|
@ -65,7 +65,7 @@ proc fragment_count {name} {
|
|||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-63707-33375 -- syntax diagram vacuum-stmt
|
||||
# EVIDENCE-OF: R-45173-45977 -- syntax diagram vacuum-stmt
|
||||
#
|
||||
do_execsql_test e_vacuum-0.1 { VACUUM } {}
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ ifcapable wal {
|
|||
PRAGMA wal_checkpoint;
|
||||
}
|
||||
file size test.db-wal
|
||||
} {1640}
|
||||
} [expr {32+2*(512+24)}]
|
||||
|
||||
do_test 4.3 {
|
||||
db close
|
||||
|
@ -205,7 +205,7 @@ ifcapable wal {
|
|||
if {$newsz>$maxsz} {set maxsz $newsz}
|
||||
}
|
||||
set maxsz
|
||||
} {2176}
|
||||
} [expr {32+3*(512+24)}]
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -386,4 +386,179 @@ ifcapable foreignkey {
|
|||
} {1}
|
||||
}
|
||||
|
||||
# Ticket [676bc02b87176125635cb174d110b431581912bb]
|
||||
# Make sure INTEGER PRIMARY KEY ON CONFLICT ... works with the xfer
|
||||
# optimization.
|
||||
#
|
||||
do_test insert4-8.1 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT REPLACE, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.2 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.3 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT IGNORE, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT IGNORE, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
do_test insert4-8.4 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT IGNORE, b);
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
do_test insert4-8.5 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT FAIL, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT FAIL, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(-99,100);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
do_test insert4-8.6 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {-99 100 1 2}
|
||||
do_test insert4-8.7 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ABORT, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ABORT, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(-99,100);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
do_test insert4-8.8 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
do_test insert4-8.9 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, y);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
INSERT INTO t2 VALUES(-99,100);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
catchsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(2,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
do_test insert4-8.10 {
|
||||
catchsql {COMMIT}
|
||||
} {1 {cannot commit - no transaction is active}}
|
||||
do_test insert4-8.11 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
|
||||
do_test insert4-8.21 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT REPLACE, y);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.22 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT IGNORE, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT IGNORE, y);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.23 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ABORT, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ABORT, y);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.24 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT FAIL, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT FAIL, y);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
do_test insert4-8.25 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, b);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY ON CONFLICT ROLLBACK, y);
|
||||
INSERT INTO t2 VALUES(1,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 3}
|
||||
|
||||
|
||||
finish_test
|
||||
|
|
|
@ -34,7 +34,7 @@ proc a_string {n} {
|
|||
# characteristics flags to "SAFE_DELETE".
|
||||
#
|
||||
testvfs tvfs -default 1
|
||||
tvfs devchar undeletable_when_open
|
||||
tvfs devchar {undeletable_when_open powersafe_overwrite}
|
||||
|
||||
# Set up a hook so that each time a journal file is opened, closed or
|
||||
# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
|
||||
|
@ -231,4 +231,3 @@ ifcapable wal {
|
|||
|
||||
tvfs delete
|
||||
finish_test
|
||||
|
||||
|
|
|
@ -352,7 +352,7 @@ do_test malloc5-6.3.1 {
|
|||
do_test malloc5-6.3.2 {
|
||||
# Try to release 7700 bytes. This should release all the
|
||||
# non-dirty pages held by db2.
|
||||
sqlite3_release_memory [expr 7*1100]
|
||||
sqlite3_release_memory [expr 7*1132]
|
||||
list [nPage db] [nPage db2]
|
||||
} {10 3}
|
||||
do_test malloc5-6.3.3 {
|
||||
|
@ -366,7 +366,7 @@ do_test malloc5-6.3.4 {
|
|||
# the rest of the db cache. But the db2 cache remains intact, because
|
||||
# SQLite tries to avoid calling sync().
|
||||
if {$::tcl_platform(wordSize)==8} {
|
||||
sqlite3_release_memory 10177
|
||||
sqlite3_release_memory 10500
|
||||
} else {
|
||||
sqlite3_release_memory 9900
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ proc reset_highwater_marks {} {
|
|||
sqlite3_status SQLITE_STATUS_PARSER_STACK 1
|
||||
}
|
||||
|
||||
set xtra_size 256
|
||||
set xtra_size 290
|
||||
|
||||
# Test 1: Both PAGECACHE and SCRATCH are shut down.
|
||||
#
|
||||
|
@ -97,9 +97,11 @@ reset_highwater_marks
|
|||
build_test_db memsubsys1-2 {PRAGMA page_size=1024}
|
||||
#show_memstats
|
||||
set MEMORY_MANAGEMENT $sqlite_options(memorymanage)
|
||||
do_test memsubsys1-2.3 {
|
||||
set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
|
||||
} [expr ($TEMP_STORE>1 || $MEMORY_MANAGEMENT==0)*1024]
|
||||
ifcapable !malloc_usable_size {
|
||||
do_test memsubsys1-2.3 {
|
||||
set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
|
||||
} [expr ($TEMP_STORE>1 || $MEMORY_MANAGEMENT==0)*1024]
|
||||
}
|
||||
do_test memsubsys1-2.4 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 20
|
||||
|
|
|
@ -14,6 +14,16 @@ set testdir [file dirname $argv0]
|
|||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
# The tests in this file assume that SQLite is compiled without
|
||||
# ENABLE_8_3_NAMES.
|
||||
#
|
||||
ifcapable 8_3_names {
|
||||
puts -nonewline "SQLite compiled with SQLITE_ENABLE_8_3_NAMES. "
|
||||
puts "Skipping tests multiplex-*."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
|
||||
set g_max_chunks 32
|
||||
|
||||
|
@ -24,7 +34,7 @@ set g_max_chunks 32
|
|||
# file name with the chunk number.
|
||||
proc multiplex_name {name chunk} {
|
||||
if {$chunk==0} { return $name }
|
||||
set num [format "%02d" $chunk]
|
||||
set num [format "%03d" $chunk]
|
||||
ifcapable {multiplex_ext_overwrite} {
|
||||
set name [string range $name 0 [expr [string length $name]-2-1]]
|
||||
}
|
||||
|
@ -146,6 +156,9 @@ sqlite3_multiplex_initialize "" 1
|
|||
multiplex_set db main 32768 16
|
||||
|
||||
forcedelete test.x
|
||||
foreach f [glob -nocomplain {test.x*[0-9][0-9][0-9]}] {
|
||||
forcedelete $f
|
||||
}
|
||||
do_test multiplex-2.1.2 {
|
||||
sqlite3 db test.x
|
||||
execsql {
|
||||
|
@ -182,12 +195,17 @@ do_test multiplex-2.4.2 {
|
|||
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {}
|
||||
do_test multiplex-2.4.4 { file size [multiplex_name test.x 0] } {7168}
|
||||
do_test multiplex-2.4.99 {
|
||||
do_test multiplex-2.4.5 {
|
||||
db close
|
||||
sqlite3 db test.x
|
||||
db eval vacuum
|
||||
db close
|
||||
glob test.x*
|
||||
} {test.x}
|
||||
do_test multiplex-2.4.99 {
|
||||
sqlite3_multiplex_shutdown
|
||||
} {SQLITE_OK}
|
||||
|
||||
|
||||
do_test multiplex-2.5.1 {
|
||||
multiplex_delete test.x
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
# 2010 October 29
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
|
||||
|
||||
do_multiclient_test tn {
|
||||
code1 { catch { sqlite3_multiplex_initialize "" 0 } }
|
||||
code2 { catch { sqlite3_multiplex_initialize "" 0 } }
|
||||
|
||||
code1 { db close }
|
||||
code2 { db2 close }
|
||||
|
||||
code1 { sqlite3 db test.db -vfs multiplex }
|
||||
code2 { sqlite3 db2 test.db -vfs multiplex }
|
||||
|
||||
code1 { sqlite3_multiplex_control db main chunk_size [expr 1024*1024] }
|
||||
code2 { sqlite3_multiplex_control db2 main chunk_size [expr 1024*1024] }
|
||||
|
||||
sql1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 64
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 128
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 256
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 512
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
|
||||
do_test multiplex-1.$tn.1 { sql1 { SELECT count(*) FROM t1 } } 512
|
||||
do_test multiplex-1.$tn.2 { sql2 { SELECT count(*) FROM t1 } } 512
|
||||
sql2 { DELETE FROM t1 ; VACUUM }
|
||||
do_test multiplex-1.$tn.3 { sql1 { SELECT count(*) FROM t1 } } 0
|
||||
|
||||
sql1 {
|
||||
INSERT INTO t1 VALUES(randomblob(10), randomblob(4000)); -- 1
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 2
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 4
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 8
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 16
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 32
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 64
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 128
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 256
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(4000) FROM t1; -- 512
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
|
||||
do_test multiplex-1.$tn.4 { sql2 { SELECT count(*) FROM t1 } } 512
|
||||
}
|
||||
|
||||
catch { sqlite3_multiplex_shutdown }
|
||||
finish_test
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
# 2011 December 13
|
||||
#
|
||||
# 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 contains tests for error (IO, OOM etc.) handling when using
|
||||
# the multiplexor extension with 8.3 filenames.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix multiplex3
|
||||
|
||||
ifcapable !8_3_names {
|
||||
puts -nonewline "SQLite compiled without SQLITE_ENABLE_8_3_NAMES. "
|
||||
puts "Skipping tests multiplex3-*."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
autoinstall_test_functions
|
||||
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
|
||||
proc destroy_vfs_stack {} {
|
||||
generic_unregister stack
|
||||
sqlite3_multiplex_shutdown
|
||||
}
|
||||
|
||||
proc multiplex_delete_db {} {
|
||||
forcedelete test.db
|
||||
for {set i 1} {$i <= 1000} {incr i} {
|
||||
forcedelete test.[format %03d $i]
|
||||
}
|
||||
}
|
||||
|
||||
# Procs to save and restore the current muliplexed database.
|
||||
#
|
||||
proc multiplex_save_db {} {
|
||||
foreach f [glob -nocomplain sv_test.*] { forcedelete $f }
|
||||
foreach f [glob -nocomplain test.*] { forcecopy $f "sv_$f" }
|
||||
}
|
||||
proc multiplex_restore_db {} {
|
||||
foreach f [glob -nocomplain test.*] {forcedelete $f}
|
||||
foreach f [glob -nocomplain sv_test.*] {forcecopy $f [string range $f 3 end]} }
|
||||
|
||||
proc setup_and_save_db {} {
|
||||
multiplex_delete_db
|
||||
sqlite3 db file:test.db?8_3_names=1
|
||||
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(randomblob(15), randomblob(2000));
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 2
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 4
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 8
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 16
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 32
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 64
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 128
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 256
|
||||
INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1; -- 512
|
||||
}
|
||||
set ::cksum1 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
|
||||
db close
|
||||
multiplex_save_db
|
||||
}
|
||||
|
||||
do_test 1.0 { setup_and_save_db } {}
|
||||
do_faultsim_test 1 -prep {
|
||||
multiplex_restore_db
|
||||
sqlite3 db file:test.db?8_3_names=1
|
||||
sqlite3_multiplex_control db main chunk_size [expr 256*1024]
|
||||
} -body {
|
||||
execsql {
|
||||
UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
if {$testrc!=0} {
|
||||
set cksum2 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
|
||||
if {$cksum2 != $::cksum1} { error "data mismatch" }
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests verify that hot-journal rollback works. As follows:
|
||||
#
|
||||
# 1. Create a large database.
|
||||
# 2. Set the pager cache to be very small.
|
||||
# 3. Open a transaction.
|
||||
# 4. Run the following 100 times:
|
||||
# a. Update a row.
|
||||
# b. Copy all files on disk to a new db location, including the journal.
|
||||
# c. Verify that the new db can be opened and that the content matches
|
||||
# the database created in step 1 (proving the journal was rolled
|
||||
# back).
|
||||
|
||||
do_test 2.0 {
|
||||
setup_and_save_db
|
||||
multiplex_restore_db
|
||||
sqlite3 db file:test.db?8_3_names=1
|
||||
execsql { PRAGMA cache_size = 10 }
|
||||
execsql { BEGIN }
|
||||
} {}
|
||||
|
||||
for {set iTest 1} {$iTest<=100} {incr iTest} {
|
||||
do_test 2.$iTest {
|
||||
execsql {
|
||||
UPDATE t1 SET a=randomblob(12), b=randomblob(1400) WHERE rowid=5*$iTest
|
||||
}
|
||||
foreach f [glob -nocomplain test.*] {forcecopy $f "xx_$f"}
|
||||
sqlite3 db2 file:xx_test.db?8_3_names=1
|
||||
execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a} db2
|
||||
} $::cksum1
|
||||
|
||||
db2 close
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
sqlite3_multiplex_shutdown
|
||||
finish_test
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue