aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.mk1
-rw-r--r--manifest73
-rw-r--r--manifest.uuid2
-rw-r--r--src/alter.c28
-rw-r--r--src/analyze.c63
-rw-r--r--src/backup.c4
-rw-r--r--src/btree.c17
-rw-r--r--src/btree.h2
-rw-r--r--src/os_unix.c256
-rw-r--r--src/sqlite.h.in8
-rw-r--r--src/tclsqlite.c2
-rw-r--r--src/test1.c39
-rw-r--r--src/test_hexio.c7
-rw-r--r--src/test_multiplex.c560
-rw-r--r--src/test_multiplex.h91
-rw-r--r--src/test_syscall.c654
-rw-r--r--src/vdbeaux.c5
-rw-r--r--src/where.c2
-rw-r--r--test/alter.test19
-rw-r--r--test/analyze.test10
-rw-r--r--test/analyze6.test48
-rw-r--r--test/analyze7.test119
-rw-r--r--test/badutf2.test8
-rw-r--r--test/fts3fault2.test3
-rw-r--r--test/malloc_common.tcl4
-rw-r--r--test/multiplex.test134
-rw-r--r--test/oserror.test12
-rw-r--r--test/syscall.test250
-rw-r--r--test/sysfault.test247
-rw-r--r--test/unixexcl.test83
-rw-r--r--test/wal.test1
-rw-r--r--test/wal2.test4
-rw-r--r--test/wal3.test3
-rw-r--r--test/wal5.test3
-rw-r--r--test/where3.test2
-rw-r--r--tool/mksqlite3c.tcl2
-rw-r--r--tool/split-sqlite3c.tcl82
37 files changed, 2416 insertions, 432 deletions
diff --git a/main.mk b/main.mk
index cfea9b590..8728eeb96 100644
--- a/main.mk
+++ b/main.mk
@@ -254,6 +254,7 @@ TESTSRC = \
$(TOP)/src/test_server.c \
$(TOP)/src/test_stat.c \
$(TOP)/src/test_superlock.c \
+ $(TOP)/src/test_syscall.c \
$(TOP)/src/test_tclvar.c \
$(TOP)/src/test_thread.c \
$(TOP)/src/test_vfs.c \
diff --git a/manifest b/manifest
index a081a12fe..6bfc30d59 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sadditional\stest\sdata\sand\sdocumentation\sto\sthe\sfuzzer\svirtual\stable.
-D 2011-04-01T20:28:31.337
+C Merge\sthe\sword-fuzzer\sbranch\sinto\strunk.
+D 2011-04-01T20:47:27.009
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 6c96e694f446500449f683070b906de9fce17b88
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -101,7 +101,7 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
-F main.mk a767e12162f02719fa94697a6ff0c8b51bcd62a6
+F main.mk 078e1b601071cc50b4b6ccac9faece349ead1c39
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -114,15 +114,15 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
-F src/alter.c 6a0c176e64a34929a4436048066a84ef4f1445b3
-F src/analyze.c a038162344265ac21dfb24b3fcc06c666ebb9c07
+F src/alter.c 280f5c04b11b492703a342222b3de0a999445280
+F src/analyze.c d0a673d303f611690fc7a3293aaefed57cccc5c8
F src/attach.c 438ea6f6b5d5961c1f49b737f2ce0f14ce7c6877
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
-F src/backup.c 6728d6d48d55b449af76a3e51c0808849cb32a2e
+F src/backup.c 537f89c7ef5021cb580f31f782e556ffffcb2ed1
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
-F src/btree.c 43302cc4f3de6479b90fa6bb271b65d86333d00e
-F src/btree.h e2f2cd9933bf30724f53ffa12c4c5a3a864bbd6e
+F src/btree.c 2b9c81ff64da339a67dda4f94c0d763627be0b67
+F src/btree.h 8d36f774ec4b1d0027b8966f8c03d9a72a518c14
F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4
F src/build.c 6c490fe14dedb094a202f559e3b29a276abffcf8
F src/callback.c 5069f224882cbdccd559f591271d28d7f37745bc
@@ -162,7 +162,7 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 2596fd2d5d0976c6c0c628d0c3c7c4e7a724f4cf
-F src/os_unix.c 942a9dca5d17c599300127c88a48413e6d55666f
+F src/os_unix.c a3b4cdf50a9c9be8b50dc4932354ab749962a07f
F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845
F src/pager.c 6aa906b60a59664ba58d3f746164bb010d407ce1
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
@@ -178,14 +178,14 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c d24406c45dd2442eb2eeaac413439066b149c944
F src/shell.c 9dc0b4bb59290c0a35256d278cab0f314987ad6a
-F src/sqlite.h.in 2ab8766c32afed165c0ea74292e18483e3aa64b1
+F src/sqlite.h.in e047f69a61d604d4f8be6cf1d1bdfc68be9ba7e5
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
F src/sqliteInt.h f8f1d00a22c98fd3f2fbc94da74eeb880879f89f
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
-F src/tclsqlite.c 44979405594d33c55ec5ef8e82533d3b4133e455
-F src/test1.c 9020310c7617234b33fd1c3064f89524db25f290
+F src/tclsqlite.c e8c3bc4662975cf9a8e0280b1703a3cf427f9831
+F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
F src/test4.c 0528360b5025688002a5feb6be906ddce52eaaee
@@ -203,14 +203,15 @@ F src/test_demovfs.c 0aed671636735116fc872c5b03706fd5612488b5
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
-F src/test_hexio.c 1237f000ec7a491009b1233f5c626ea71bce1ea2
+F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c fd6188b1501c0010fb4241ddc9f0d5ac402c688d
-F src/test_multiplex.c 655cb3b663f87db7d3d2427ea127c9daacae4abc
+F src/test_multiplex.c fdabd793ee7a9642c5a8a470def2347144c46d05
+F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c
@@ -221,6 +222,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_stat.c f682704b5d1ba8e1d4e7e882a6d7922e2dcf066c
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
+F src/test_syscall.c 61bb14684142a19c580a13b4dde680044d37e0f5
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0
F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86
@@ -236,7 +238,7 @@ F src/vdbe.c e3f37ca0afdd72e883475e2a32a06167df2810d0
F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2
F src/vdbeInt.h e1c6254641168507d25b46affb6dfb53c782f553
F src/vdbeapi.c a09ad9164cafc505250d5dd6b69660c960f1308c
-F src/vdbeaux.c cfd3f3ac674691ba1166ceb9a2698b0d00b2ef91
+F src/vdbeaux.c 77921792f7ebae267490816deb6a9488f938fa85
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5
@@ -244,21 +246,22 @@ F src/vtab.c e1edca38c4c4310710635bb91bb3c87fdf60f21d
F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
-F src/where.c a6e89fe7e56ab7e633be6fdebdddd857e9e5bc99
+F src/where.c 176574bfeee13775761ce56416f773b0ec115d3f
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 51756962d522e474338e9b2ebb26e7364d4aa125
-F test/alter.test 15f9224868b290d6bf7a63f31437f31aee070636
+F test/alter.test 4e47fb9ea59348b88fce4e8bb49de530128b104c
F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b
F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07
F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
-F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5
+F test/analyze.test c8cb89e8736336f1f0646c8123e6028a14c7b55e
F test/analyze2.test 8f2b1534d43f5547ce9a6b736c021d4192c75be3
F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
F test/analyze5.test adc89b92fc9fee5ca1cb0bc8512f3206ad0fe5aa
-F test/analyze6.test 1ba1aea8fad25a77ffd71f24522d1bb9ecc949fc
+F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290
+F test/analyze7.test 8095fed183e1fb999c1b1c4a1606cec1bcb13d1f
F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3
F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6
F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e
@@ -282,7 +285,7 @@ F test/backup2.test b7c69f937c912e85ac8a5dbd1e1cf290302b2d49
F test/backup_ioerr.test 1f012e692f42c0442ae652443258f70e9f20fa38
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
-F test/badutf2.test a47fda0d986d5325aa0ec2a0ebdd2d68db45e623
+F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
@@ -459,7 +462,7 @@ F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3fault.test f83e556465bb69dc8bc676339eca408dce4ca246
-F test/fts3fault2.test f275554f4a4fc7abf71e2975a9d6f4693f390526
+F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
@@ -561,7 +564,7 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
-F test/malloc_common.tcl 660b82ab528521cc4a48ff6df05ca3b6a00d47c5
+F test/malloc_common.tcl 50d0ed21eed0ae9548b58935bd29ac89a05a54fa
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
@@ -579,7 +582,7 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test 29032efcd3d826fbd409e2a7af873e7939f4a4e3
F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33
-F test/multiplex.test 92a4839213fd8cba8b59f86d42b7a1da1857db39
+F test/multiplex.test a88f3e2c16e567e72be7296195c59fbdd6a8d3d4
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723
@@ -590,7 +593,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/omitunique.test bbb2ec4345d9125d9ee21cd9488d97a163020d5f
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
-F test/oserror.test d1f085bdbac20456fccdf5877f52016453654fc3
+F test/oserror.test 6c61c859cd94864cfd6af83e0549e2800238c413
F test/pager1.test d8672fd0af5f4f9b99b06283d00f01547809bebe
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
@@ -673,6 +676,8 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 5d7a4954b0059c903f82c7b67867bc5451a7c082
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
+F test/syscall.test d1dae1fee88613cf763d97ad0038d867509e0c42
+F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test 04ba066432430657712d167ebf28080fe878d305
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
@@ -827,6 +832,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
+F test/unixexcl.test 9d80a54d86d2261f660758928959368ffc36151e
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test 29b60e8cc9e573b39676df6c4a75fe9e02d04a09
@@ -853,11 +859,11 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
-F test/wal.test f060cae4b2164c4375109a8f803873187234661d
-F test/wal2.test 57a218446654ed3e3592c925762633c1d1e85636
-F test/wal3.test ec87d9dd9e9cebabed4024064e8ff531d336ead2
+F test/wal.test 973a4747a69247a43cc03292c44f59cc76f4df65
+F test/wal2.test e561a8c6fdd1c2cd1876f3e39757934e7b7361f8
+F test/wal3.test 5c396cc22497244d627306f4c1d360167353f8dd
F test/wal4.test 3404b048fa5e10605facaf70384e6d2943412e30
-F test/wal5.test 3fef990d256cd9e95e9ad97e5dfdf3f150743fce
+F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
F test/wal6.test 07aa31ca8892d0527f2c5c5a9a2a87aa421dfaa8
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test 4df1c7369da0301caeb9a48fa45997fd592380e4
@@ -874,7 +880,7 @@ F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
-F test/where3.test c81d4ecfaed54e8aef9c1a8a90ac83c9f5c49090
+F test/where3.test 8e1175c7ef710c70502858fc4fb08d784b3620b9
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
@@ -895,7 +901,7 @@ F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
-F tool/mksqlite3c.tcl cf44512a48112b1ba09590548660a5a6877afdb3
+F tool/mksqlite3c.tcl 623e26cc8c83322e4151d3ad85ac69d41221bae8
F tool/mksqlite3h.tcl d76c226a5e8e1f3b5f6593bcabe5e98b3b1ec9ff
F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
F tool/omittest.tcl 4f4cc66bb7ca6a5b8f61ee37b6333f60fb8a746a
@@ -917,8 +923,9 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
+F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 7958cbba736a599c1293b06602eec43dfe4fd7d1
-R a5f7c9b6891187773247bee84f644851
+P b477852f82c1fddbda61fad83d55055ad8503dda a6a81d4fdafabba514e8f8e1958d6132b3850772
+R 8fa020ba89288daa4e980326f651b43c
U drh
-Z b39064b9fd85dd1a63e76912c214c45c
+Z f9a8b03fbe76a0642382439288b4d943
diff --git a/manifest.uuid b/manifest.uuid
index f8b467bbd..c526826d6 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-a6a81d4fdafabba514e8f8e1958d6132b3850772 \ No newline at end of file
+f77609d44194ee8871b3fb281ea6b90a9182f69f \ No newline at end of file
diff --git a/src/alter.c b/src/alter.c
index 1534fdf69..aa3fa929f 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -371,6 +371,22 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
}
/*
+** Parameter zName is the name of a table that is about to be altered
+** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
+** If the table is a system table, this function leaves an error message
+** in pParse->zErr (system tables may not be altered) and returns non-zero.
+**
+** Or, if zName is not a system table, zero is returned.
+*/
+static int isSystemTable(Parse *pParse, const char *zName){
+ if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
+ sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
+ return 1;
+ }
+ return 0;
+}
+
+/*
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
** command.
*/
@@ -420,14 +436,11 @@ void sqlite3AlterRenameTable(
/* Make sure it is not a system table being altered, or a reserved name
** that the table is being renamed to.
*/
- if( sqlite3Strlen30(pTab->zName)>6
- && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
- ){
- sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
+ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
goto exit_rename_table;
}
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
- goto exit_rename_table;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
+ exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
@@ -759,6 +772,9 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
goto exit_begin_add_column;
}
+ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
+ goto exit_begin_add_column;
+ }
assert( pTab->addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
diff --git a/src/analyze.c b/src/analyze.c
index 0a8339baf..231d41431 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -34,7 +34,8 @@ static void openStatTable(
Parse *pParse, /* Parsing context */
int iDb, /* The database we are looking in */
int iStatCur, /* Open the sqlite_stat1 table on this cursor */
- const char *zWhere /* Delete entries associated with this table */
+ const char *zWhere, /* Delete entries for this table or index */
+ const char *zWhereType /* Either "tbl" or "idx" */
){
static const struct {
const char *zName;
@@ -79,7 +80,7 @@ static void openStatTable(
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere
+ "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere
);
}else{
/* The sqlite_stat[12] table already exists. Delete all rows. */
@@ -103,6 +104,7 @@ static void openStatTable(
static void analyzeOneTable(
Parse *pParse, /* Parser context */
Table *pTab, /* Table whose indices are to be analyzed */
+ Index *pOnlyIdx, /* If not NULL, only analyze this one index */
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
int iMem /* Available memory locations begin here */
){
@@ -113,8 +115,7 @@ static void analyzeOneTable(
int i; /* Loop counter */
int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */
- int addr = 0; /* The address of an instruction */
- int jZeroRows = 0; /* Jump from here if number of rows is zero */
+ int jZeroRows = -1; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
@@ -125,6 +126,7 @@ static void analyzeOneTable(
int regRowid = iMem++; /* Rowid for the inserted record */
#ifdef SQLITE_ENABLE_STAT2
+ int addr = 0; /* Instruction address */
int regTemp2 = iMem++; /* Temporary use register */
int regSamplerecno = iMem++; /* Index of next sample to record */
int regRecno = iMem++; /* Current sample index */
@@ -160,9 +162,12 @@ static void analyzeOneTable(
iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int nCol = pIdx->nColumn;
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
+ int nCol;
+ KeyInfo *pKey;
+ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
+ nCol = pIdx->nColumn;
+ pKey = sqlite3IndexKeyinfo(pParse, pIdx);
if( iMem+1+(nCol*2)>pParse->nMem ){
pParse->nMem = iMem+1+(nCol*2);
}
@@ -319,7 +324,7 @@ static void analyzeOneTable(
** is never possible.
*/
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
- if( jZeroRows==0 ){
+ if( jZeroRows<0 ){
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
}
for(i=0; i<nCol; i++){
@@ -345,10 +350,10 @@ static void analyzeOneTable(
VdbeComment((v, "%s", pTab->zName));
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
+ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
}else{
- assert( jZeroRows>0 );
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, jZeroRows);
+ jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
@@ -356,9 +361,7 @@ static void analyzeOneTable(
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
if( pParse->nMem<regRec ) pParse->nMem = regRec;
- if( jZeroRows ){
- sqlite3VdbeJumpHere(v, addr);
- }
+ sqlite3VdbeJumpHere(v, jZeroRows);
}
/*
@@ -385,20 +388,21 @@ static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 2;
- openStatTable(pParse, iDb, iStatCur, 0);
+ openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
- analyzeOneTable(pParse, pTab, iStatCur, iMem);
+ analyzeOneTable(pParse, pTab, 0, iStatCur, iMem);
}
loadAnalysis(pParse, iDb);
}
/*
** Generate code that will do an analysis of a single table in
-** a database.
+** a database. If pOnlyIdx is not NULL then it is a single index
+** in pTab that should be analyzed.
*/
-static void analyzeTable(Parse *pParse, Table *pTab){
+static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
int iDb;
int iStatCur;
@@ -408,8 +412,12 @@ static void analyzeTable(Parse *pParse, Table *pTab){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 2;
- openStatTable(pParse, iDb, iStatCur, pTab->zName);
- analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1);
+ if( pOnlyIdx ){
+ openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
+ }else{
+ openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl");
+ }
+ analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1);
loadAnalysis(pParse, iDb);
}
@@ -431,6 +439,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
int i;
char *z, *zDb;
Table *pTab;
+ Index *pIdx;
Token *pTableName;
/* Read the database schema. If an error occurs, leave an error message
@@ -455,11 +464,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
}else{
z = sqlite3NameFromToken(db, pName1);
if( z ){
- pTab = sqlite3LocateTable(pParse, 0, z, 0);
- sqlite3DbFree(db, z);
- if( pTab ){
- analyzeTable(pParse, pTab);
+ if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){
+ analyzeTable(pParse, pIdx->pTable, pIdx);
+ }else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){
+ analyzeTable(pParse, pTab, 0);
}
+ sqlite3DbFree(db, z);
}
}
}else{
@@ -469,11 +479,12 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
zDb = db->aDb[iDb].zName;
z = sqlite3NameFromToken(db, pTableName);
if( z ){
- pTab = sqlite3LocateTable(pParse, 0, z, zDb);
- sqlite3DbFree(db, z);
- if( pTab ){
- analyzeTable(pParse, pTab);
+ if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
+ analyzeTable(pParse, pIdx->pTable, pIdx);
+ }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){
+ analyzeTable(pParse, pTab, 0);
}
+ sqlite3DbFree(db, z);
}
}
}
diff --git a/src/backup.c b/src/backup.c
index 5d8ea7f3f..82be9635b 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -488,7 +488,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest))
+ && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
){
rc = SQLITE_DONE;
}
@@ -502,7 +502,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( bCloseTrans ){
TESTONLY( int rc2 );
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
- TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
+ TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
assert( rc2==SQLITE_OK );
}
diff --git a/src/btree.c b/src/btree.c
index 33d746067..088c555fc 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3160,10 +3160,21 @@ static void btreeEndTransaction(Btree *p){
** the rollback journal (which causes the transaction to commit) and
** drop locks.
**
+** Normally, if an error occurs while the pager layer is attempting to
+** finalize the underlying journal file, this function returns an error and
+** the upper layer will attempt a rollback. However, if the second argument
+** is non-zero then this b-tree transaction is part of a multi-file
+** transaction. In this case, the transaction has already been committed
+** (by deleting a master journal file) and the caller will ignore this
+** functions return code. So, even if an error occurs in the pager layer,
+** reset the b-tree objects internal state to indicate that the write
+** transaction has been closed. This is quite safe, as the pager will have
+** transitioned to the error state.
+**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
-int sqlite3BtreeCommitPhaseTwo(Btree *p){
+int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
sqlite3BtreeEnter(p);
@@ -3178,7 +3189,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK && bCleanup==0 ){
sqlite3BtreeLeave(p);
return rc;
}
@@ -3198,7 +3209,7 @@ int sqlite3BtreeCommit(Btree *p){
sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeCommitPhaseTwo(p);
+ rc = sqlite3BtreeCommitPhaseTwo(p, 0);
}
sqlite3BtreeLeave(p);
return rc;
diff --git a/src/btree.h b/src/btree.h
index 6886dd944..468723b33 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -87,7 +87,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
-int sqlite3BtreeCommitPhaseTwo(Btree*);
+int sqlite3BtreeCommitPhaseTwo(Btree*, int);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*,int);
diff --git a/src/os_unix.c b/src/os_unix.c
index f4e689fa9..f04b6af27 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -371,7 +371,7 @@ static struct unix_syscall {
#else
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osFallocate ((int(*)(int,off_t,off_t)aSyscall[15].pCurrent)
+#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
}; /* End of the overrideable system calls */
@@ -444,18 +444,16 @@ static sqlite3_syscall_ptr unixGetSystemCall(
** system call.
*/
static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
- unsigned int i;
+ int i = -1;
UNUSED_PARAMETER(p);
- if( zName==0 ){
- i = -1;
- }else{
- for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0])-1; i++){
- if( strcmp(zName, aSyscall[0].zName)==0 ) break;
+ if( zName ){
+ for(i=0; i<ArraySize(aSyscall)-1; i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break;
}
}
- for(i++; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
- if( aSyscall[0].pCurrent!=0 ) return aSyscall[0].zName;
+ for(i++; i<ArraySize(aSyscall); i++){
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
}
return 0;
}
@@ -595,9 +593,22 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){
*/
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
switch (posixError) {
+#if 0
+ /* At one point this code was not commented out. In theory, this branch
+ ** should never be hit, as this function should only be called after
+ ** a locking-related function (i.e. fcntl()) has returned non-zero with
+ ** the value of errno as the first argument. Since a system call has failed,
+ ** errno should be non-zero.
+ **
+ ** Despite this, if errno really is zero, we still don't want to return
+ ** SQLITE_OK. The system call failed, and *some* SQLite error should be
+ ** propagated back to the caller. Commenting this branch out means errno==0
+ ** will be handled by the "default:" case below.
+ */
case 0:
return SQLITE_OK;
-
+#endif
+
case EAGAIN:
case ETIMEDOUT:
case EBUSY:
@@ -619,8 +630,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
case EPERM:
return SQLITE_PERM;
+ /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
+ ** this module never makes such a call. And the code in SQLite itself
+ ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
+ ** this case is also commented out. If the system does set errno to EDEADLK,
+ ** the default SQLITE_IOERR_XXX code will be returned. */
+#if 0
case EDEADLK:
return SQLITE_IOERR_BLOCKED;
+#endif
#if EOPNOTSUPP!=ENOTSUP
case EOPNOTSUPP:
@@ -1039,7 +1057,7 @@ static void closePendingFds(unixFile *pFile){
static void releaseInodeInfo(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
assert( unixMutexHeld() );
- if( pInode ){
+ if( ALWAYS(pInode) ){
pInode->nRef--;
if( pInode->nRef==0 ){
assert( pInode->pShmNode==0 );
@@ -1181,10 +1199,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
lock.l_start = RESERVED_BYTE;
lock.l_len = 1;
lock.l_type = F_WRLCK;
- if (-1 == osFcntl(pFile->h, F_GETLK, &lock)) {
- int tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
- pFile->lastErrno = tErrno;
+ if( osFcntl(pFile->h, F_GETLK, &lock) ){
+ rc = SQLITE_IOERR_CHECKRESERVEDLOCK;
+ pFile->lastErrno = errno;
} else if( lock.l_type!=F_UNLCK ){
reserved = 1;
}
@@ -1213,6 +1230,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
** This function is a pass-through to fcntl(F_SETLK) if pFile is using
** any VFS other than "unix-excl" or if pFile is opened on "unix-excl"
** and is read-only.
+**
+** Zero is returned if the call completes successfully, or -1 if a call
+** to fcntl() fails. In this case, errno is set appropriately (by fcntl()).
*/
static int unixFileLock(unixFile *pFile, struct flock *pLock){
int rc;
@@ -1309,7 +1329,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){
unixFile *pFile = (unixFile*)id;
unixInodeInfo *pInode = pFile->pInode;
struct flock lock;
- int s = 0;
int tErrno = 0;
assert( pFile );
@@ -1378,11 +1397,10 @@ static int unixLock(sqlite3_file *id, int eFileLock){
){
lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
lock.l_start = PENDING_BYTE;
- s = unixFileLock(pFile, &lock);
- if( s==(-1) ){
+ if( unixFileLock(pFile, &lock) ){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
- if( IS_LOCK_ERROR(rc) ){
+ if( rc!=SQLITE_BUSY ){
pFile->lastErrno = tErrno;
}
goto end_lock;
@@ -1396,33 +1414,31 @@ static int unixLock(sqlite3_file *id, int eFileLock){
if( eFileLock==SHARED_LOCK ){
assert( pInode->nShared==0 );
assert( pInode->eFileLock==0 );
+ assert( rc==SQLITE_OK );
/* Now get the read-lock */
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
- if( (s = unixFileLock(pFile, &lock))==(-1) ){
+ if( unixFileLock(pFile, &lock) ){
tErrno = errno;
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
}
+
/* Drop the temporary PENDING lock */
lock.l_start = PENDING_BYTE;
lock.l_len = 1L;
lock.l_type = F_UNLCK;
- if( unixFileLock(pFile, &lock)!=0 ){
- if( s != -1 ){
- /* This could happen with a network mount */
- tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
- goto end_lock;
- }
+ if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){
+ /* This could happen with a network mount */
+ tErrno = errno;
+ rc = SQLITE_IOERR_UNLOCK;
}
- if( s==(-1) ){
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
- if( IS_LOCK_ERROR(rc) ){
+
+ if( rc ){
+ if( rc!=SQLITE_BUSY ){
pFile->lastErrno = tErrno;
}
+ goto end_lock;
}else{
pFile->eFileLock = SHARED_LOCK;
pInode->nLock++;
@@ -1439,22 +1455,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){
*/
assert( 0!=pFile->eFileLock );
lock.l_type = F_WRLCK;
- switch( eFileLock ){
- case RESERVED_LOCK:
- lock.l_start = RESERVED_BYTE;
- break;
- case EXCLUSIVE_LOCK:
- lock.l_start = SHARED_FIRST;
- lock.l_len = SHARED_SIZE;
- break;
- default:
- assert(0);
+
+ assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK );
+ if( eFileLock==RESERVED_LOCK ){
+ lock.l_start = RESERVED_BYTE;
+ lock.l_len = 1L;
+ }else{
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
}
- s = unixFileLock(pFile, &lock);
- if( s==(-1) ){
+
+ if( unixFileLock(pFile, &lock) ){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
- if( IS_LOCK_ERROR(rc) ){
+ if( rc!=SQLITE_BUSY ){
pFile->lastErrno = tErrno;
}
}
@@ -1525,7 +1539,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
struct flock lock;
int rc = SQLITE_OK;
int h;
- int tErrno; /* Error code from system call errors */
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
@@ -1580,15 +1593,16 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
#endif
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
if( handleNFSUnlock ){
+ int tErrno; /* Error code from system call errors */
off_t divSize = SHARED_SIZE - 1;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = SHARED_FIRST;
lock.l_len = divSize;
- if( unixFileLock(pFile,, &lock)==(-1) ){
+ if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ rc = SQLITE_IOERR_UNLOCK;
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
@@ -1612,7 +1626,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_len = SHARED_SIZE-divSize;
if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ rc = SQLITE_IOERR_UNLOCK;
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
}
@@ -1625,12 +1639,15 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_whence = SEEK_SET;
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
- if( unixFileLock(pFile, &lock)==(-1) ){
- tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
+ if( unixFileLock(pFile, &lock) ){
+ /* In theory, the call to unixFileLock() cannot fail because another
+ ** process is holding an incompatible lock. If it does, this
+ ** indicates that the other process is not following the locking
+ ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning
+ ** SQLITE_BUSY would confuse the upper layer (in practice it causes
+ ** an assert to fail). */
+ rc = SQLITE_IOERR_RDLOCK;
+ pFile->lastErrno = errno;
goto end_unlock;
}
}
@@ -1639,14 +1656,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_whence = SEEK_SET;
lock.l_start = PENDING_BYTE;
lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
- if( unixFileLock(pFile, &lock)!=(-1) ){
+ if( unixFileLock(pFile, &lock)==0 ){
pInode->eFileLock = SHARED_LOCK;
}else{
- tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
+ rc = SQLITE_IOERR_UNLOCK;
+ pFile->lastErrno = errno;
goto end_unlock;
}
}
@@ -1663,14 +1677,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
SimulateIOErrorBenign(1);
SimulateIOError( h=(-1) )
SimulateIOErrorBenign(0);
- if( unixFileLock(pFile, &lock)!=(-1) ){
+ if( unixFileLock(pFile, &lock)==0 ){
pInode->eFileLock = NO_LOCK;
}else{
- tErrno = errno;
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
+ rc = SQLITE_IOERR_UNLOCK;
+ pFile->lastErrno = errno;
pInode->eFileLock = NO_LOCK;
pFile->eFileLock = NO_LOCK;
}
@@ -1716,29 +1727,27 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
- if( pFile ){
- if( pFile->dirfd>=0 ){
- robust_close(pFile, pFile->dirfd, __LINE__);
- pFile->dirfd=-1;
- }
- if( pFile->h>=0 ){
- robust_close(pFile, pFile->h, __LINE__);
- pFile->h = -1;
- }
+ if( pFile->dirfd>=0 ){
+ robust_close(pFile, pFile->dirfd, __LINE__);
+ pFile->dirfd=-1;
+ }
+ if( pFile->h>=0 ){
+ robust_close(pFile, pFile->h, __LINE__);
+ pFile->h = -1;
+ }
#if OS_VXWORKS
- if( pFile->pId ){
- if( pFile->isDelete ){
- unlink(pFile->pId->zCanonicalName);
- }
- vxworksReleaseFileId(pFile->pId);
- pFile->pId = 0;
+ if( pFile->pId ){
+ if( pFile->isDelete ){
+ unlink(pFile->pId->zCanonicalName);
}
-#endif
- OSTRACE(("CLOSE %-3d\n", pFile->h));
- OpenCounter(-1);
- sqlite3_free(pFile->pUnused);
- memset(pFile, 0, sizeof(unixFile));
+ vxworksReleaseFileId(pFile->pId);
+ pFile->pId = 0;
}
+#endif
+ OSTRACE(("CLOSE %-3d\n", pFile->h));
+ OpenCounter(-1);
+ sqlite3_free(pFile->pUnused);
+ memset(pFile, 0, sizeof(unixFile));
return SQLITE_OK;
}
@@ -1747,24 +1756,25 @@ static int closeUnixFile(sqlite3_file *id){
*/
static int unixClose(sqlite3_file *id){
int rc = SQLITE_OK;
- if( id ){
- unixFile *pFile = (unixFile *)id;
- unixUnlock(id, NO_LOCK);
- unixEnterMutex();
- assert( pFile->pInode==0 || pFile->pInode->nLock>0
- || pFile->pInode->bProcessLock==0 );
- if( pFile->pInode && pFile->pInode->nLock ){
- /* If there are outstanding locks, do not actually close the file just
- ** yet because that would clear those locks. Instead, add the file
- ** descriptor to pInode->pUnused list. It will be automatically closed
- ** when the last lock is cleared.
- */
- setPendingFd(pFile);
- }
- releaseInodeInfo(pFile);
- rc = closeUnixFile(id);
- unixLeaveMutex();
+ unixFile *pFile = (unixFile *)id;
+ unixUnlock(id, NO_LOCK);
+ unixEnterMutex();
+
+ /* unixFile.pInode is always valid here. Otherwise, a different close
+ ** routine (e.g. nolockClose()) would be called instead.
+ */
+ assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
+ if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pInode->pUnused list. It will be automatically closed
+ ** when the last lock is cleared.
+ */
+ setPendingFd(pFile);
}
+ releaseInodeInfo(pFile);
+ rc = closeUnixFile(id);
+ unixLeaveMutex();
return rc;
}
@@ -1979,7 +1989,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
int rc = 0;
int tErrno = errno;
if( ENOENT != tErrno ){
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ rc = SQLITE_IOERR_UNLOCK;
}
if( IS_LOCK_ERROR(rc) ){
pFile->lastErrno = tErrno;
@@ -2067,7 +2077,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
if ( lrc ) {
int tErrno = errno;
/* unlock failed with an error */
- lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
+ lrc = SQLITE_IOERR_UNLOCK;
if( IS_LOCK_ERROR(lrc) ){
pFile->lastErrno = tErrno;
rc = lrc;
@@ -2189,21 +2199,12 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) {
}
/* no, really, unlock. */
- int rc = robust_flock(pFile->h, LOCK_UN);
- if (rc) {
- int r, tErrno = errno;
- r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
- if( IS_LOCK_ERROR(r) ){
- pFile->lastErrno = tErrno;
- }
+ if( robust_flock(pFile->h, LOCK_UN) ){
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
- if( (r & SQLITE_IOERR) == SQLITE_IOERR ){
- r = SQLITE_BUSY;
- }
+ return SQLITE_OK;
#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */
-
- return r;
- } else {
+ return SQLITE_IOERR_UNLOCK;
+ }else{
pFile->eFileLock = NO_LOCK;
return SQLITE_OK;
}
@@ -3009,6 +3010,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
#else
newOffset = lseek(id->h, offset, SEEK_SET);
+ SimulateIOError( newOffset-- );
if( newOffset!=offset ){
if( newOffset == -1 ){
((unixFile*)id)->lastErrno = errno;
@@ -3377,12 +3379,16 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
if( nSize>(i64)buf.st_size ){
+
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
- int rc;
+ /* The code below is handling the return value of osFallocate()
+ ** correctly. posix_fallocate() is defined to "returns zero on success,
+ ** or an error number on failure". See the manpage for details. */
+ int err;
do{
- rc = osFallocate(pFile->.h, buf.st_size, nSize-buf.st_size;
- }while( rc<0 && errno=EINTR );
- if( rc ) return SQLITE_IOERR_WRITE;
+ err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
+ }while( err==EINTR );
+ if( err ) return SQLITE_IOERR_WRITE;
#else
/* If the OS does not have posix_fallocate(), fake it. First use
** ftruncate() to set the file size, then write a single byte to
@@ -5735,7 +5741,9 @@ static int proxyCreateUnixFile(
}
memset(pNew, 0, sizeof(unixFile));
pNew->openFlags = openFlags;
+ memset(&dummyVfs, 0, sizeof(dummyVfs));
dummyVfs.pAppData = (void*)&autolockIoFinder;
+ dummyVfs.zName = "dummy";
pUnused->fd = fd;
pUnused->flags = openFlags;
pNew->pUnused = pUnused;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index d3be1b32a..c4dc4a6ef 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2975,7 +2975,9 @@ int sqlite3_column_count(sqlite3_stmt *pStmt);
** column number. ^The leftmost column is number 0.
**
** ^The returned string pointer is valid until either the [prepared statement]
-** is destroyed by [sqlite3_finalize()] or until the next call to
+** is destroyed by [sqlite3_finalize()] or until the statement is automatically
+** reprepared by the first call to [sqlite3_step()] for a particular run
+** or until the next call to
** sqlite3_column_name() or sqlite3_column_name16() on the same column.
**
** ^If sqlite3_malloc() fails during the processing of either routine
@@ -3001,7 +3003,9 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N);
** the database name, the _table_ routines return the table name, and
** the origin_ routines return the column name.
** ^The returned string is valid until the [prepared statement] is destroyed
-** using [sqlite3_finalize()] or until the same information is requested
+** using [sqlite3_finalize()] or until the statement is automatically
+** reprepared by the first call to [sqlite3_step()] for a particular run
+** or until the same information is requested
** again in a different encoding.
**
** ^The names returned are the original un-aliased names of the
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index e77a1ee91..79ee1946b 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -3581,6 +3581,7 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
+ extern int SqlitetestSyscall_Init(Tcl_Interp*);
extern int Sqlitetestfuzzer_Init(Tcl_Interp*);
#ifdef SQLITE_ENABLE_ZIPVFS
@@ -3619,6 +3620,7 @@ static void init_all(Tcl_Interp *interp){
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
+ SqlitetestSyscall_Init(interp);
Sqlitetestfuzzer_Init(interp);
Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
diff --git a/src/test1.c b/src/test1.c
index 852ecdecc..8a0d09a71 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -4889,6 +4889,44 @@ static int file_control_chunksize_test(
}
/*
+** tclcmd: file_control_sizehint_test DB DBNAME SIZE
+**
+** This TCL command runs the sqlite3_file_control interface and
+** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and
+** SQLITE_SET_LOCKPROXYFILE verbs.
+*/
+static int file_control_sizehint_test(
+ 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_int64 nSize; /* Hinted size */
+ char *zDb; /* Db name ("main", "temp" etc.) */
+ sqlite3 *db; /* Database handle */
+ int rc; /* file_control() return code */
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)
+ || Tcl_GetWideIntFromObj(interp, objv[3], &nSize)
+ ){
+ return TCL_ERROR;
+ }
+ zDb = Tcl_GetString(objv[2]);
+ if( zDb[0]=='\0' ) zDb = NULL;
+
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize);
+ if( rc ){
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
** tclcmd: file_control_lockproxy_test DB PWD
**
** This TCL command runs the sqlite3_file_control interface and
@@ -5608,6 +5646,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_lasterrno_test", file_control_lasterrno_test, 0 },
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
+ { "file_control_sizehint_test", file_control_sizehint_test, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },
diff --git a/src/test_hexio.c b/src/test_hexio.c
index 02bd60c8a..e3258e869 100644
--- a/src/test_hexio.c
+++ b/src/test_hexio.c
@@ -312,8 +312,13 @@ static int utf8_to_utf8(
sqlite3TestBinToHex(z,nOut);
Tcl_AppendResult(interp, (char*)z, 0);
sqlite3_free(z);
-#endif
return TCL_OK;
+#else
+ Tcl_AppendResult(interp,
+ "[utf8_to_utf8] unavailable - SQLITE_DEBUG not defined", 0
+ );
+ return TCL_ERROR;
+#endif
}
static int getFts3Varint(const char *p, sqlite_int64 *v){
diff --git a/src/test_multiplex.c b/src/test_multiplex.c
index 72a71621b..d8a7db86e 100644
--- a/src/test_multiplex.c
+++ b/src/test_multiplex.c
@@ -22,7 +22,22 @@
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
-#include "sqliteInt.h"
+#include "test_multiplex.h"
+
+#ifndef SQLITE_CORE
+ #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
+#endif
+#include "sqlite3ext.h"
+
+/*
+** These should be defined to be the same as the values in
+** sqliteInt.h. They are defined seperately here so that
+** the multiplex VFS shim can be built as a loadable
+** module.
+*/
+#define UNUSED_PARAMETER(x) (void)(x)
+#define MAX_PAGE_SIZE 0x10000
+#define DEFAULT_SECTOR_SIZE 0x1000
/*
** For a build without mutexes, no-op the mutex calls.
@@ -40,14 +55,18 @@
/************************ Shim Definitions ******************************/
+#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
+
/* This is the limit on the chunk size. It may be changed by calling
-** the sqlite3_multiplex_set() interface.
+** the xFileControl() interface. It will be rounded up to a
+** multiple of MAX_PAGE_SIZE. We default it here to 1GB.
*/
-#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
+#define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384)
+
/* 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 sqlite3_multiplex_set() interface.
+** the xFileControl() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
@@ -82,6 +101,9 @@ struct multiplexGroup {
char *zName; /* Base filename of this group */
int nName; /* Length of base filename */
int flags; /* Flags used for original opening */
+ int nChunkSize; /* Chunk size used for this group */
+ int nMaxChunks; /* Max number of chunks for this group */
+ int bEnabled; /* TRUE to use Multiplex VFS for this file */
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
};
@@ -140,11 +162,6 @@ static struct {
*/
multiplexGroup *pGroups;
- /* Chunk params.
- */
- int nChunkSize;
- int nMaxChunks;
-
/* Storage for temp file names. Allocated during
** initialization to the max pathname of the underlying VFS.
*/
@@ -160,13 +177,28 @@ static struct {
static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
+/*
+** Compute a string length that is limited to what can be stored in
+** lower 30 bits of a 32-bit signed integer.
+**
+** The value returned will never be negative. Nor will it ever be greater
+** than the actual length of the string. For very long strings (greater
+** than 1GiB) the value returned might be less than the true string length.
+*/
+int multiplexStrlen30(const char *z){
+ const char *z2 = z;
+ if( z==0 ) return 0;
+ while( *z2 ){ z2++; }
+ return 0x3fffffff & (int)(z2 - z);
+}
+
/* Translate an sqlite3_file* that is really a multiplexGroup* into
** the sqlite3_file* for the underlying original VFS.
*/
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
multiplexGroup *pGroup = pConn->pGroup;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
- if( iChunk<gMultiplex.nMaxChunks ){
+ if( iChunk<pGroup->nMaxChunks ){
sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
if( !pGroup->bOpen[iChunk] ){
memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
@@ -191,6 +223,62 @@ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc,
return NULL;
}
+/*
+** This is the implementation of the multiplex_control() SQL function.
+*/
+static void multiplexControlFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int rc = SQLITE_OK;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int op;
+ int iVal;
+
+ if( !db || argc!=2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ /* extract params */
+ op = sqlite3_value_int(argv[0]);
+ iVal = sqlite3_value_int(argv[1]);
+ /* map function op to file_control op */
+ switch( op ){
+ case 1:
+ op = MULTIPLEX_CTRL_ENABLE;
+ break;
+ case 2:
+ op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
+ break;
+ case 3:
+ op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
+ break;
+ default:
+ rc = SQLITE_NOTFOUND;
+ break;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_file_control(db, 0, op, &iVal);
+ }
+ sqlite3_result_error_code(context, rc);
+}
+
+/*
+** This is the entry point to register the auto-extension for the
+** multiplex_control() function.
+*/
+static int multiplexFuncInit(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc;
+ rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
+ 0, multiplexControlFunc, 0, 0);
+ return rc;
+}
+
/************************* VFS Method Wrappers *****************************/
/*
@@ -212,7 +300,7 @@ static int multiplexOpen(
multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
sqlite3_file *pSubOpen; /* Real file descriptor */
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
- int nName = sqlite3Strlen30(zName);
+ int nName = multiplexStrlen30(zName);
int i;
int sz;
@@ -224,11 +312,11 @@ static int multiplexOpen(
multiplexEnter();
pMultiplexOpen = (multiplexConn*)pConn;
/* allocate space for group */
- sz = sizeof(multiplexGroup) /* multiplexGroup */
- + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
- + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */
- + gMultiplex.nMaxChunks /* bOpen[] */
- + nName + 1; /* zName */
+ sz = sizeof(multiplexGroup) /* multiplexGroup */
+ + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */
+ + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */
+ + SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */
+ + nName + 1; /* zName */
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
sz += SQLITE_MULTIPLEX_EXT_SZ;
assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
@@ -244,14 +332,18 @@ static int multiplexOpen(
char *p = (char *)&pGroup[1];
pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
+ pGroup->bEnabled = -1;
+ pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
+ pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
pGroup->pReal = (sqlite3_file **)p;
- p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
- for(i=0; i<gMultiplex.nMaxChunks; i++){
+ p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
+ for(i=0; i<pGroup->nMaxChunks; i++){
pGroup->pReal[i] = (sqlite3_file *)p;
p += pOrigVfs->szOsFile;
}
+ /* bOpen[] vals should all be zero from memset above */
pGroup->bOpen = p;
- p += gMultiplex.nMaxChunks;
+ p += pGroup->nMaxChunks;
pGroup->zName = p;
/* save off base filename, name length, and original open flags */
memcpy(pGroup->zName, zName, nName+1);
@@ -259,6 +351,14 @@ static int multiplexOpen(
pGroup->flags = flags;
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
if( pSubOpen ){
+ /* if this file is already larger than chunk size, disable
+ ** the multiplex feature.
+ */
+ sqlite3_int64 sz;
+ int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
+ pGroup->bEnabled = 0;
+ }
if( pSubOpen->pMethods->iVersion==1 ){
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
}else{
@@ -288,24 +388,29 @@ static int multiplexDelete(
){
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
int rc = SQLITE_OK;
- int nName = sqlite3Strlen30(zName);
+ int nName = multiplexStrlen30(zName);
int i;
UNUSED_PARAMETER(pVfs);
multiplexEnter();
memcpy(gMultiplex.zName, zName, nName+1);
- for(i=0; i<gMultiplex.nMaxChunks; i++){
+ for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){
int rc2;
int exists = 0;
if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#else
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+nName,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
}
- rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
+ rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
+ SQLITE_ACCESS_EXISTS, &exists);
if( rc2==SQLITE_OK && exists){
/* if it exists, delete it */
rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
@@ -367,7 +472,7 @@ static int multiplexClose(sqlite3_file *pConn){
int i;
multiplexEnter();
/* close any open handles */
- for(i=0; i<gMultiplex.nMaxChunks; i++){
+ for(i=0; i<pGroup->nMaxChunks; i++){
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
@@ -398,23 +503,29 @@ static int multiplexRead(
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
- while( iAmt > 0 ){
- int i = (int)(iOfst/gMultiplex.nChunkSize);
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
- if( pSubOpen ){
- int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
- if( extra<0 ) extra = 0;
- iAmt -= extra;
- rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
- if( rc!=SQLITE_OK ) break;
- pBuf = (char *)pBuf + iAmt;
- iOfst += iAmt;
- iAmt = extra;
- }else{
- rc = SQLITE_IOERR_READ;
- break;
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
+ }else{
+ while( iAmt > 0 ){
+ int i = (int)(iOfst / pGroup->nChunkSize);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_READ;
+ break;
+ }
}
}
multiplexLeave();
@@ -432,23 +543,29 @@ static int multiplexWrite(
sqlite3_int64 iOfst
){
multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
- while( iAmt > 0 ){
- int i = (int)(iOfst/gMultiplex.nChunkSize);
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
- if( pSubOpen ){
- int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
- if( extra<0 ) extra = 0;
- iAmt -= extra;
- rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
- if( rc!=SQLITE_OK ) break;
- pBuf = (char *)pBuf + iAmt;
- iOfst += iAmt;
- iAmt = extra;
- }else{
- rc = SQLITE_IOERR_WRITE;
- break;
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
+ }else{
+ while( iAmt > 0 ){
+ int i = (int)(iOfst / pGroup->nChunkSize);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_WRITE;
+ break;
+ }
}
}
multiplexLeave();
@@ -463,35 +580,44 @@ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
- int rc2;
- int i;
- sqlite3_file *pSubOpen;
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
multiplexEnter();
- memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
- /* delete the chunks above the truncate limit */
- for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
- /* close any open chunks before deleting them */
- if( pGroup->bOpen[i] ){
- pSubOpen = pGroup->pReal[i];
- rc2 = pSubOpen->pMethods->xClose(pSubOpen);
- if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
- pGroup->bOpen[i] = 0;
- }
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
+ }else{
+ int rc2;
+ int i;
+ sqlite3_file *pSubOpen;
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
+ /* delete the chunks above the truncate limit */
+ for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){
+ /* close any open chunks before deleting them */
+ if( pGroup->bOpen[i] ){
+ pSubOpen = pGroup->pReal[i];
+ rc2 = pSubOpen->pMethods->xClose(pSubOpen);
+ if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
+ pGroup->bOpen[i] = 0;
+ }
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#else
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+pGroup->nName,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
- rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
- if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
- }
- pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
- if( pSubOpen ){
- rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
- if( rc2!=SQLITE_OK ) rc = rc2;
- }else{
- rc = SQLITE_IOERR_TRUNCATE;
+ rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
+ if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
+ }
+ pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL);
+ if( pSubOpen ){
+ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
+ if( rc2!=SQLITE_OK ) rc = rc2;
+ }else{
+ rc = SQLITE_IOERR_TRUNCATE;
+ }
}
multiplexLeave();
return rc;
@@ -505,7 +631,7 @@ static int multiplexSync(sqlite3_file *pConn, int flags){
int rc = SQLITE_OK;
int i;
multiplexEnter();
- for(i=0; i<gMultiplex.nMaxChunks; i++){
+ for(i=0; i<pGroup->nMaxChunks; i++){
/* if we don't have it open, we don't need to sync it */
if( pGroup->bOpen[i] ){
sqlite3_file *pSubOpen = pGroup->pReal[i];
@@ -527,46 +653,56 @@ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
int rc2;
int i;
multiplexEnter();
- *pSize = 0;
- for(i=0; i<gMultiplex.nMaxChunks; i++){
- sqlite3_file *pSubOpen = NULL;
- /* if not opened already, check to see if the chunk exists */
- if( pGroup->bOpen[i] ){
- pSubOpen = pGroup->pReal[i];
- }else{
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
- int exists = 0;
- memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
- if( i ){
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
+ }else{
+ *pSize = 0;
+ for(i=0; i<pGroup->nMaxChunks; i++){
+ sqlite3_file *pSubOpen = NULL;
+ /* if not opened already, check to see if the chunk exists */
+ if( pGroup->bOpen[i] ){
+ pSubOpen = pGroup->pReal[i];
+ }else{
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ int exists = 0;
+ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
+ if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#else
- sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
+ sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1,
+ gMultiplex.zName+pGroup->nName,
+ SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
+ }
+ rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName,
+ SQLITE_ACCESS_EXISTS, &exists);
+ if( rc2==SQLITE_OK && exists){
+ /* if it exists, open it */
+ pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ }else{
+ /* stop at first "gap" */
+ break;
+ }
}
- rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
- if( rc2==SQLITE_OK && exists){
- /* if it exists, open it */
- pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ if( pSubOpen ){
+ sqlite3_int64 sz;
+ rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( rc2!=SQLITE_OK ){
+ rc = rc2;
+ }else{
+ if( sz>pGroup->nChunkSize ){
+ rc = SQLITE_IOERR_FSTAT;
+ }
+ *pSize += sz;
+ }
}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>gMultiplex.nChunkSize ){
- rc = SQLITE_IOERR_FSTAT;
- }
- *pSize += sz;
- }
- }else{
- break;
- }
}
multiplexLeave();
return rc;
@@ -608,18 +744,62 @@ static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
return SQLITE_IOERR_CHECKRESERVEDLOCK;
}
-/* Pass xFileControl requests through to the original VFS unchanged.
+/* Pass xFileControl requests through to the original VFS unchanged,
+** except for any MULTIPLEX_CTRL_* requests here.
*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
multiplexConn *p = (multiplexConn*)pConn;
- int rc;
+ multiplexGroup *pGroup = p->pGroup;
+ int rc = SQLITE_ERROR;
sqlite3_file *pSubOpen;
- if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
- pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
- if( pSubOpen ){
- return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
+
+ if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
+ switch( op ){
+ case MULTIPLEX_CTRL_ENABLE:
+ if( pArg ) {
+ int bEnabled = *(int *)pArg;
+ pGroup->bEnabled = bEnabled;
+ rc = SQLITE_OK;
+ }
+ break;
+ case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
+ if( pArg ) {
+ int nChunkSize = *(int *)pArg;
+ if( nChunkSize<1 ){
+ rc = SQLITE_MISUSE;
+ }else{
+ /* Round up to nearest multiple of MAX_PAGE_SIZE. */
+ nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1));
+ nChunkSize &= ~(MAX_PAGE_SIZE-1);
+ pGroup->nChunkSize = nChunkSize;
+ rc = SQLITE_OK;
+ }
+ }
+ break;
+ case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
+ if( pArg ) {
+ int nMaxChunks = *(int *)pArg;
+ if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){
+ rc = SQLITE_MISUSE;
+ }else{
+ pGroup->nMaxChunks = nMaxChunks;
+ rc = SQLITE_OK;
+ }
+ }
+ break;
+ case SQLITE_FCNTL_SIZE_HINT:
+ case SQLITE_FCNTL_CHUNK_SIZE:
+ /* no-op these */
+ rc = SQLITE_OK;
+ break;
+ default:
+ pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
+ }
+ break;
}
- return SQLITE_ERROR;
+ return rc;
}
/* Pass xSectorSize requests through to the original VFS unchanged.
@@ -631,7 +811,7 @@ static int multiplexSectorSize(sqlite3_file *pConn){
if( pSubOpen ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
- return SQLITE_DEFAULT_SECTOR_SIZE;
+ return DEFAULT_SECTOR_SIZE;
}
/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
@@ -706,9 +886,10 @@ static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
/************************** Public Interfaces *****************************/
/*
-** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName
-** as the VFS that does the actual work. Use the default if
-** zOrigVfsName==NULL.
+** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
+**
+** Use the VFS named zOrigVfsName as the VFS that does the actual work.
+** Use the default if zOrigVfsName==NULL.
**
** The multiplex VFS shim is named "multiplex". It will become the default
** VFS if makeDefault is non-zero.
@@ -731,14 +912,12 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
sqlite3_mutex_free(gMultiplex.pMutex);
return SQLITE_NOMEM;
}
- gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
- gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
gMultiplex.pGroups = NULL;
gMultiplex.isInitialized = 1;
gMultiplex.pOrigVfs = pOrigVfs;
gMultiplex.sThisVfs = *pOrigVfs;
gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
- gMultiplex.sThisVfs.zName = "multiplex";
+ gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
gMultiplex.sThisVfs.xOpen = multiplexOpen;
gMultiplex.sThisVfs.xDelete = multiplexDelete;
gMultiplex.sThisVfs.xAccess = multiplexAccess;
@@ -773,11 +952,14 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
+
+ sqlite3_auto_extension((void*)multiplexFuncInit);
+
return SQLITE_OK;
}
/*
-** Shutdown the multiplex system.
+** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
**
** All SQLite database connections must be closed before calling this
** routine.
@@ -796,31 +978,9 @@ int sqlite3_multiplex_shutdown(void){
return SQLITE_OK;
}
-/*
-** Adjust chunking params. VFS should be initialized first.
-** No files should be open. Re-intializing will reset these
-** to the default.
-*/
-int sqlite3_multiplex_set(
- int nChunkSize, /* Max chunk size */
- int nMaxChunks /* Max number of chunks */
-){
- if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
- if( gMultiplex.pGroups ) return SQLITE_MISUSE;
- if( nChunkSize<32 ) return SQLITE_MISUSE;
- if( nMaxChunks<1 ) return SQLITE_MISUSE;
- if( nMaxChunks>99 ) return SQLITE_MISUSE;
- multiplexEnter();
- gMultiplex.nChunkSize = nChunkSize;
- gMultiplex.nMaxChunks = nMaxChunks;
- multiplexLeave();
- return SQLITE_OK;
-}
-
/***************************** Test Code ***********************************/
#ifdef SQLITE_TEST
#include <tcl.h>
-
extern const char *sqlite3TestErrorName(int);
@@ -881,36 +1041,6 @@ static int test_multiplex_shutdown(
}
/*
-** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
-*/
-static int test_multiplex_set(
- void * clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *CONST objv[]
-){
- int nChunkSize; /* Max chunk size */
- int nMaxChunks; /* Max number of chunks */
- int rc; /* Value returned by sqlite3_multiplex_set() */
-
- UNUSED_PARAMETER(clientData);
-
- /* Process arguments */
- if( objc!=3 ){
- Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
- return TCL_ERROR;
- }
- if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
- if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
-
- /* Invoke sqlite3_multiplex_set() */
- rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
-
- Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
- return TCL_OK;
-}
-
-/*
** tclcmd: sqlite3_multiplex_dump
*/
static int test_multiplex_dump(
@@ -943,16 +1073,16 @@ static int test_multiplex_dump(
Tcl_NewIntObj(pGroup->flags));
/* count number of chunks with open handles */
- for(i=0; i<gMultiplex.nMaxChunks; i++){
+ for(i=0; i<pGroup->nMaxChunks; i++){
if( pGroup->bOpen[i] ) nChunks++;
}
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewIntObj(nChunks));
Tcl_ListObjAppendElement(interp, pGroupTerm,
- Tcl_NewIntObj(gMultiplex.nChunkSize));
+ Tcl_NewIntObj(pGroup->nChunkSize));
Tcl_ListObjAppendElement(interp, pGroupTerm,
- Tcl_NewIntObj(gMultiplex.nMaxChunks));
+ Tcl_NewIntObj(pGroup->nMaxChunks));
Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
}
@@ -962,6 +1092,68 @@ static int test_multiplex_dump(
}
/*
+** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
+*/
+static int test_multiplex_control(
+ ClientData cd,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc; /* Return code from file_control() */
+ int idx; /* Index in aSub[] */
+ Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */
+ sqlite3 *db; /* Underlying db handle for HANDLE */
+ int iValue = 0;
+ void *pArg = 0;
+
+ struct SubCommand {
+ const char *zName;
+ int op;
+ int argtype;
+ } aSub[] = {
+ { "enable", MULTIPLEX_CTRL_ENABLE, 1 },
+ { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 },
+ { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 },
+ { 0, 0, 0 }
+ };
+
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
+ return TCL_ERROR;
+ }
+
+ if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
+ Tcl_AppendResult(interp, "expected database handle, got \"", 0);
+ Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
+ return TCL_ERROR;
+ }else{
+ db = *(sqlite3 **)cmdInfo.objClientData;
+ }
+
+ rc = Tcl_GetIndexFromObjStruct(
+ interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
+ );
+ if( rc!=TCL_OK ) return rc;
+
+ switch( aSub[idx].argtype ){
+ case 1:
+ if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
+ return TCL_ERROR;
+ }
+ pArg = (void *)&iValue;
+ break;
+ default:
+ Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
+ return TCL_ERROR;
+ }
+
+ rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
+ return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
+}
+
+/*
** This routine registers the custom TCL commands defined in this
** module. This should be the only procedure visible from outside
** of this module.
@@ -973,8 +1165,8 @@ int Sqlitemultiplex_Init(Tcl_Interp *interp){
} aCmd[] = {
{ "sqlite3_multiplex_initialize", test_multiplex_initialize },
{ "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
- { "sqlite3_multiplex_set", test_multiplex_set },
{ "sqlite3_multiplex_dump", test_multiplex_dump },
+ { "sqlite3_multiplex_control", test_multiplex_control },
};
int i;
diff --git a/src/test_multiplex.h b/src/test_multiplex.h
new file mode 100644
index 000000000..ec1ba9bb2
--- /dev/null
+++ b/src/test_multiplex.h
@@ -0,0 +1,91 @@
+/*
+** 2011 March 18
+**
+** 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 a VFS "shim" - a layer that sits in between the
+** pager and the real VFS.
+**
+** This particular shim enforces a multiplex system on DB files.
+** This shim shards/partitions a single DB file into smaller
+** "chunks" such that the total DB file size may exceed the maximum
+** file size of the underlying file system.
+**
+*/
+
+#ifndef _TEST_MULTIPLEX_H
+#define _TEST_MULTIPLEX_H
+
+/*
+** CAPI: File-control Operations Supported by Multiplex VFS
+**
+** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle.
+**
+** MULTIPLEX_CTRL_ENABLE:
+** This file control is used to enable or disable the multiplex
+** shim.
+**
+** MULTIPLEX_CTRL_SET_CHUNK_SIZE:
+** This file control is used to set the maximum allowed chunk
+** size for a multiplex file set. The chunk size should be
+** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up
+** if not.
+**
+** MULTIPLEX_CTRL_SET_MAX_CHUNKS:
+** This file control is used to set the maximum number of chunks
+** allowed to be used for a mutliplex file set.
+*/
+#define MULTIPLEX_CTRL_ENABLE 214014
+#define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015
+#define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016
+
+/*
+** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
+**
+** Use the VFS named zOrigVfsName as the VFS that does the actual work.
+** Use the default if zOrigVfsName==NULL.
+**
+** The multiplex VFS shim is named "multiplex". It will become the default
+** VFS if makeDefault is non-zero.
+**
+** An auto-extension is registered which will make the function
+** multiplex_control() available to database connections. This
+** function gives access to the xFileControl interface of the
+** multiplex VFS shim.
+**
+** SELECT multiplex_control(<op>,<val>);
+**
+** <op>=1 MULTIPLEX_CTRL_ENABLE
+** <val>=0 disable
+** <val>=1 enable
+**
+** <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE
+** <val> int, chunk size
+**
+** <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS
+** <val> int, max chunks
+**
+** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
+** during start-up.
+*/
+extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault);
+
+/*
+** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
+**
+** 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 multiplex groups.
+*/
+extern int sqlite3_multiplex_shutdown(void);
+
+#endif
diff --git a/src/test_syscall.c b/src/test_syscall.c
new file mode 100644
index 000000000..a757e66a5
--- /dev/null
+++ b/src/test_syscall.c
@@ -0,0 +1,654 @@
+/*
+** 2011 March 28
+**
+** 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.
+**
+*************************************************************************
+**
+** The code in this file implements a Tcl interface used to test error
+** handling in the os_unix.c module. Wrapper functions that support fault
+** injection are registered as the low-level OS functions using the
+** xSetSystemCall() method of the VFS. The Tcl interface is as follows:
+**
+**
+** test_syscall install LIST
+** Install wrapper functions for all system calls in argument LIST.
+** LIST must be a list consisting of zero or more of the following
+** literal values:
+**
+** open close access getcwd stat fstat
+** ftruncate fcntl read pread pread64 write
+** pwrite pwrite64 fchmod fallocate
+**
+** test_syscall uninstall
+** Uninstall all wrapper functions.
+**
+** test_syscall fault ?COUNT PERSIST?
+** If [test_syscall fault] is invoked without the two arguments, fault
+** injection is disabled. Otherwise, fault injection is configured to
+** cause a failure on the COUNT'th next call to a system call with a
+** wrapper function installed. A COUNT value of 1 means fail the next
+** system call.
+**
+** Argument PERSIST is interpreted as a boolean. If true, the all
+** system calls following the initial failure also fail. Otherwise, only
+** the single transient failure is injected.
+**
+** test_syscall errno CALL ERRNO
+** Set the value that the global "errno" is set to following a fault
+** in call CALL. Argument CALL must be one of the system call names
+** listed above (under [test_syscall install]). ERRNO is a symbolic
+** name (i.e. "EACCES"). Not all errno codes are supported. Add extra
+** to the aErrno table in function test_syscall_errno() below as
+** required.
+**
+** test_syscall reset ?SYSTEM-CALL?
+** With no argument, this is an alias for the [uninstall] command. However,
+** this command uses a VFS call of the form:
+**
+** xSetSystemCall(pVfs, 0, 0);
+**
+** To restore the default system calls. The [uninstall] command restores
+** each system call individually by calling (i.e.):
+**
+** xSetSystemCall(pVfs, "open", 0);
+**
+** With an argument, this command attempts to reset the system call named
+** by the parameter using the same method as [uninstall].
+**
+** test_syscall exists SYSTEM-CALL
+** Return true if the named system call exists. Or false otherwise.
+**
+** test_syscall list
+** Return a list of all system calls. The list is constructed using
+** the xNextSystemCall() VFS method.
+*/
+
+#include "sqlite3.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef SQLITE_OS_UNIX
+
+/* From test1.c */
+extern const char *sqlite3TestErrorName(int);
+
+#include <sys/types.h>
+#include <errno.h>
+
+static struct TestSyscallGlobal {
+ int bPersist; /* 1 for persistent errors, 0 for transient */
+ int nCount; /* Fail after this many more calls */
+ int nFail; /* Number of failures that have occurred */
+} gSyscall = { 0, 0 };
+
+static int ts_open(const char *, int, int);
+static int ts_close(int fd);
+static int ts_access(const char *zPath, int mode);
+static char *ts_getcwd(char *zPath, size_t nPath);
+static int ts_stat(const char *zPath, struct stat *p);
+static int ts_fstat(int fd, struct stat *p);
+static int ts_ftruncate(int fd, off_t n);
+static int ts_fcntl(int fd, int cmd, ... );
+static int ts_read(int fd, void *aBuf, size_t nBuf);
+static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
+static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off);
+static int ts_write(int fd, const void *aBuf, size_t nBuf);
+static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
+static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off);
+static int ts_fchmod(int fd, mode_t mode);
+static int ts_fallocate(int fd, off_t off, off_t len);
+
+
+struct TestSyscallArray {
+ const char *zName;
+ sqlite3_syscall_ptr xTest;
+ sqlite3_syscall_ptr xOrig;
+ int default_errno; /* Default value for errno following errors */
+ int custom_errno; /* Current value for errno if error */
+} aSyscall[] = {
+ /* 0 */ { "open", (sqlite3_syscall_ptr)ts_open, 0, EACCES, 0 },
+ /* 1 */ { "close", (sqlite3_syscall_ptr)ts_close, 0, 0, 0 },
+ /* 2 */ { "access", (sqlite3_syscall_ptr)ts_access, 0, 0, 0 },
+ /* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 },
+ /* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 },
+ /* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 },
+ /* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 },
+ /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, EACCES, 0 },
+ /* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 },
+ /* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 },
+ /* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 },
+ /* 11 */ { "write", (sqlite3_syscall_ptr)ts_write, 0, 0, 0 },
+ /* 12 */ { "pwrite", (sqlite3_syscall_ptr)ts_pwrite, 0, 0, 0 },
+ /* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 },
+ /* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 },
+ /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+#define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig)
+#define orig_close ((int(*)(int))aSyscall[1].xOrig)
+#define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig)
+#define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig)
+#define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig)
+#define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig)
+#define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig)
+#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig)
+#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig)
+#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig)
+#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig)
+#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig)
+#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
+ aSyscall[12].xOrig)
+#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
+ aSyscall[13].xOrig)
+#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig)
+#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
+
+/*
+** This function is called exactly once from within each invocation of a
+** system call wrapper in this file. It returns 1 if the function should
+** fail, or 0 if it should succeed.
+*/
+static int tsIsFail(void){
+ gSyscall.nCount--;
+ if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){
+ gSyscall.nFail++;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Return the current error-number value for function zFunc. zFunc must be
+** the name of a system call in the aSyscall[] table.
+**
+** Usually, the current error-number is the value that errno should be set
+** to if the named system call fails. The exception is "fallocate". See
+** comments above the implementation of ts_fallocate() for details.
+*/
+static int tsErrno(const char *zFunc){
+ int i;
+ int nFunc = strlen(zFunc);
+ for(i=0; aSyscall[i].zName; i++){
+ if( strlen(aSyscall[i].zName)!=nFunc ) continue;
+ if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
+ return aSyscall[i].custom_errno;
+ }
+
+ assert(0);
+ return 0;
+}
+
+/*
+** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the
+** value of errno before returning.
+*/
+static int tsIsFailErrno(const char *zFunc){
+ if( tsIsFail() ){
+ errno = tsErrno(zFunc);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** A wrapper around open().
+*/
+static int ts_open(const char *zFile, int flags, int mode){
+ if( tsIsFailErrno("open") ){
+ return -1;
+ }
+ return orig_open(zFile, flags, mode);
+}
+
+/*
+** A wrapper around close().
+*/
+static int ts_close(int fd){
+ if( tsIsFail() ){
+ /* Even if simulating an error, close the original file-descriptor.
+ ** This is to stop the test process from running out of file-descriptors
+ ** when running a long test. If a call to close() appears to fail, SQLite
+ ** never attempts to use the file-descriptor afterwards (or even to close
+ ** it a second time). */
+ orig_close(fd);
+ return -1;
+ }
+ return orig_close(fd);
+}
+
+/*
+** A wrapper around access().
+*/
+static int ts_access(const char *zPath, int mode){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_access(zPath, mode);
+}
+
+/*
+** A wrapper around getcwd().
+*/
+static char *ts_getcwd(char *zPath, size_t nPath){
+ if( tsIsFail() ){
+ return NULL;
+ }
+ return orig_getcwd(zPath, nPath);
+}
+
+/*
+** A wrapper around stat().
+*/
+static int ts_stat(const char *zPath, struct stat *p){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_stat(zPath, p);
+}
+
+/*
+** A wrapper around fstat().
+*/
+static int ts_fstat(int fd, struct stat *p){
+ if( tsIsFailErrno("fstat") ){
+ return -1;
+ }
+ return orig_fstat(fd, p);
+}
+
+/*
+** A wrapper around ftruncate().
+*/
+static int ts_ftruncate(int fd, off_t n){
+ if( tsIsFailErrno("ftruncate") ){
+ return -1;
+ }
+ return orig_ftruncate(fd, n);
+}
+
+/*
+** A wrapper around fcntl().
+*/
+static int ts_fcntl(int fd, int cmd, ... ){
+ va_list ap;
+ void *pArg;
+ if( tsIsFailErrno("fcntl") ){
+ return -1;
+ }
+ va_start(ap, cmd);
+ pArg = va_arg(ap, void *);
+ return orig_fcntl(fd, cmd, pArg);
+}
+
+/*
+** A wrapper around read().
+*/
+static int ts_read(int fd, void *aBuf, size_t nBuf){
+ if( tsIsFailErrno("read") ){
+ return -1;
+ }
+ return orig_read(fd, aBuf, nBuf);
+}
+
+/*
+** A wrapper around pread().
+*/
+static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFailErrno("pread") ){
+ return -1;
+ }
+ return orig_pread(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around pread64().
+*/
+static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFailErrno("pread64") ){
+ return -1;
+ }
+ return orig_pread64(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around write().
+*/
+static int ts_write(int fd, const void *aBuf, size_t nBuf){
+ if( tsIsFailErrno("write") ){
+ return -1;
+ }
+ return orig_write(fd, aBuf, nBuf);
+}
+
+/*
+** A wrapper around pwrite().
+*/
+static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFailErrno("pwrite") ){
+ return -1;
+ }
+ return orig_pwrite(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around pwrite64().
+*/
+static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFailErrno("pwrite64") ){
+ return -1;
+ }
+ return orig_pwrite64(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around fchmod().
+*/
+static int ts_fchmod(int fd, mode_t mode){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_fchmod(fd, mode);
+}
+
+/*
+** A wrapper around fallocate().
+**
+** SQLite assumes that the fallocate() function is compatible with
+** posix_fallocate(). According to the Linux man page (2009-09-30):
+**
+** posix_fallocate() returns zero on success, or an error number on
+** failure. Note that errno is not set.
+*/
+static int ts_fallocate(int fd, off_t off, off_t len){
+ if( tsIsFail() ){
+ return tsErrno("fallocate");
+ }
+ return orig_fallocate(fd, off, len);
+}
+
+static int test_syscall_install(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int nElem;
+ int i;
+ Tcl_Obj **apElem;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST");
+ return TCL_ERROR;
+ }
+ if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
+ return TCL_ERROR;
+ }
+ pVfs = sqlite3_vfs_find(0);
+
+ for(i=0; i<nElem; i++){
+ int iCall;
+ int rc = Tcl_GetIndexFromObjStruct(interp,
+ apElem[i], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
+ );
+ if( rc ) return rc;
+ if( aSyscall[iCall].xOrig==0 ){
+ aSyscall[iCall].xOrig = pVfs->xGetSystemCall(pVfs, aSyscall[iCall].zName);
+ pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest);
+ }
+ aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno;
+ }
+
+ return TCL_OK;
+}
+
+static int test_syscall_uninstall(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int i;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ for(i=0; aSyscall[i].zName; i++){
+ if( aSyscall[i].xOrig ){
+ pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0);
+ aSyscall[i].xOrig = 0;
+ }
+ }
+ return TCL_OK;
+}
+
+static int test_syscall_reset(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int i;
+ int rc;
+
+ if( objc!=2 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ if( objc==2 ){
+ rc = pVfs->xSetSystemCall(pVfs, 0, 0);
+ for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0;
+ }else{
+ int nFunc;
+ char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc);
+ rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0);
+ for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){
+ if( strlen(aSyscall[i].zName)!=nFunc ) continue;
+ if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
+ aSyscall[i].xOrig = 0;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1));
+ return TCL_ERROR;
+ }
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+static int test_syscall_exists(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ sqlite3_syscall_ptr x;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2]));
+
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0));
+ return TCL_OK;
+}
+
+static int test_syscall_fault(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nCount = 0;
+ int bPersist = 0;
+
+ if( objc!=2 && objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?");
+ return TCL_ERROR;
+ }
+
+ if( objc==4 ){
+ if( Tcl_GetIntFromObj(interp, objv[2], &nCount)
+ || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist)
+ ){
+ return TCL_ERROR;
+ }
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail));
+ gSyscall.nCount = nCount;
+ gSyscall.bPersist = bPersist;
+ gSyscall.nFail = 0;
+ return TCL_OK;
+}
+
+static int test_syscall_errno(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int iCall;
+ int iErrno;
+ int rc;
+
+ struct Errno {
+ const char *z;
+ int i;
+ } aErrno[] = {
+ { "EACCES", EACCES },
+ { "EINTR", EINTR },
+ { "EIO", EIO },
+ { "EOVERFLOW", EOVERFLOW },
+ { "ENOMEM", ENOMEM },
+ { "EAGAIN", EAGAIN },
+ { "ETIMEDOUT", ETIMEDOUT },
+ { "EBUSY", EBUSY },
+ { "EPERM", EPERM },
+ { "EDEADLK", EDEADLK },
+ { "ENOLCK", ENOLCK },
+ { 0, 0 }
+ };
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO");
+ return TCL_ERROR;
+ }
+
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
+ );
+ if( rc!=TCL_OK ) return rc;
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno
+ );
+ if( rc!=TCL_OK ) return rc;
+
+ aSyscall[iCall].custom_errno = aErrno[iErrno].i;
+ return TCL_OK;
+}
+
+static int test_syscall_list(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zSys;
+ sqlite3_vfs *pVfs;
+ Tcl_Obj *pList;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ pList = Tcl_NewObj();
+ Tcl_IncrRefCount(pList);
+ for(zSys = pVfs->xNextSystemCall(pVfs, 0);
+ zSys!=0;
+ zSys = pVfs->xNextSystemCall(pVfs, zSys)
+ ){
+ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1));
+ }
+
+ Tcl_SetObjResult(interp, pList);
+ Tcl_DecrRefCount(pList);
+ return TCL_OK;
+}
+
+static int test_syscall(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ struct SyscallCmd {
+ const char *zName;
+ Tcl_ObjCmdProc *xCmd;
+ } aCmd[] = {
+ { "fault", test_syscall_fault },
+ { "install", test_syscall_install },
+ { "uninstall", test_syscall_uninstall },
+ { "reset", test_syscall_reset },
+ { "errno", test_syscall_errno },
+ { "exists", test_syscall_exists },
+ { "list", test_syscall_list },
+ { 0, 0 }
+ };
+ int iCmd;
+ int rc;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
+ return TCL_ERROR;
+ }
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
+ );
+ if( rc!=TCL_OK ) return rc;
+ return aCmd[iCmd].xCmd(clientData, interp, objc, objv);
+}
+
+int SqlitetestSyscall_Init(Tcl_Interp *interp){
+ struct SyscallCmd {
+ const char *zName;
+ Tcl_ObjCmdProc *xCmd;
+ } aCmd[] = {
+ { "test_syscall", test_syscall},
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0);
+ }
+ return TCL_OK;
+}
+#else
+int SqlitetestSyscall_Init(Tcl_Interp *interp){
+ return TCL_OK;
+}
+#endif
+
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 36e0d260b..7cab12f78 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -559,6 +559,7 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
+ assert( addr>=0 );
sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -1703,7 +1704,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- rc = sqlite3BtreeCommitPhaseTwo(pBt);
+ rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
if( rc==SQLITE_OK ){
@@ -1835,7 +1836,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- sqlite3BtreeCommitPhaseTwo(pBt);
+ sqlite3BtreeCommitPhaseTwo(pBt, 1);
}
}
sqlite3EndBenignMalloc();
diff --git a/src/where.c b/src/where.c
index cdcaa98c0..5d8b8f4ad 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2942,7 +2942,7 @@ static void bestBtreeIndex(
if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
- testcase( pFirstTerm->pOperator==WO_ISNULL );
+ testcase( pFirstTerm->eOperator==WO_ISNULL );
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
}else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
diff --git a/test/alter.test b/test/alter.test
index bf7cf0062..d4b72a6ae 100644
--- a/test/alter.test
+++ b/test/alter.test
@@ -840,4 +840,23 @@ do_test alter-14.2 {
} {1 {Cannot add a PRIMARY KEY column}}
+#-------------------------------------------------------------------------
+# Test that it is not possible to use ALTER TABLE on any system table.
+#
+set system_table_list {1 sqlite_master}
+catchsql ANALYZE
+ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
+ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 }
+
+foreach {tn tbl} $system_table_list {
+ do_test alter-15.$tn.1 {
+ catchsql "ALTER TABLE $tbl RENAME TO xyz"
+ } [list 1 "table $tbl may not be altered"]
+
+ do_test alter-15.$tn.2 {
+ catchsql "ALTER TABLE $tbl ADD COLUMN xyz"
+ } [list 1 "table $tbl may not be altered"]
+}
+
+
finish_test
diff --git a/test/analyze.test b/test/analyze.test
index 177936c22..766cd50d1 100644
--- a/test/analyze.test
+++ b/test/analyze.test
@@ -96,7 +96,7 @@ do_test analyze-1.11 {
execsql {
SELECT * FROM sqlite_stat1
}
-} {t1 {} 0}
+} {}
do_test analyze-1.12 {
catchsql {
ANALYZE t1;
@@ -106,7 +106,7 @@ do_test analyze-1.13 {
execsql {
SELECT * FROM sqlite_stat1
}
-} {t1 {} 0}
+} {}
# Create some indices that can be analyzed. But do not yet add
# data. Without data in the tables, no analysis is done.
@@ -117,21 +117,21 @@ do_test analyze-2.1 {
ANALYZE main.t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
-} {t1 {} 0}
+} {}
do_test analyze-2.2 {
execsql {
CREATE INDEX t1i2 ON t1(b);
ANALYZE t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
-} {t1 {} 0}
+} {}
do_test analyze-2.3 {
execsql {
CREATE INDEX t1i3 ON t1(a,b);
ANALYZE main;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
-} {t1 {} 0}
+} {}
# Start adding data to the table. Verify that the analysis
# is done correctly.
diff --git a/test/analyze6.test b/test/analyze6.test
index 515747cbf..b090b5b09 100644
--- a/test/analyze6.test
+++ b/test/analyze6.test
@@ -71,4 +71,52 @@ do_test analyze6-1.2 {
} {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}}
+# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30
+# If ANALYZE is run on an empty table, make sure indices are used
+# on the table.
+#
+do_test analyze6-2.1 {
+ execsql {
+ CREATE TABLE t201(x INTEGER PRIMARY KEY, y UNIQUE, z);
+ CREATE INDEX t201z ON t201(z);
+ ANALYZE;
+ }
+ eqp {SELECT * FROM t201 WHERE z=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+do_test analyze6-2.2 {
+ eqp {SELECT * FROM t201 WHERE y=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+do_test analyze6-2.3 {
+ eqp {SELECT * FROM t201 WHERE x=5}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+do_test analyze6-2.4 {
+ execsql {
+ INSERT INTO t201 VALUES(1,2,3);
+ ANALYZE t201;
+ }
+ eqp {SELECT * FROM t201 WHERE z=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+do_test analyze6-2.5 {
+ eqp {SELECT * FROM t201 WHERE y=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+do_test analyze6-2.6 {
+ eqp {SELECT * FROM t201 WHERE x=5}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+do_test analyze6-2.7 {
+ execsql {
+ INSERT INTO t201 VALUES(4,5,7);
+ INSERT INTO t201 SELECT x+100, y+100, z+100 FROM t201;
+ INSERT INTO t201 SELECT x+200, y+200, z+200 FROM t201;
+ INSERT INTO t201 SELECT x+400, y+400, z+400 FROM t201;
+ ANALYZE t201;
+ }
+ eqp {SELECT * FROM t201 WHERE z=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+do_test analyze6-2.8 {
+ eqp {SELECT * FROM t201 WHERE y=5}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+do_test analyze6-2.9 {
+ eqp {SELECT * FROM t201 WHERE x=5}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+
finish_test
diff --git a/test/analyze7.test b/test/analyze7.test
new file mode 100644
index 000000000..9107376e2
--- /dev/null
+++ b/test/analyze7.test
@@ -0,0 +1,119 @@
+# 2011 April 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 implements regression tests for SQLite library.
+# This file implements tests for the ANALYZE command when an idnex
+# name is given as the argument.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# There is nothing to test if ANALYZE is disable for this build.
+#
+ifcapable {!analyze} {
+ finish_test
+ return
+}
+
+# Generate some test data
+#
+do_test analyze7-1.0 {
+ execsql {
+ CREATE TABLE sequence(x INTEGER PRIMARY KEY);
+ INSERT INTO sequence VALUES(1);
+ INSERT INTO sequence VALUES(2);
+ INSERT INTO sequence SELECT x+2 FROM sequence;
+ INSERT INTO sequence SELECT x+4 FROM sequence;
+ INSERT INTO sequence SELECT x+8 FROM sequence;
+ INSERT INTO sequence SELECT x+16 FROM sequence;
+ INSERT INTO sequence SELECT x+32 FROM sequence;
+ INSERT INTO sequence SELECT x+64 FROM sequence;
+ INSERT INTO sequence SELECT x+128 FROM sequence;
+ INSERT INTO sequence SELECT x+256 FROM sequence;
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX t1a ON t1(a);
+ CREATE INDEX t1b ON t1(b);
+ CREATE INDEX t1cd ON t1(c,d);
+ INSERT INTO t1 SELECT x, x, x/100, x FROM sequence;
+ EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;
+ }
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}}
+do_test analyze7-1.1 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+do_test analyze7-1.2 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}}
+
+# Run an analyze on one of the three indices. Verify that this
+# effects the row-count estimate on the one query that uses that
+# one index.
+#
+do_test analyze7-2.0 {
+ execsql {ANALYZE t1a;}
+ db cache flush
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+do_test analyze7-2.1 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+do_test analyze7-2.2 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}}
+
+# Verify that since the query planner now things that t1a is more
+# selective than t1b, it prefers to use t1a.
+#
+do_test analyze7-2.3 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+
+# Run an analysis on another of the three indices. Verify that this
+# new analysis works and does not disrupt the previous analysis.
+#
+do_test analyze7-3.0 {
+ execsql {ANALYZE t1cd;}
+ db cache flush;
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+do_test analyze7-3.1 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+do_test analyze7-3.2.1 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
+ifcapable stat2 {
+ # If ENABLE_STAT2 is defined, SQLite comes up with a different estimated
+ # row count for (c=2) than it does for (c=?).
+ do_test analyze7-3.2.2 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~102 rows)}}
+} else {
+ # If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the
+ # same as that for (c=?).
+ do_test analyze7-3.2.3 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
+}
+do_test analyze7-3.3 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+do_test analyze7-3.4 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
+do_test analyze7-3.5 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+do_test analyze7-3.6 {
+ execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}}
+
+finish_test
diff --git a/test/badutf2.test b/test/badutf2.test
index 462e98892..36b40fb95 100644
--- a/test/badutf2.test
+++ b/test/badutf2.test
@@ -105,9 +105,11 @@ foreach { i len uval xstr ustr u2u } {
utf8_to_ustr2 [ sqlite3_column_text $S 0 ]
} $ustr
- do_test badutf2-5.1.$i {
- utf8_to_utf8 $uval
- } $u2u
+ ifcapable debug {
+ do_test badutf2-5.1.$i {
+ utf8_to_utf8 $uval
+ } $u2u
+ }
}
diff --git a/test/fts3fault2.test b/test/fts3fault2.test
index 6d41aee1c..fb877737f 100644
--- a/test/fts3fault2.test
+++ b/test/fts3fault2.test
@@ -14,6 +14,9 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix fts3fault2
+# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
+ifcapable !fts3 { finish_test ; return }
+
do_test 1.0 {
execsql {
CREATE VIRTUAL TABLE t1 USING fts4(x);
diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl
index 379b97074..e7f615648 100644
--- a/test/malloc_common.tcl
+++ b/test/malloc_common.tcl
@@ -117,8 +117,8 @@ proc do_faultsim_test {name args} {
set DEFAULT(-prep) ""
set DEFAULT(-body) ""
set DEFAULT(-test) ""
- set DEFAULT(-install) ""
- set DEFAULT(-uninstall) ""
+ set DEFAULT(-install) ""
+ set DEFAULT(-uninstall) ""
fix_testname name
diff --git a/test/multiplex.test b/test/multiplex.test
index 742ca5079..518cbe37a 100644
--- a/test/multiplex.test
+++ b/test/multiplex.test
@@ -14,7 +14,7 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
-set g_chunk_size 2147483648
+set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
set g_max_chunks 32
# This handles appending the chunk number
@@ -32,13 +32,17 @@ proc multiplex_name {name chunk} {
}
# This saves off the parameters and calls the
-# underlying sqlite3_multiplex_set() API.
-proc multiplex_set {chunk_size max_chunks} {
+# underlying sqlite3_multiplex_control() API.
+proc multiplex_set {db name chunk_size max_chunks} {
global g_chunk_size
global g_max_chunks
- set g_chunk_size $chunk_size
+ set g_chunk_size [ expr (($chunk_size+($::SQLITE_MAX_PAGE_SIZE-1)) & ~($::SQLITE_MAX_PAGE_SIZE-1)) ]
set g_max_chunks $max_chunks
- sqlite3_multiplex_set $chunk_size $max_chunks
+ set rc [catch {sqlite3_multiplex_control $db $name chunk_size $chunk_size} msg]
+ if { $rc==0 } {
+ set rc [catch {sqlite3_multiplex_control $db $name max_chunks $max_chunks} msg]
+ }
+ list $msg
}
# This attempts to delete the base file and
@@ -70,14 +74,51 @@ do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK}
do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK}
-do_test multiplex-1.9 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
-do_test multiplex-1.10.1 { multiplex_set 32768 16 } {SQLITE_OK}
-do_test multiplex-1.10.2 { multiplex_set 32768 -1 } {SQLITE_MISUSE}
-do_test multiplex-1.10.3 { multiplex_set -1 16 } {SQLITE_MISUSE}
-do_test multiplex-1.10.4 { multiplex_set 31 16 } {SQLITE_MISUSE}
-do_test multiplex-1.10.5 { multiplex_set 32768 100 } {SQLITE_MISUSE}
-do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+do_test multiplex-1.9.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.9.2 { sqlite3 db test.db } {}
+do_test multiplex-1.9.3 { multiplex_set db main 32768 16 } {SQLITE_OK}
+do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_MISUSE}
+do_test multiplex-1.9.5 { multiplex_set db main -1 16 } {SQLITE_MISUSE}
+do_test multiplex-1.9.6 { multiplex_set db main 31 16 } {SQLITE_OK}
+do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_MISUSE}
+do_test multiplex-1.9.8 { multiplex_set db main 1073741824 1 } {SQLITE_OK}
+do_test multiplex-1.9.9 { db close } {}
+do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.10.2 { sqlite3 db test.db } {}
+do_test multiplex-1.10.3 { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 } {0}
+do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {1}
+do_test multiplex-1.10.5 { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 } {1}
+do_test multiplex-1.10.6 { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 } {0}
+do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {1}
+do_test multiplex-1.10.8 { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0}
+do_test multiplex-1.10.9 { db close } {}
+do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+do_test multiplex-1.11.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.11.2 { sqlite3 db test.db } {}
+do_test multiplex-1.11.3 { sqlite3_multiplex_control db main enable 0 } {SQLITE_OK}
+do_test multiplex-1.11.4 { sqlite3_multiplex_control db main enable 1 } {SQLITE_OK}
+do_test multiplex-1.11.5 { sqlite3_multiplex_control db main enable -1 } {SQLITE_OK}
+do_test multiplex-1.11.6 { db close } {}
+do_test multiplex-1.11.7 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+do_test multiplex-1.12.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.12.2 { sqlite3 db test.db } {}
+do_test multiplex-1.12.3 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0}
+do_test multiplex-1.12.4 { lindex [ catchsql { SELECT multiplex_control(1, 1); } ] 0 } {0}
+do_test multiplex-1.12.5 { lindex [ catchsql { SELECT multiplex_control(1, -1); } ] 0 } {0}
+do_test multiplex-1.12.6 { db close } {}
+do_test multiplex-1.12.7 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+do_test multiplex-1.13.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.13.2 { sqlite3 db test.db } {}
+do_test multiplex-1.13.3 { lindex [ catchsql { SELECT multiplex_control(-1, 0); } ] 0 } {1}
+do_test multiplex-1.13.4 { lindex [ catchsql { SELECT multiplex_control(4, 1); } ] 0 } {1}
+do_test multiplex-1.13.6 { db close } {}
+do_test multiplex-1.13.7 { sqlite3_multiplex_shutdown } {SQLITE_OK}
#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback
@@ -98,9 +139,12 @@ do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK}
#
# multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
# well as varying journal mode.
+#
+# multiplex-2.7.*: Disable/enable tests.
+#
sqlite3_multiplex_initialize "" 1
-multiplex_set 32768 16
+multiplex_set db main 32768 16
do_test multiplex-2.1.2 {
sqlite3 db test.db
@@ -130,6 +174,7 @@ do_test multiplex-2.3.1 {
db2 close
} {}
+
do_test multiplex-2.4.1 {
sqlite3_multiplex_shutdown
} {SQLITE_MISUSE}
@@ -146,11 +191,11 @@ do_test multiplex-2.4.99 {
do_test multiplex-2.5.1 {
multiplex_delete test.db
sqlite3_multiplex_initialize "" 1
- multiplex_set 4096 16
+ sqlite3 db test.db
+ multiplex_set db main 4096 16
} {SQLITE_OK}
do_test multiplex-2.5.2 {
- sqlite3 db test.db
execsql {
PRAGMA page_size = 1024;
PRAGMA journal_mode = delete;
@@ -165,7 +210,9 @@ do_test multiplex-2.5.3 {
INSERT INTO t1 VALUES(2, randomblob(4000));
INSERT INTO t1 VALUES(3, 'three');
INSERT INTO t1 VALUES(4, randomblob(4000));
- INSERT INTO t1 VALUES(5, 'five')
+ INSERT INTO t1 VALUES(5, 'five');
+ INSERT INTO t1 VALUES(6, randomblob($g_chunk_size));
+ INSERT INTO t1 VALUES(7, randomblob($g_chunk_size));
}
} {}
@@ -205,11 +252,11 @@ foreach jmode $all_journal_modes {
do_test multiplex-2.6.1.$sz.$jmode {
multiplex_delete test.db
sqlite3_multiplex_initialize "" 1
- multiplex_set $sz 32
+ sqlite3 db test.db
+ multiplex_set db main $sz 32
} {SQLITE_OK}
do_test multiplex-2.6.2.$sz.$jmode {
- sqlite3 db test.db
db eval {
PRAGMA page_size = 1024;
PRAGMA auto_vacuum = off;
@@ -243,6 +290,31 @@ foreach jmode $all_journal_modes {
}
}
+do_test multiplex-2.7.1 { multiplex_delete test.db } {}
+do_test multiplex-2.7.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-2.7.3 { sqlite3 db test.db } {}
+do_test multiplex-2.7.4 { lindex [ catchsql { SELECT multiplex_control(2, 65536); } ] 0 } {0}
+do_test multiplex-2.7.5 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0}
+do_test multiplex-2.7.6 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, randomblob(1000));
+ }
+} {}
+# verify only one file, and file size is less than chunks size
+do_test multiplex-2.7.7 { expr ([file size [multiplex_name test.db 0]] < 65536) } {1}
+do_test multiplex-2.7.8 { file exists [multiplex_name test.db 1] } {0}
+do_test multiplex-2.7.9 {
+ execsql {
+ INSERT INTO t1 VALUES(2, randomblob(65536));
+ }
+} {}
+# verify only one file, and file size exceeds chunks size
+do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1}
+do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] } {0}
+do_test multiplex-2.7.12 { db close } {}
+do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
#-------------------------------------------------------------------------
# Try some tests with more than one connection to a database file. Still
# in rollback mode.
@@ -255,10 +327,10 @@ foreach jmode $all_journal_modes {
do_test multiplex-3.1.1 {
multiplex_delete test.db
sqlite3_multiplex_initialize "" 1
- multiplex_set 32768 16
+ sqlite3 db test.db
+ multiplex_set db main 32768 16
} {SQLITE_OK}
do_test multiplex-3.1.2 {
- sqlite3 db test.db
execsql {
PRAGMA page_size = 1024;
PRAGMA journal_mode = delete;
@@ -341,7 +413,7 @@ do_test multiplex-3.2.X {
#
sqlite3_multiplex_initialize "" 1
-multiplex_set 32768 16
+multiplex_set db main 32768 16
# Return a list of all currently defined multiplexs.
proc multiplex_list {} {
@@ -403,7 +475,7 @@ do_test multiplex-4.1.12 {
#
sqlite3_multiplex_initialize "" 1
-multiplex_set 32768 16
+multiplex_set db main 32768 16
do_faultsim_test multiplex-5.1 -prep {
catch {db close}
@@ -448,7 +520,7 @@ do_faultsim_test multiplex-5.5 -prep {
catch { sqlite3_multiplex_shutdown }
} -body {
sqlite3_multiplex_initialize "" 1
- multiplex_set 32768 16
+ multiplex_set db main 32768 16
}
# test that mismatch filesize is detected
@@ -473,20 +545,20 @@ if {0==[info exists ::G(perm:presql)] || $::G(perm:presql) == ""} {
do_test multiplex-5.6.2.$jmode {
execsql {
CREATE TABLE t1(a, b);
- INSERT INTO t1 VALUES(1, randomblob(1100));
- INSERT INTO t1 VALUES(2, randomblob(1100));
- INSERT INTO t1 VALUES(3, randomblob(1100));
- INSERT INTO t1 VALUES(4, randomblob(1100));
- INSERT INTO t1 VALUES(5, randomblob(1100));
+ INSERT INTO t1 VALUES(1, randomblob(15000));
+ INSERT INTO t1 VALUES(2, randomblob(15000));
+ INSERT INTO t1 VALUES(3, randomblob(15000));
+ INSERT INTO t1 VALUES(4, randomblob(15000));
+ INSERT INTO t1 VALUES(5, randomblob(15000));
}
db close
sqlite3_multiplex_initialize "" 1
- multiplex_set 4096 16
sqlite3 db test.db
- } {}
+ multiplex_set db main 4096 16
+ } {SQLITE_OK}
do_test multiplex-5.6.3.$jmode {
catchsql {
- INSERT INTO t1 VALUES(6, randomblob(1100));
+ INSERT INTO t1 VALUES(6, randomblob(15000));
}
} {1 {disk I/O error}}
do_test multiplex-5.6.4.$jmode {
diff --git a/test/oserror.test b/test/oserror.test
index ecb02116b..d1952a30d 100644
--- a/test/oserror.test
+++ b/test/oserror.test
@@ -44,7 +44,12 @@ proc do_re_test {tn script expression} {
# Tests oserror-1.* test failures in the open() system call.
#
-# Test a failure in open() due to too many files.
+# Test a failure in open() due to too many files.
+#
+# The xOpen() method of the unix VFS calls getcwd() as well as open().
+# Although this does not appear to be documented in the man page, on OSX
+# a call to getcwd() may fail if there are no free file descriptors. So
+# an error may be reported for either open() or getcwd() here.
#
do_test 1.1.1 {
set ::log [list]
@@ -55,8 +60,9 @@ do_test 1.1.1 {
do_test 1.1.2 {
catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } }
} {1}
-
-do_re_test 1.1.3 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*test.db\) - }
+do_re_test 1.1.3 {
+ lindex $::log 0
+} {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - }
# Test a failure in open() due to the path being a directory.
diff --git a/test/syscall.test b/test/syscall.test
new file mode 100644
index 000000000..21295a59c
--- /dev/null
+++ b/test/syscall.test
@@ -0,0 +1,250 @@
+# 2011 March 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/lock_common.tcl
+source $testdir/malloc_common.tcl
+
+if {[llength [info commands test_syscall]]==0} {
+ finish_test
+ return
+}
+set testprefix syscall
+
+
+#-------------------------------------------------------------------------
+# Tests for the xSetSystemCall method.
+#
+do_test 1.1.1 {
+ list [catch { test_syscall reset open } msg] $msg
+} {0 {}}
+do_test 1.1.2 {
+ list [catch { test_syscall reset nosuchcall } msg] $msg
+} {1 SQLITE_NOTFOUND}
+do_test 1.1.3 {
+ list [catch { test_syscall reset open } msg] $msg
+} {0 {}}
+do_test 1.1.4 {
+ list [catch { test_syscall reset ""} msg] $msg
+} {1 SQLITE_NOTFOUND}
+
+do_test 1.2 { test_syscall reset } {}
+
+do_test 1.3.1 { test_syscall install {open getcwd access} } {}
+do_test 1.3.2 { test_syscall reset } {}
+
+#-------------------------------------------------------------------------
+# Tests for the xGetSystemCall method.
+#
+do_test 2.1.1 { test_syscall exists open } 1
+do_test 2.1.2 { test_syscall exists nosuchcall } 0
+
+#-------------------------------------------------------------------------
+# Tests for the xNextSystemCall method.
+#
+set syscall_list [list \
+ open close access getcwd stat fstat ftruncate \
+ fcntl read pread write pwrite fchmod \
+]
+if {[test_syscall exists fallocate]} {lappend syscall_list fallocate}
+do_test 3.1 { test_syscall list } $syscall_list
+
+#-------------------------------------------------------------------------
+# This test verifies that if a call to open() fails and errno is set to
+# EINTR, the call is retried. If it succeeds, execution continues as if
+# nothing happened.
+#
+test_syscall reset
+forcedelete test.db2
+do_execsql_test 4.1 {
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(1, 2);
+ ATTACH 'test.db2' AS aux;
+ CREATE TABLE aux.t2(x, y);
+ INSERT INTO t2 VALUES(3, 4);
+}
+
+db_save_and_close
+test_syscall install open
+foreach jrnl [list wal delete] {
+ for {set i 1} {$i < 20} {incr i} {
+ db_restore_and_reopen
+ test_syscall fault $i 0
+ test_syscall errno open EINTR
+
+ do_test 4.2.$jrnl.$i {
+ sqlite3 db test.db
+ execsql { ATTACH 'test.db2' AS aux }
+ execsql "PRAGMA main.journal_mode = $jrnl"
+ execsql "PRAGMA aux.journal_mode = $jrnl"
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(5, 6);
+ INSERT INTO t2 VALUES(7, 8);
+ COMMIT;
+ }
+
+ db close
+ sqlite3 db test.db
+ execsql { ATTACH 'test.db2' AS aux }
+ execsql {
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+ } {1 2 5 6 3 4 7 8}
+ }
+}
+
+#-------------------------------------------------------------------------
+# This test verifies that closing database handles does not drop locks
+# held by other database handles in the same process on the same file.
+#
+# The os_unix.c module has to take precautions to prevent this as the
+# close() system call drops locks held by other file-descriptors on the
+# same file. From the Linux man page:
+#
+# close() closes a file descriptor, so that it no longer refers to any file
+# and may be reused. Any record locks (see fcntl(2)) held on the file it
+# was associated with, and owned by the process, are removed (regardless
+# of the file descriptor that was used to obtain the lock).
+#
+catch { db close }
+forcedelete test.db test.db2
+
+do_multiclient_test tn {
+ code1 {
+ sqlite3 dbX1 test.db
+ sqlite3 dbX2 test.db
+ }
+
+ do_test syscall-5.$tn.1 {
+ sql1 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ BEGIN;
+ INSERT INTO t1 VALUES(3, 4);
+ }
+ } {}
+
+ do_test syscall-5.$tn.2 { sql2 { SELECT * FROM t1 } } {1 2}
+ do_test syscall-5.$tn.3 {
+ csql2 { INSERT INTO t1 VALUES(5, 6) }
+ } {1 {database is locked}}
+
+ do_test syscall-5.$tn.4 {
+ code1 {
+ dbX1 close
+ dbX2 close
+ }
+ } {}
+
+ do_test syscall-5.$tn.5 {
+ csql2 { INSERT INTO t1 VALUES(5, 6) }
+ } {1 {database is locked}}
+
+ do_test syscall-5.$tn.6 { sql1 { COMMIT } } {}
+
+ do_test syscall-5.$tn.7 {
+ csql2 { INSERT INTO t1 VALUES(5, 6) }
+ } {0 {}}
+}
+
+catch {db close}
+do_test 6.1 {
+ sqlite3 db1 test.db1
+ sqlite3 db2 test.db2
+ sqlite3 db3 test.db3
+ sqlite3 dbM ""
+
+ db2 close
+ db3 close
+ dbM close
+ db1 close
+} {}
+
+do_test 6.2 {
+ sqlite3 db test.db
+ execsql {
+ PRAGMA temp_store = file;
+
+ PRAGMA main.cache_size = 10;
+ PRAGMA temp.cache_size = 10;
+ CREATE TABLE temp.tt(a, b);
+ INSERT INTO tt VALUES(randomblob(500), randomblob(600));
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt;
+ }
+
+ db close
+} {}
+
+#-------------------------------------------------------------------------
+# Test that a database file a single byte in size is treated as an empty
+# file. Whereas a file 2 bytes or larger might be considered corrupt.
+#
+catch { db close }
+forcedelete test.db test.db2
+
+proc create_db_file {nByte} {
+ set fd [open test.db w]
+ fconfigure $fd -translation binary -encoding binary
+ puts -nonewline $fd [string range "xSQLite" 1 $nByte]
+ close $fd
+}
+
+foreach {nByte res} {
+ 1 {0 {}}
+ 2 {1 {file is encrypted or is not a database}}
+ 3 {1 {file is encrypted or is not a database}}
+} {
+ do_test 7.$nByte {
+ create_db_file $nByte
+ sqlite3 db test.db
+ catchsql { CREATE TABLE t1(a, b) }
+ } $res
+ catch { db close }
+}
+
+#-------------------------------------------------------------------------
+#
+catch { db close }
+forcedelete test.db test.db2
+
+do_test 8.1 {
+ sqlite3 db test.db
+ file_control_chunksize_test db main 4096
+ file size test.db
+} {0}
+
+foreach {tn hint size} {
+ 1 1000 4096
+ 2 1000 4096
+ 3 3000 4096
+ 4 4096 4096
+ 5 4197 8192
+} {
+ do_test 8.2.$tn {
+ file_control_sizehint_test db main $hint
+ file size test.db
+ } $size
+}
+
+
+
+finish_test
diff --git a/test/sysfault.test b/test/sysfault.test
new file mode 100644
index 000000000..07d525ca9
--- /dev/null
+++ b/test/sysfault.test
@@ -0,0 +1,247 @@
+# 2011 March 28
+#
+# 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/lock_common.tcl
+source $testdir/malloc_common.tcl
+
+if {[llength [info commands test_syscall]]==0} {
+ finish_test
+ return
+}
+
+set testprefix sysfault
+
+set FAULTSIM(vfsfault-transient) [list \
+ -injectinstall vfsfault_install \
+ -injectstart vfsfault_injectstart_t \
+ -injectstop vfsfault_injectstop \
+ -injecterrlist {} \
+ -injectuninstall {test_syscall uninstall} \
+]
+set FAULTSIM(vfsfault-persistent) [list \
+ -injectinstall vfsfault_install \
+ -injectstart vfsfault_injectstart_p \
+ -injectstop vfsfault_injectstop \
+ -injecterrlist {} \
+ -injectuninstall {test_syscall uninstall} \
+]
+
+proc vfsfault_injectstart_t {iFail} { test_syscall fault $iFail 0 }
+proc vfsfault_injectstart_p {iFail} { test_syscall fault $iFail 1 }
+proc vfsfault_injectstop {} { test_syscall fault }
+
+faultsim_save_and_close
+
+
+set open_and_write_body {
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ PRAGMA journal_mode = WAL;
+ INSERT INTO t1 VALUES(3, 4);
+ SELECT * FROM t1;
+ CREATE TEMP TABLE t2(x);
+ INSERT INTO t2 VALUES('y');
+ }
+}
+
+proc vfsfault_install {} { test_syscall install {open getcwd} }
+do_faultsim_test 1 -faults vfsfault-* -prep {
+ faultsim_restore
+} -body $open_and_write_body -test {
+ faultsim_test_result {0 {wal 1 2 3 4}} \
+ {1 {unable to open database file}} \
+ {1 {attempt to write a readonly database}}
+}
+
+#-------------------------------------------------------------------------
+# Errors in the fstat() function when opening and writing a file. Cases
+# where fstat() fails and sets errno to ENOMEM and EOVERFLOW are both
+# tested. EOVERFLOW is interpreted as meaning that a file on disk is
+# too large to be opened by the OS.
+#
+foreach {tn errno errlist} {
+ 1 ENOMEM {{disk I/O error}}
+ 2 EOVERFLOW {{disk I/O error} {large file support is disabled}}
+} {
+ proc vfsfault_install {} { test_syscall install fstat }
+ set errs [list]
+ foreach e $errlist { lappend errs [list 1 $e] }
+ do_faultsim_test 1.2.$tn -faults vfsfault-* -prep {
+ faultsim_restore
+ } -body "
+ test_syscall errno fstat $errno
+ $open_and_write_body
+ " -test "
+ faultsim_test_result {0 {wal 1 2 3 4}} $errs
+ "
+}
+
+#-------------------------------------------------------------------------
+# Various errors in locking functions.
+#
+foreach vfs {unix unix-excl} {
+ foreach {tn errno errlist} {
+ 1 EAGAIN {{database is locked} {disk I/O error}}
+ 2 ETIMEDOUT {{database is locked} {disk I/O error}}
+ 3 EBUSY {{database is locked} {disk I/O error}}
+ 4 EINTR {{database is locked} {disk I/O error}}
+ 5 ENOLCK {{database is locked} {disk I/O error}}
+ 6 EACCES {{database is locked} {disk I/O error}}
+ 7 EPERM {{access permission denied} {disk I/O error}}
+ 8 EDEADLK {{disk I/O error}}
+ 9 ENOMEM {{disk I/O error}}
+ } {
+ proc vfsfault_install {} { test_syscall install fcntl }
+ set errs [list]
+ foreach e $errlist { lappend errs [list 1 $e] }
+
+ set body [string map [list %VFS% $vfs] {
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ }
+ set fd [open test.db-journal w]
+ puts $fd "hello world"
+ close $fd
+ sqlite3 db test.db -vfs %VFS%
+ db eval {
+ SELECT * FROM t1;
+ }
+ }]
+
+ do_faultsim_test 1.3.$vfs.$tn -faults vfsfault-* -prep {
+ faultsim_restore
+ } -body "
+ test_syscall errno fcntl $errno
+ $body
+ " -test "
+ faultsim_test_result {0 {1 2}} $errs
+ "
+ }
+}
+
+#-------------------------------------------------------------------------
+# Check that a single EINTR error does not affect processing.
+#
+proc vfsfault_install {} {
+ test_syscall reset
+ test_syscall install {open ftruncate close read pread pread64 write fallocate}
+}
+
+forcedelete test.db test.db2
+sqlite3 db test.db
+do_test 2.setup {
+ execsql {
+ CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
+ INSERT INTO t1 VALUES('abc', 'def', 'ghi');
+ ATTACH 'test.db2' AS 'aux';
+ CREATE TABLE aux.t2(x);
+ INSERT INTO t2 VALUES(1);
+ }
+ faultsim_save_and_close
+} {}
+
+do_faultsim_test 2.1 -faults vfsfault-transient -prep {
+ catch { db close }
+ faultsim_restore
+} -body {
+ test_syscall errno open EINTR
+ test_syscall errno ftruncate EINTR
+ test_syscall errno close EINTR
+ test_syscall errno read EINTR
+ test_syscall errno pread EINTR
+ test_syscall errno pread64 EINTR
+ test_syscall errno write EINTR
+ test_syscall errno fallocate EINTR
+
+ sqlite3 db test.db
+ file_control_chunksize_test db main 8192
+
+ set res [db eval {
+ ATTACH 'test.db2' AS 'aux';
+ SELECT * FROM t1;
+ PRAGMA journal_mode = truncate;
+ BEGIN;
+ INSERT INTO t1 VALUES('jkl', 'mno', 'pqr');
+ INSERT INTO t1 VALUES(randomblob(10000), 0, 0);
+ UPDATE t2 SET x = 2;
+ COMMIT;
+ DELETE FROM t1 WHERE length(a)>3;
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }]
+ db close
+ set res
+} -test {
+ faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}}
+}
+
+do_faultsim_test 2.2 -faults vfsfault-* -prep {
+ catch { db close }
+ faultsim_restore
+} -body {
+ sqlite3 db test.db
+ set res [db eval {
+ ATTACH 'test.db2' AS 'aux';
+ SELECT * FROM t1;
+ PRAGMA journal_mode = truncate;
+ BEGIN;
+ INSERT INTO t1 VALUES('jkl', 'mno', 'pqr');
+ UPDATE t2 SET x = 2;
+ COMMIT;
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }]
+ db close
+ set res
+} -test {
+ faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} \
+ {1 {unable to open database file}} \
+ {1 {unable to open database: test.db2}} \
+ {1 {attempt to write a readonly database}} \
+ {1 {disk I/O error}}
+}
+
+#-------------------------------------------------------------------------
+
+proc vfsfault_install {} {
+ test_syscall reset
+ test_syscall install {fstat fallocate}
+}
+do_faultsim_test 3 -faults vfsfault-* -prep {
+ faultsim_delete_and_reopen
+ file_control_chunksize_test db main 8192
+ execsql {
+ CREATE TABLE t1(a, b);
+ BEGIN;
+ SELECT * FROM t1;
+ }
+} -body {
+ test_syscall errno fstat EIO
+ test_syscall errno fallocate EIO
+
+ execsql {
+ INSERT INTO t1 VALUES(randomblob(10000), randomblob(10000));
+ SELECT length(a) + length(b) FROM t1;
+ COMMIT;
+ }
+} -test {
+ faultsim_test_result {0 20000}
+}
+
+finish_test
+
diff --git a/test/unixexcl.test b/test/unixexcl.test
new file mode 100644
index 000000000..057ae0af1
--- /dev/null
+++ b/test/unixexcl.test
@@ -0,0 +1,83 @@
+# 2011 March 30
+#
+# 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 the "unix-excl" VFS module (part of
+# os_unix.c).
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+
+if {$::tcl_platform(platform)!="unix" || [info commands test_syscall]==""} {
+ finish_test
+ return
+}
+set testprefix unixexcl
+
+
+
+# Test that when using VFS "unix-excl", the first time the database is read
+# a process-wide exclusive lock is taken on it. This means other connections
+# within the process may still access the db normally, but connections from
+# outside the process cannot.
+#
+do_multiclient_test tn {
+ do_test unixexcl-1.$tn.1 {
+ sql1 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES('hello', 'world');
+ }
+ } {}
+ do_test unixexcl-1.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world}
+ do_test unixexcl-1.$tn.3 {
+ code1 {
+ db close
+ sqlite3 db test.db -vfs unix-excl
+ db eval { SELECT * FROM t1 }
+ }
+ } {hello world}
+ if {$tn==1} {
+ do_test unixexcl-1.$tn.4.multiproc {
+ csql2 { SELECT * FROM t1 }
+ } {1 {database is locked}}
+ } else {
+ do_test unixexcl-1.$tn.4.singleproc {
+ csql2 { SELECT * FROM t1 }
+ } {0 {hello world}}
+ }
+}
+
+# Test that when using VFS "unix-excl", if a file is opened in read-only mode
+# the behaviour is the same as if VFS "unix" were used.
+#
+do_multiclient_test tn {
+ do_test unixexcl-2.$tn.1 {
+ sql1 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES('hello', 'world');
+ }
+ } {}
+ do_test unixexcl-2.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world}
+ do_test unixexcl-2.$tn.3 {
+ code1 {
+ db close
+ sqlite3 db test.db -readonly yes -vfs unix-excl
+ db eval { SELECT * FROM t1 }
+ }
+ } {hello world}
+ do_test unixexcl-2.$tn.4 {
+ csql2 { SELECT * FROM t1 }
+ } {0 {hello world}}
+}
+
+finish_test
diff --git a/test/wal.test b/test/wal.test
index 339661e2e..257a87b09 100644
--- a/test/wal.test
+++ b/test/wal.test
@@ -463,6 +463,7 @@ do_multiclient_test tn {
#
do_test wal-10.$tn.1 {
execsql {
+ PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
diff --git a/test/wal2.test b/test/wal2.test
index 7bb820cc1..82234330f 100644
--- a/test/wal2.test
+++ b/test/wal2.test
@@ -343,6 +343,7 @@ file delete -force test.db test.db-wal test.db-journal
do_test wal2-4.1 {
sqlite3 db test.db
execsql {
+ PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = WAL;
CREATE TABLE data(x);
INSERT INTO data VALUES('need xShmOpen to see this');
@@ -622,6 +623,7 @@ set READMARK1_WRITE {
foreach {tn sql res expected_locks} {
2 {
+ PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = WAL;
BEGIN;
CREATE TABLE t1(x);
@@ -707,6 +709,7 @@ tvfs delete
do_test wal2-6.5.1 {
sqlite3 db test.db
execsql {
+ PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
PRAGMA locking_mode = exclusive;
CREATE TABLE t2(a, b);
@@ -1166,6 +1169,7 @@ foreach {tn sql reslist} {
} {
faultsim_delete_and_reopen
+ execsql {PRAGMA auto_vacuum = 0}
execsql $sql
do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}
diff --git a/test/wal3.test b/test/wal3.test
index b00663893..bd296154e 100644
--- a/test/wal3.test
+++ b/test/wal3.test
@@ -413,6 +413,7 @@ testvfs T -default 1
do_test wal3-6.1.1 {
file delete -force test.db test.db-journal test.db wal
sqlite3 db test.db
+ execsql { PRAGMA auto_vacuum = off }
execsql { PRAGMA journal_mode = WAL }
execsql {
CREATE TABLE t1(a, b);
@@ -494,6 +495,7 @@ do_test wal3-6.2.1 {
file delete -force test.db test.db-journal test.db wal
sqlite3 db test.db
sqlite3 db2 test.db
+ execsql { PRAGMA auto_vacuum = off }
execsql { PRAGMA journal_mode = WAL }
execsql {
CREATE TABLE t1(a, b);
@@ -617,6 +619,7 @@ do_test wal3-8.1 {
sqlite3 db test.db
sqlite3 db2 test.db
execsql {
+ PRAGMA auto_vacuum = off;
PRAGMA journal_mode = WAL;
CREATE TABLE b(c);
INSERT INTO b VALUES('Tehran');
diff --git a/test/wal5.test b/test/wal5.test
index d571dae0d..ad6bcfc7d 100644
--- a/test/wal5.test
+++ b/test/wal5.test
@@ -169,6 +169,8 @@ foreach {testprefix do_wal_checkpoint} {
sql2 { ATTACH 'test.db2' AS aux }
sql3 { ATTACH 'test.db2' AS aux }
sql1 {
+ PRAGMA aux.auto_vacuum = 0;
+ PRAGMA main.auto_vacuum = 0;
PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL;
PRAGMA aux.page_size=1024; PRAGMA aux.journal_mode=WAL;
}
@@ -309,6 +311,7 @@ foreach {testprefix do_wal_checkpoint} {
do_test 3.$tn.1 {
sql1 {
+ PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = WAL;
PRAGMA synchronous = normal;
CREATE TABLE t1(x, y);
diff --git a/test/where3.test b/test/where3.test
index ce283905a..ab75fdec1 100644
--- a/test/where3.test
+++ b/test/where3.test
@@ -222,6 +222,7 @@ do_execsql_test where3-3.0 {
CREATE INDEX t301c ON t301(c);
INSERT INTO t301 VALUES(1,2,3);
CREATE TABLE t302(x, y);
+ INSERT INTO t302 VALUES(4,5);
ANALYZE;
explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y;
} {
@@ -341,5 +342,4 @@ do_execsql_test where3-5.3 {
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
-
finish_test
diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl
index d56b06dbc..fa99f2df7 100644
--- a/tool/mksqlite3c.tcl
+++ b/tool/mksqlite3c.tcl
@@ -51,7 +51,7 @@ puts $out [subst \
{/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version $VERSION. By combining all the individual C code files into this
-** single large file, the entire code can be compiled as a one translation
+** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
diff --git a/tool/split-sqlite3c.tcl b/tool/split-sqlite3c.tcl
new file mode 100644
index 000000000..287b75282
--- /dev/null
+++ b/tool/split-sqlite3c.tcl
@@ -0,0 +1,82 @@
+#!/usr/bin/tclsh
+#
+# This script splits the sqlite3.c amalgamated source code files into
+# several smaller files such that no single files is more than a fixed
+# number of lines in length (32k or 64k). Each of the split out files
+# is #include-ed by the master file.
+#
+# Splitting files up this way allows them to be used with older compilers
+# that cannot handle really long source files.
+#
+set MAX 32768 ;# Maximum number of lines per file.
+
+set BEGIN {^/\*+ Begin file ([a-zA-Z0-9_.]+) \*+/}
+set END {^/\*+ End of %s \*+/}
+
+set in [open sqlite3.c]
+set out1 [open sqlite3-all.c w]
+
+# Copy the header from sqlite3.c into sqlite3-all.c
+#
+while {[gets $in line]} {
+ if {[regexp $BEGIN $line]} break
+ puts $out1 $line
+}
+
+# Gather the complete content of a file into memory. Store the
+# content in $bufout. Store the number of lines is $nout
+#
+proc gather_one_file {firstline bufout nout} {
+ regexp $::BEGIN $firstline all filename
+ set end [format $::END $filename]
+ upvar $bufout buf $nout n
+ set buf $firstline\n
+ global in
+ set n 0
+ while {[gets $in line]>=0} {
+ incr n
+ append buf $line\n
+ if {[regexp $end $line]} break
+ }
+}
+
+# Write a big chunk of text in to an auxiliary file "sqlite3-NNN.c".
+# Also add an appropriate #include to sqlite3-all.c
+#
+set filecnt 0
+proc write_one_file {content} {
+ global filecnt
+ incr filecnt
+ set out [open sqlite3-$filecnt.c w]
+ puts -nonewline $out $content
+ close $out
+ puts $::out1 "#include \"sqlite3-$filecnt.c\""
+}
+
+# Continue reading input. Store chunks in separate files and add
+# the #includes to the main sqlite3-all.c file as necessary to reference
+# the extra chunks.
+#
+set all {}
+set N 0
+while {[regexp $BEGIN $line]} {
+ set buf {}
+ set n 0
+ gather_one_file $line buf n
+ if {$n+$N>=$MAX} {
+ write_one_file $all
+ set all {}
+ set N 0
+ }
+ append all $buf
+ incr N $n
+ while {[gets $in line]>=0} {
+ if {[regexp $BEGIN $line]} break
+ puts $out1 $line
+ }
+}
+if {$N>0} {
+ write_one_file $all
+}
+close $out1
+close $in