track 3.7.10

This commit is contained in:
Stephen Lombardo 2012-01-18 00:18:26 -05:00
parent ddb595fbc9
commit d09051a1f2
126 changed files with 10956 additions and 4081 deletions

View File

@ -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

View File

@ -1 +1 @@
3.7.9
3.7.10

View File

@ -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

6436
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -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...)

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

@ -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

View File

@ -1 +1 @@
c7c6050ef060877ebe77b41d959e9df13f8c9b5e
ebd01a8deffb5024a5d7494eef800d2366d97204

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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))

View File

@ -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]);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -150,4 +150,4 @@ int sqlite3_mutex_notheld(sqlite3_mutex *p){
}
#endif
#endif /* SQLITE_MUTEX_OMIT */
#endif /* !defined(SQLITE_MUTEX_OMIT) */

View File

@ -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) */

View File

@ -348,4 +348,4 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return &sMutex;
}
#endif /* SQLITE_MUTEX_PTHREAD */
#endif /* SQLITE_MUTEX_PTHREADS */

View File

@ -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);
}

View File

@ -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
*/

View File

@ -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++){

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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) */

View File

@ -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()

View File

@ -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
**

View File

@ -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);

View File

@ -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

View File

@ -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 },

View File

@ -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;

View File

@ -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;

View File

@ -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 }
};

View File

@ -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

View File

@ -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){

View File

@ -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;
}

View File

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

View File

@ -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,

View File

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

View File

@ -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;
}

View File

@ -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;

209
src/test_quota.h Normal file
View File

@ -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_ */

View File

@ -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.

View File

@ -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();

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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 ){

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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_) */

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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
View File

@ -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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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;

View File

@ -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.
#

59
test/bigfile2.test Normal file
View 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

View File

@ -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

150
test/corruptF.test Normal file
View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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" {}

View File

@ -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

View File

@ -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.*.
#

View File

@ -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)"

View File

@ -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" {}

View File

@ -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

View File

@ -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}

View File

@ -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" {}

View File

@ -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 } {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

70
test/multiplex2.test Normal file
View File

@ -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

133
test/multiplex3.test Normal file
View File

@ -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