diff options
author | drh <drh@noemail.net> | 2011-04-04 13:19:36 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2011-04-04 13:19:36 +0000 |
commit | b92fce1eae7d894cbc16385dc654e63dc61f723f (patch) | |
tree | ff46bed41e3117be445ba42244369920bbefcbd6 | |
parent | 3d062377702a59cf6c3419fe4d74c0ae70869fb8 (diff) | |
parent | 7b94e7f8382b0a29b12e3ee2427331d41a6a0061 (diff) | |
download | sqlite-b92fce1eae7d894cbc16385dc654e63dc61f723f.tar.gz sqlite-b92fce1eae7d894cbc16385dc654e63dc61f723f.zip |
Merge the latest changes of trunk into the session branch.
FossilOrigin-Name: 95d53c44320b9639f2623aa9cc88d0d3e1a3be8f
-rw-r--r-- | Makefile.in | 3 | ||||
-rw-r--r-- | main.mk | 4 | ||||
-rw-r--r-- | manifest | 73 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btmutex.c | 139 | ||||
-rw-r--r-- | src/btree.c | 2 | ||||
-rw-r--r-- | src/btree.h | 20 | ||||
-rw-r--r-- | src/btreeInt.h | 5 | ||||
-rw-r--r-- | src/build.c | 8 | ||||
-rw-r--r-- | src/os_unix.c | 2 | ||||
-rw-r--r-- | src/sqliteInt.h | 12 | ||||
-rw-r--r-- | src/tclsqlite.c | 4 | ||||
-rw-r--r-- | src/test_fuzzer.c | 944 | ||||
-rw-r--r-- | src/test_multiplex.c | 560 | ||||
-rw-r--r-- | src/test_multiplex.h | 91 | ||||
-rw-r--r-- | src/test_syscall.c | 39 | ||||
-rw-r--r-- | src/test_wholenumber.c | 311 | ||||
-rw-r--r-- | src/vdbe.c | 100 | ||||
-rw-r--r-- | src/vdbeInt.h | 13 | ||||
-rw-r--r-- | src/vdbeaux.c | 172 | ||||
-rw-r--r-- | test/analyze7.test | 37 | ||||
-rw-r--r-- | test/fuzzer1.test | 1382 | ||||
-rw-r--r-- | test/memdb.test | 14 | ||||
-rw-r--r-- | test/multiplex.test | 134 | ||||
-rw-r--r-- | test/oserror.test | 13 | ||||
-rw-r--r-- | test/pagerfault3.test | 1 | ||||
-rw-r--r-- | test/syscall.test | 29 | ||||
-rw-r--r-- | test/wal.test | 1 | ||||
-rw-r--r-- | test/wal2.test | 4 | ||||
-rw-r--r-- | test/wal3.test | 3 | ||||
-rw-r--r-- | test/wal5.test | 3 | ||||
-rw-r--r-- | test/walfault.test | 2 | ||||
-rw-r--r--[-rwxr-xr-x] | tool/mkopts.tcl | 0 | ||||
-rw-r--r-- | tool/mksqlite3c.tcl | 2 | ||||
-rw-r--r-- | tool/split-sqlite3c.tcl | 82 |
35 files changed, 3702 insertions, 509 deletions
diff --git a/Makefile.in b/Makefile.in index 2c5490265..f3239f3d7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -356,6 +356,7 @@ TESTSRC = \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ + $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -371,10 +372,12 @@ TESTSRC = \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_superlock.c \ + $(TOP)/src/test_syscall.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ + $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c # Source code to the library files needed by the test fixture @@ -238,6 +238,7 @@ TESTSRC = \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ + $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -258,7 +259,8 @@ TESTSRC = \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ - $(TOP)/src/test_wsd.c + $(TOP)/src/test_wholenumber.c \ + $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c @@ -1,7 +1,7 @@ -C Merge\slatest\strunk\schanges. -D 2011-04-01T15:43:40.999 +C Merge\sthe\slatest\schanges\sof\strunk\sinto\sthe\ssession\sbranch. +D 2011-04-04T13:19:36.696 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f +F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 @@ -110,7 +110,7 @@ F ext/session/sqlite3session.h f284bac51c12de0e0096fc986e61f5ae6b9e5be5 F ext/session/test_session.c 82e3fd7d94f485ea63bcfb15d636c95a01db97a9 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 -F main.mk e283752f215b7055cdc48399da82033b67024e42 +F main.mk f942406cb7df55d1aec40a88a7ae399b730cd94f F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -129,11 +129,11 @@ F src/attach.c 438ea6f6b5d5961c1f49b737f2ce0f14ce7c6877 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 537f89c7ef5021cb580f31f782e556ffffcb2ed1 F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef -F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff -F src/btree.c 2b9c81ff64da339a67dda4f94c0d763627be0b67 -F src/btree.h 8d36f774ec4b1d0027b8966f8c03d9a72a518c14 -F src/btreeInt.h 20f73dc93b1eeb83afd7259fbc6bd7dcf2df7fe4 -F src/build.c 6c490fe14dedb094a202f559e3b29a276abffcf8 +F src/btmutex.c 3e595ee1bb99e3a1f16824137b435ffc97c98e5f +F src/btree.c 107723ed4f9bdb55213ba6164c30c49af75f4bf9 +F src/btree.h a840a20c1969391f98ee06960d5ee2dc460186b3 +F src/btreeInt.h 6714ce2f5e879eb9a904a6a4575dc4faa4f29991 +F src/build.c 3a8c6c4b1e16798755d46e699b7fcc12b9f27b2b F src/callback.c 5069f224882cbdccd559f591271d28d7f37745bc F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 52ff72f966cee3087e0138a3ec69371c22be3c01 @@ -171,7 +171,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 a3b4cdf50a9c9be8b50dc4932354ab749962a07f +F src/os_unix.c 32414676594a0a26cfccd7e02656230a3406eee7 F src/os_win.c 24d72407a90551969744cf9bcbb1b4c72c5fa845 F src/pager.c 6aa906b60a59664ba58d3f746164bb010d407ce1 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 @@ -189,11 +189,11 @@ F src/select.c d24406c45dd2442eb2eeaac413439066b149c944 F src/shell.c 9dc0b4bb59290c0a35256d278cab0f314987ad6a F src/sqlite.h.in 235300cdca517ce148385d3ab816e7e8cf9e1ff3 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 -F src/sqliteInt.h 14325fe41ee564f064adacda02ab64837fa905c4 +F src/sqliteInt.h bb9e764799e8c48ca547fbd1ddc3978aeb035bc5 F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44 F src/status.c 4997380fbb915426fef9e500b4872e79c99267fc F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 8376ae45f7e28d521ad708a0086771fab3c96475 +F src/tclsqlite.c fe0da0eb0ebd8d21eec90683b779456e64351de6 F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc @@ -211,6 +211,7 @@ F src/test_config.c 3050df9faf023fb52937f7e9998004c2415d4122 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 c4773049603151704a6ab25ac5e936b5109caf5a F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99 @@ -218,7 +219,8 @@ 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 @@ -229,11 +231,12 @@ 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 349a2b913e82b029f01527f58f65d66a02a09a84 +F src/test_syscall.c 34ce1be806cd180e241bc688ae3762e5e7825bda F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_thread.c bedd05cad673dba53326f3aa468cc803038896c0 F src/test_vfs.c 2ed8853c1e51ac6f9ea091f7ce4e0d618bba8b86 F src/test_vfstrace.c 2265c9895f350c8d3c39b079998fbe7481505cc1 +F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080 F src/trigger.c ec4813709e990a169b6923293e839fa5dfd64282 @@ -241,11 +244,11 @@ F src/update.c 3f3f3bb734a0da1dffd0ed33e504642b35ed3605 F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60 F src/util.c cd997077bad039efc0597eb027c929658f93c018 F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f -F src/vdbe.c 4784ded3d2c97f8084a17cacbeea490a0dccf75e +F src/vdbe.c e4afe80d602eb2e59d384e7e8e7cb19b06858bde F src/vdbe.h edef9c4f0be83e1f1dccd049da37b40e021b63d9 -F src/vdbeInt.h b6748a8ac9be169d83585a0f5daf747863c6b8db +F src/vdbeInt.h 36dddc4cc333867d27f00d326e29af30062a102a F src/vdbeapi.c 7ef519083e3420bf7793d8b4eae2961d894d98ab -F src/vdbeaux.c 0dcfffca621c534d8c64abfc47888dca5dd7b809 +F src/vdbeaux.c 15eff6a3202145a54e7a6e65374652045ece3545 F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5 @@ -268,7 +271,7 @@ F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045 F test/analyze5.test adc89b92fc9fee5ca1cb0bc8512f3206ad0fe5aa F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290 -F test/analyze7.test 06405dd3d2a3101de14270da508016a59254873b +F test/analyze7.test 9cbca440bebc5142a875ad23947797ff52f7b37c F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3 F test/async2.test bf5e2ca2c96763b4cba3d016249ad7259a5603b6 F test/async3.test 93edaa9122f498e56ea98c36c72abc407f4fb11e @@ -486,6 +489,7 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test dd7001ac86d09c154a7dff064f4739c60e2b312c +F test/fuzzer1.test 3105b5a89a6cb0d475f0877debec942fe4143462 F test/hook.test 040cf2ca263f192c66b358e095138dad0a9d75bb F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d @@ -573,7 +577,7 @@ F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9 F test/malloc_common.tcl 50d0ed21eed0ae9548b58935bd29ac89a05a54fa F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f -F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498 +F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test 679db68394a5692791737b150852173b3e2fea10 F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc @@ -588,7 +592,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 @@ -599,13 +603,13 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/omitunique.test bbb2ec4345d9125d9ee21cd9488d97a163020d5f F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec -F test/oserror.test 6c61c859cd94864cfd6af83e0549e2800238c413 +F test/oserror.test 498d8337e9d15543eb7b004fef8594bf204ff43c F test/pager1.test d8672fd0af5f4f9b99b06283d00f01547809bebe F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pagerfault.test 9de4d3e0c59970b4c6cb8dac511fa242f335d8a7 F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401 -F test/pagerfault3.test 9b413f48a3e9a9a8c26968118f8db19fd7bfb8c7 +F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7 F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806 F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 @@ -683,7 +687,7 @@ 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/syscall.test 02e5359bf4f5e6f716696318fde4e0b5c2c70d21 F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f F test/table.test 04ba066432430657712d167ebf28080fe878d305 F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 @@ -866,11 +870,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 @@ -878,7 +882,7 @@ F test/walbig.test e882bc1d014afffbfa2b6ba36e0f07d30a633ad0 F test/walcksum.test a37b36375c595e61bdb7e1ec49b5f0979b6fc7ce F test/walcrash.test e763841551d6b23677ccb419797c1589dcbdbaf5 F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142 -F test/walfault.test bf3808895f3b0751f6b0dbf52bb6d2e3a7b76a9d +F test/walfault.test 58fce626359c9376fe35101b5c0f2df8040aa839 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 22ddccd073c817ac9ead62b88ac446e8dedc7d2c F test/walnoshm.test a074428046408f4eb5c6a00e09df8cc97ff93317 @@ -906,9 +910,9 @@ F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/lemon.c dfd81a51b6e27e469ba21d01a75ddf092d429027 F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309 -F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e x +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 @@ -930,8 +934,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 f87bfe6e12093e5958b658a49d3766fb29562cbb d9707ef8dcd29667b6d366897f6ad02c87aa0041 -R e5326ee14e908456b763c8934a17defb -U dan -Z 5580ee572f02ed202a1934709ea63a74 +P d184cf00574b52b93f1f089025d6a0addca57558 425e3edb146c497817855dd741878709a25c8b98 +R ffd652e7771374b0d07d334e1f6024f2 +U drh +Z 333cd8aa259ea963e7224b1b34eb509c diff --git a/manifest.uuid b/manifest.uuid index da86ff7b0..5524b4073 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d184cf00574b52b93f1f089025d6a0addca57558
\ No newline at end of file +95d53c44320b9639f2623aa9cc88d0d3e1a3be8f
\ No newline at end of file diff --git a/src/btmutex.c b/src/btmutex.c index 201291a3e..42d0bdcbc 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -39,15 +39,34 @@ static void lockBtreeMutex(Btree *p){ ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p){ + BtShared *pBt = p->pBt; assert( p->locked==1 ); - assert( sqlite3_mutex_held(p->pBt->mutex) ); + assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3_mutex_held(p->db->mutex) ); - assert( p->db==p->pBt->db ); + assert( p->db==pBt->db ); - sqlite3_mutex_leave(p->pBt->mutex); + pBt->iMutexCounter++; + sqlite3_mutex_leave(pBt->mutex); p->locked = 0; } +#ifdef SQLITE_DEBUG +/* +** Return the number of times that the mutex has been exited for +** the given btree. +** +** This is a small circular counter that wraps around to zero on +** overflow. It is used only for sanity checking - to verify that +** mutexes are held continously by asserting that the value of +** this counter at the beginning of a region is the same as at +** the end. +*/ +u32 sqlite3BtreeMutexCounter(Btree *p){ + assert( p->locked==1 || p->sharable==0 ); + return p->pBt->iMutexCounter; +} +#endif + /* ** Enter a mutex on the given BTree object. ** @@ -92,6 +111,24 @@ void sqlite3BtreeEnter(Btree *p){ p->wantToLock++; if( p->locked ) return; + /* Increment the mutex counter on all locked btrees in the same + ** database connection. This simulates the unlocking that would + ** occur on a worst-case mutex dead-lock avoidance scenario. + */ +#ifdef SQLITE_DEBUG + { + int ii; + sqlite3 *db = p->db; + Btree *pOther; + for(ii=0; ii<db->nDb; ii++){ + if( ii==1 ) continue; + pOther = db->aDb[ii].pBt; + if( pOther==0 || pOther->sharable==0 || pOther->locked==0 ) continue; + pOther->pBt->iMutexCounter++; + } + } +#endif + /* In most cases, we should be able to acquire the lock we ** want without having to go throught the ascending lock ** procedure that follows. Just be sure not to block. @@ -195,7 +232,7 @@ void sqlite3BtreeEnterAll(sqlite3 *db){ if( !p->locked ){ assert( p->wantToLock==1 ); while( p->pPrev ) p = p->pPrev; - /* Reason for ALWAYS: There must be at least on unlocked Btree in + /* Reason for ALWAYS: There must be at least one unlocked Btree in ** the chain. Otherwise the !p->locked test above would have failed */ while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; for(pLater = p->pNext; pLater; pLater=pLater->pNext){ @@ -251,97 +288,17 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ } #endif /* NDEBUG */ +#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* -** Add a new Btree pointer to a BtreeMutexArray. -** if the pointer can possibly be shared with -** another database connection. +** The following are special cases for mutex enter routines for use +** in single threaded applications that use shared cache. Except for +** these two routines, all mutex operations are no-ops in that case and +** are null #defines in btree.h. ** -** The pointers are kept in sorted order by pBtree->pBt. That -** way when we go to enter all the mutexes, we can enter them -** in order without every having to backup and retry and without -** worrying about deadlock. -** -** The number of shared btrees will always be small (usually 0 or 1) -** so an insertion sort is an adequate algorithm here. +** If shared cache is disabled, then all btree mutex routines, including +** the ones below, are no-ops and are null #defines in btree.h. */ -void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){ - int i, j; - BtShared *pBt; - if( pBtree==0 || pBtree->sharable==0 ) return; -#ifndef NDEBUG - { - for(i=0; i<pArray->nMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - } - } -#endif - assert( pArray->nMutex>=0 ); - assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 ); - pBt = pBtree->pBt; - for(i=0; i<pArray->nMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - if( pArray->aBtree[i]->pBt>pBt ){ - for(j=pArray->nMutex; j>i; j--){ - pArray->aBtree[j] = pArray->aBtree[j-1]; - } - pArray->aBtree[i] = pBtree; - pArray->nMutex++; - return; - } - } - pArray->aBtree[pArray->nMutex++] = pBtree; -} - -/* -** Enter the mutex of every btree in the array. This routine is -** called at the beginning of sqlite3VdbeExec(). The mutexes are -** exited at the end of the same function. -*/ -void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){ - int i; - for(i=0; i<pArray->nMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); - assert( !p->locked || p->wantToLock>0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - /* The Btree is sharable because only sharable Btrees are entered - ** into the array in the first place. */ - assert( p->sharable ); - - p->wantToLock++; - if( !p->locked ){ - lockBtreeMutex(p); - } - } -} - -/* -** Leave the mutex of every btree in the group. -*/ -void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){ - int i; - for(i=0; i<pArray->nMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); - assert( p->locked ); - assert( p->wantToLock>0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } -} -#else void sqlite3BtreeEnter(Btree *p){ p->pBt->db = p->db; } diff --git a/src/btree.c b/src/btree.c index 088c555fc..d86b9edad 100644 --- a/src/btree.c +++ b/src/btree.c @@ -7990,7 +7990,7 @@ int sqlite3BtreeIsInBackup(Btree *p){ ** ** Just before the shared-btree is closed, the function passed as the ** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. This function should not call sqlite3_free() +** blob of allocated memory. The xFree function should not call sqlite3_free() ** on the memory, the btree layer does that. */ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ diff --git a/src/btree.h b/src/btree.h index 468723b33..4fd4f6768 100644 --- a/src/btree.h +++ b/src/btree.h @@ -39,18 +39,6 @@ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; -typedef struct BtreeMutexArray BtreeMutexArray; - -/* -** This structure records all of the Btrees that need to hold -** a mutex before we enter sqlite3VdbeExec(). The Btrees are -** are placed in aBtree[] in order of aBtree[]->pBt. That way, -** we can always lock and unlock them all quickly. -*/ -struct BtreeMutexArray { - int nMutex; - Btree *aBtree[SQLITE_MAX_ATTACHED+1]; -}; int sqlite3BtreeOpen( @@ -228,23 +216,19 @@ void sqlite3BtreeCursorList(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); void sqlite3BtreeLeaveCursor(BtCursor*); void sqlite3BtreeLeaveAll(sqlite3*); - void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); - void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); - void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ int sqlite3BtreeHoldsMutex(Btree*); int sqlite3BtreeHoldsAllMutexes(sqlite3*); + u32 sqlite3BtreeMutexCounter(Btree*); #endif #else # define sqlite3BtreeLeave(X) +# define sqlite3BtreeMutexCounter(X) 0 # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) -# define sqlite3BtreeMutexArrayEnter(X) -# define sqlite3BtreeMutexArrayLeave(X) -# define sqlite3BtreeMutexArrayInsert(X,Y) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 diff --git a/src/btreeInt.h b/src/btreeInt.h index 0e71195b0..2aee063d3 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -336,7 +336,7 @@ struct BtLock { ** All fields in this structure are accessed under sqlite3.mutex. ** The pBt pointer itself may not be changed while there exists cursors ** in the referenced BtShared that point back to this Btree since those -** cursors have to do go through this Btree to find their BtShared and +** cursors have to go through this Btree to find their BtShared and ** they often do so without holding sqlite3.mutex. */ struct Btree { @@ -426,7 +426,7 @@ struct BtShared { u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ + sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ @@ -435,6 +435,7 @@ struct BtShared { Btree *pWriter; /* Btree with currently open write transaction */ u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */ u8 isPending; /* If waiting for read-locks to clear */ + u16 iMutexCounter; /* The number of mutex_leave(mutex) calls */ #endif u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ }; diff --git a/src/build.c b/src/build.c index bf3273fc8..b4528347d 100644 --- a/src/build.c +++ b/src/build.c @@ -148,7 +148,7 @@ void sqlite3FinishCoding(Parse *pParse){ ** on each used database. */ if( pParse->cookieGoto>0 ){ - tAttachMask mask; + yDbMask mask; int iDb; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ @@ -3444,12 +3444,12 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ } if( iDb>=0 ){ sqlite3 *db = pToplevel->db; - tAttachMask mask; + yDbMask mask; assert( iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDb<SQLITE_MAX_ATTACHED+2 ); - mask = ((tAttachMask)1)<<iDb; + mask = ((yDbMask)1)<<iDb; if( (pToplevel->cookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; @@ -3476,7 +3476,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - pToplevel->writeMask |= ((tAttachMask)1)<<iDb; + pToplevel->writeMask |= ((yDbMask)1)<<iDb; pToplevel->isMultiWrite |= setStatement; } diff --git a/src/os_unix.c b/src/os_unix.c index f04b6af27..67dd06fc1 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -394,10 +394,10 @@ static int unixSetSystemCall( /* If no zName is given, restore all system calls to their default ** settings and return NULL */ + rc = SQLITE_OK; for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ if( aSyscall[i].pDefault ){ aSyscall[i].pCurrent = aSyscall[i].pDefault; - rc = SQLITE_OK; } } }else{ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index d31ed4ae6..ce1e106df 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2133,11 +2133,13 @@ struct TriggerPrg { TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ }; -/* Datatype for the bitmask of all attached databases */ +/* +** The yDbMask datatype for the bitmask of all attached databases. +*/ #if SQLITE_MAX_ATTACHED>30 - typedef sqlite3_uint64 tAttachMask; + typedef sqlite3_uint64 yDbMask; #else - typedef unsigned int tAttachMask; + typedef unsigned int yDbMask; #endif /* @@ -2188,8 +2190,8 @@ struct Parse { int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ - tAttachMask writeMask; /* Start a write transaction on these databases */ - tAttachMask cookieMask; /* Bitmask of schema verified databases */ + yDbMask writeMask; /* Start a write transaction on these databases */ + yDbMask cookieMask; /* Bitmask of schema verified databases */ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ diff --git a/src/tclsqlite.c b/src/tclsqlite.c index de0e992bd..e1f451541 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -3728,6 +3728,8 @@ static void init_all(Tcl_Interp *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*); + extern int Sqlitetestwholenumber_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) extern int TestSession_Init(Tcl_Interp*); #endif @@ -3768,6 +3770,8 @@ static void init_all(Tcl_Interp *interp){ Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); + Sqlitetestfuzzer_Init(interp); + Sqlitetestwholenumber_Init(interp); #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) TestSession_Init(interp); #endif diff --git a/src/test_fuzzer.c b/src/test_fuzzer.c new file mode 100644 index 000000000..cf5925717 --- /dev/null +++ b/src/test_fuzzer.c @@ -0,0 +1,944 @@ +/* +** 2011 March 24 +** +** 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. +** +************************************************************************* +** +** Code for demonstartion virtual table that generates variations +** on an input word at increasing edit distances from the original. +** +** A fuzzer virtual table is created like this: +** +** CREATE VIRTUAL TABLE temp.f USING fuzzer; +** +** The name of the new virtual table in the example above is "f". +** Note that all fuzzer virtual tables must be TEMP tables. The +** "temp." prefix in front of the table name is required when the +** table is being created. The "temp." prefix can be omitted when +** using the table as long as the name is unambiguous. +** +** Before being used, the fuzzer needs to be programmed by giving it +** character transformations and a cost associated with each transformation. +** Examples: +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); +** +** The above statement says that the cost of inserting a letter 'a' is +** 100. (All costs are integers. We recommend that costs be scaled so +** that the average cost is around 100.) +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); +** +** The above statement says that the cost of deleting a single letter +** 'b' is 87. +** +** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); +** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); +** +** This third example says that the cost of transforming the single +** letter "o" into the two-letter sequence "oe" is 38 and that the +** cost of transforming "oe" back into "o" is 40. +** +** After all the transformation costs have been set, the fuzzer table +** can be queried as follows: +** +** SELECT word, distance FROM f +** WHERE word MATCH 'abcdefg' +** AND distance<200; +** +** This first query outputs the string "abcdefg" and all strings that +** can be derived from that string by appling the specified transformations. +** The strings are output together with their total transformation cost +** (called "distance") and appear in order of increasing cost. No string +** is output more than once. If there are multiple ways to transform the +** target string into the output string then the lowest cost transform is +** the one that is returned. In the example, the search is limited to +** strings with a total distance of less than 200. +** +** It is important to put some kind of a limit on the fuzzer output. This +** can be either in the form of a LIMIT clause at the end of the query, +** or better, a "distance<NNN" constraint where NNN is some number. The +** running time and memory requirement is exponential in the value of NNN +** so you want to make sure that NNN is not too big. A value of NNN that +** is about twice the average transformation cost seems to give good results. +** +** The fuzzer table can be useful for tasks such as spelling correction. +** Suppose there is a second table vocabulary(w) where the w column contains +** all correctly spelled words. Let $word be a word you want to look up. +** +** SELECT vocabulary.w FROM f, vocabulary +** WHERE f.word MATCH $word +** AND f.distance<=200 +** AND f.word=vocabulary.w +** LIMIT 20 +** +** The query above gives the 20 closest words to the $word being tested. +** (Note that for good performance, the vocubulary.w column should be +** indexed.) +** +** A similar query can be used to find all words in the dictionary that +** begin with some prefix $prefix: +** +** SELECT vocabulary.w FROM f, vocabulary +** WHERE f.word MATCH $prefix +** AND f.distance<=200 +** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF') +** LIMIT 50 +** +** This last query will show up to 50 words out of the vocabulary that +** match or nearly match the $prefix. +*/ +#include "sqlite3.h" +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +/* +** Forward declaration of objects used by this implementation +*/ +typedef struct fuzzer_vtab fuzzer_vtab; +typedef struct fuzzer_cursor fuzzer_cursor; +typedef struct fuzzer_rule fuzzer_rule; +typedef struct fuzzer_seen fuzzer_seen; +typedef struct fuzzer_stem fuzzer_stem; + +/* +** Type of the "cost" of an edit operation. Might be changed to +** "float" or "double" or "sqlite3_int64" in the future. +*/ +typedef int fuzzer_cost; + + +/* +** Each transformation rule is stored as an instance of this object. +** All rules are kept on a linked list sorted by rCost. +*/ +struct fuzzer_rule { + fuzzer_rule *pNext; /* Next rule in order of increasing rCost */ + fuzzer_cost rCost; /* Cost of this transformation */ + int nFrom, nTo; /* Length of the zFrom and zTo strings */ + char *zFrom; /* Transform from */ + char zTo[4]; /* Transform to (extra space appended) */ +}; + +/* +** A stem object is used to generate variants. It is also used to record +** previously generated outputs. +** +** Every stem is added to a hash table as it is output. Generation of +** duplicate stems is suppressed. +** +** Active stems (those that might generate new outputs) are kepts on a linked +** list sorted by increasing cost. The cost is the sum of rBaseCost and +** pRule->rCost. +*/ +struct fuzzer_stem { + char *zBasis; /* Word being fuzzed */ + int nBasis; /* Length of the zBasis string */ + const fuzzer_rule *pRule; /* Current rule to apply */ + int n; /* Apply pRule at this character offset */ + fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */ + fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */ + fuzzer_stem *pNext; /* Next stem in rCost order */ + fuzzer_stem *pHash; /* Next stem with same hash on zBasis */ +}; + +/* +** A fuzzer virtual-table object +*/ +struct fuzzer_vtab { + sqlite3_vtab base; /* Base class - must be first */ + char *zClassName; /* Name of this class. Default: "fuzzer" */ + fuzzer_rule *pRule; /* All active rules in this fuzzer */ + fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */ + int nCursor; /* Number of active cursors */ +}; + +#define FUZZER_HASH 4001 /* Hash table size */ +#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */ + +/* A fuzzer cursor object */ +struct fuzzer_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_int64 iRowid; /* The rowid of the current word */ + fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */ + fuzzer_cost rLimit; /* Maximum cost of any term */ + fuzzer_stem *pStem; /* Stem with smallest rCostX */ + fuzzer_stem *pDone; /* Stems already processed to completion */ + fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */ + int mxQueue; /* Largest used index in aQueue[] */ + char *zBuf; /* Temporary use buffer */ + int nBuf; /* Bytes allocated for zBuf */ + int nStem; /* Number of stems allocated */ + fuzzer_rule nullRule; /* Null rule used first */ + fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ +}; + +/* Methods for the fuzzer module */ +static int fuzzerConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + fuzzer_vtab *pNew; + int n; + if( strcmp(argv[1],"temp")!=0 ){ + *pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]); + return SQLITE_ERROR; + } + n = strlen(argv[0]) + 1; + pNew = sqlite3_malloc( sizeof(*pNew) + n ); + if( pNew==0 ) return SQLITE_NOMEM; + pNew->zClassName = (char*)&pNew[1]; + memcpy(pNew->zClassName, argv[0], n); + sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)"); + memset(pNew, 0, sizeof(*pNew)); + *ppVtab = &pNew->base; + return SQLITE_OK; +} +/* Note that for this virtual table, the xCreate and xConnect +** methods are identical. */ + +static int fuzzerDisconnect(sqlite3_vtab *pVtab){ + fuzzer_vtab *p = (fuzzer_vtab*)pVtab; + assert( p->nCursor==0 ); + do{ + while( p->pRule ){ + fuzzer_rule *pRule = p->pRule; + p->pRule = pRule->pNext; + sqlite3_free(pRule); + } + p->pRule = p->pNewRule; + p->pNewRule = 0; + }while( p->pRule ); + sqlite3_free(p); + return SQLITE_OK; +} +/* The xDisconnect and xDestroy methods are also the same */ + +/* +** The two input rule lists are both sorted in order of increasing +** cost. Merge them together into a single list, sorted by cost, and +** return a pointer to the head of that list. +*/ +static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){ + fuzzer_rule head; + fuzzer_rule *pTail; + + pTail = &head; + while( pA && pB ){ + if( pA->rCost<=pB->rCost ){ + pTail->pNext = pA; + pTail = pA; + pA = pA->pNext; + }else{ + pTail->pNext = pB; + pTail = pB; + pB = pB->pNext; + } + } + if( pA==0 ){ + pTail->pNext = pB; + }else{ + pTail->pNext = pA; + } + return head.pNext; +} + + +/* +** Open a new fuzzer cursor. +*/ +static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + fuzzer_vtab *p = (fuzzer_vtab*)pVTab; + fuzzer_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + pCur->pVtab = p; + *ppCursor = &pCur->base; + if( p->nCursor==0 && p->pNewRule ){ + unsigned int i; + fuzzer_rule *pX; + fuzzer_rule *a[15]; + for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0; + while( (pX = p->pNewRule)!=0 ){ + p->pNewRule = pX->pNext; + pX->pNext = 0; + for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){ + pX = fuzzerMergeRules(a[i], pX); + a[i] = 0; + } + a[i] = fuzzerMergeRules(a[i], pX); + } + for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){ + pX = fuzzerMergeRules(a[i], pX); + } + p->pRule = fuzzerMergeRules(p->pRule, pX); + } + p->nCursor++; + return SQLITE_OK; +} + +/* +** Free all stems in a list. +*/ +static void fuzzerClearStemList(fuzzer_stem *pStem){ + while( pStem ){ + fuzzer_stem *pNext = pStem->pNext; + sqlite3_free(pStem); + pStem = pNext; + } +} + +/* +** Free up all the memory allocated by a cursor. Set it rLimit to 0 +** to indicate that it is at EOF. +*/ +static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){ + int i; + fuzzerClearStemList(pCur->pStem); + fuzzerClearStemList(pCur->pDone); + for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]); + pCur->rLimit = (fuzzer_cost)0; + if( clearHash && pCur->nStem ){ + pCur->mxQueue = 0; + pCur->pStem = 0; + pCur->pDone = 0; + memset(pCur->aQueue, 0, sizeof(pCur->aQueue)); + memset(pCur->apHash, 0, sizeof(pCur->apHash)); + } + pCur->nStem = 0; +} + +/* +** Close a fuzzer cursor. +*/ +static int fuzzerClose(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor *)cur; + fuzzerClearCursor(pCur, 0); + sqlite3_free(pCur->zBuf); + pCur->pVtab->nCursor--; + sqlite3_free(pCur); + return SQLITE_OK; +} + +/* +** Compute the current output term for a fuzzer_stem. +*/ +static int fuzzerRender( + fuzzer_stem *pStem, /* The stem to be rendered */ + char **pzBuf, /* Write results into this buffer. realloc if needed */ + int *pnBuf /* Size of the buffer */ +){ + const fuzzer_rule *pRule = pStem->pRule; + int n; + char *z; + + n = pStem->nBasis + pRule->nTo - pRule->nFrom; + if( (*pnBuf)<n+1 ){ + (*pzBuf) = sqlite3_realloc((*pzBuf), n+100); + if( (*pzBuf)==0 ) return SQLITE_NOMEM; + (*pnBuf) = n+100; + } + n = pStem->n; + z = *pzBuf; + if( n<0 ){ + memcpy(z, pStem->zBasis, pStem->nBasis+1); + }else{ + memcpy(z, pStem->zBasis, n); + memcpy(&z[n], pRule->zTo, pRule->nTo); + memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], + pStem->nBasis-n-pRule->nFrom+1); + } + return SQLITE_OK; +} + +/* +** Compute a hash on zBasis. +*/ +static unsigned int fuzzerHash(const char *z){ + unsigned int h = 0; + while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); } + return h % FUZZER_HASH; +} + +/* +** Current cost of a stem +*/ +static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){ + return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost; +} + +#if 0 +/* +** Print a description of a fuzzer_stem on stderr. +*/ +static void fuzzerStemPrint( + const char *zPrefix, + fuzzer_stem *pStem, + const char *zSuffix +){ + if( pStem->n<0 ){ + fprintf(stderr, "%s[%s](%d)-->self%s", + zPrefix, + pStem->zBasis, pStem->rBaseCost, + zSuffix + ); + }else{ + char *zBuf = 0; + int nBuf = 0; + if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return; + fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s", + zPrefix, + pStem->zBasis, pStem->rBaseCost, zBuf, pStem->, + zSuffix + ); + sqlite3_free(zBuf); + } +} +#endif + +/* +** Return 1 if the string to which the cursor is point has already +** been emitted. Return 0 if not. Return -1 on a memory allocation +** failures. +*/ +static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){ + unsigned int h; + fuzzer_stem *pLookup; + + if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ + return -1; + } + h = fuzzerHash(pCur->zBuf); + pLookup = pCur->apHash[h]; + while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){ + pLookup = pLookup->pHash; + } + return pLookup!=0; +} + +/* +** Advance a fuzzer_stem to its next value. Return 0 if there are +** no more values that can be generated by this fuzzer_stem. Return +** -1 on a memory allocation failure. +*/ +static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ + const fuzzer_rule *pRule; + while( (pRule = pStem->pRule)!=0 ){ + while( pStem->n < pStem->nBasis - pRule->nFrom ){ + pStem->n++; + if( pRule->nFrom==0 + || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0 + ){ + /* Found a rewrite case. Make sure it is not a duplicate */ + int rc = fuzzerSeen(pCur, pStem); + if( rc<0 ) return -1; + if( rc==0 ){ + fuzzerCost(pStem); + return 1; + } + } + } + pStem->n = -1; + pStem->pRule = pRule->pNext; + if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; + } + return 0; +} + +/* +** The two input stem lists are both sorted in order of increasing +** rCostX. Merge them together into a single list, sorted by rCostX, and +** return a pointer to the head of that new list. +*/ +static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){ + fuzzer_stem head; + fuzzer_stem *pTail; + + pTail = &head; + while( pA && pB ){ + if( pA->rCostX<=pB->rCostX ){ + pTail->pNext = pA; + pTail = pA; + pA = pA->pNext; + }else{ + pTail->pNext = pB; + pTail = pB; + pB = pB->pNext; + } + } + if( pA==0 ){ + pTail->pNext = pB; + }else{ + pTail->pNext = pA; + } + return head.pNext; +} + +/* +** Load pCur->pStem with the lowest-cost stem. Return a pointer +** to the lowest-cost stem. +*/ +static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){ + fuzzer_stem *pBest, *pX; + int iBest; + int i; + + if( pCur->pStem==0 ){ + iBest = -1; + pBest = 0; + for(i=0; i<=pCur->mxQueue; i++){ + pX = pCur->aQueue[i]; + if( pX==0 ) continue; + if( pBest==0 || pBest->rCostX>pX->rCostX ){ + pBest = pX; + iBest = i; + } + } + if( pBest ){ + pCur->aQueue[iBest] = pBest->pNext; + pBest->pNext = 0; + pCur->pStem = pBest; + } + } + return pCur->pStem; +} + +/* +** Insert pNew into queue of pending stems. Then find the stem +** with the lowest rCostX and move it into pCur->pStem. +** list. The insert is done such the pNew is in the correct order +** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost. +*/ +static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){ + fuzzer_stem *pX; + int i; + + /* If pCur->pStem exists and is greater than pNew, then make pNew + ** the new pCur->pStem and insert the old pCur->pStem instead. + */ + if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){ + pNew->pNext = 0; + pCur->pStem = pNew; + pNew = pX; + } + + /* Insert the new value */ + pNew->pNext = 0; + pX = pNew; + for(i=0; i<=pCur->mxQueue; i++){ + if( pCur->aQueue[i] ){ + pX = fuzzerMergeStems(pX, pCur->aQueue[i]); + pCur->aQueue[i] = 0; + }else{ + pCur->aQueue[i] = pX; + break; + } + } + if( i>pCur->mxQueue ){ + if( i<FUZZER_NQUEUE ){ + pCur->mxQueue = i; + pCur->aQueue[i] = pX; + }else{ + assert( pCur->mxQueue==FUZZER_NQUEUE-1 ); + pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]); + pCur->aQueue[FUZZER_NQUEUE-1] = pX; + } + } + + return fuzzerLowestCostStem(pCur); +} + +/* +** Allocate a new fuzzer_stem. Add it to the hash table but do not +** link it into either the pCur->pStem or pCur->pDone lists. +*/ +static fuzzer_stem *fuzzerNewStem( + fuzzer_cursor *pCur, + const char *zWord, + fuzzer_cost rBaseCost +){ + fuzzer_stem *pNew; + unsigned int h; + + pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 ); + if( pNew==0 ) return 0; + memset(pNew, 0, sizeof(*pNew)); + pNew->zBasis = (char*)&pNew[1]; + pNew->nBasis = strlen(zWord); + memcpy(pNew->zBasis, zWord, pNew->nBasis+1); + pNew->pRule = pCur->pVtab->pRule; + pNew->n = -1; + pNew->rBaseCost = pNew->rCostX = rBaseCost; + h = fuzzerHash(pNew->zBasis); + pNew->pHash = pCur->apHash[h]; + pCur->apHash[h] = pNew; + pCur->nStem++; + return pNew; +} + + +/* +** Advance a cursor to its next row of output +*/ +static int fuzzerNext(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + int rc; + fuzzer_stem *pStem, *pNew; + + pCur->iRowid++; + + /* Use the element the cursor is currently point to to create + ** a new stem and insert the new stem into the priority queue. + */ + pStem = pCur->pStem; + if( pStem->rCostX>0 ){ + rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf); + if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; + pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX); + if( pNew ){ + if( fuzzerAdvance(pCur, pNew)==0 ){ + pNew->pNext = pCur->pDone; + pCur->pDone = pNew; + }else{ + if( fuzzerInsert(pCur, pNew)==pNew ){ + return SQLITE_OK; + } + } + }else{ + return SQLITE_NOMEM; + } + } + + /* Adjust the priority queue so that the first element of the + ** stem list is the next lowest cost word. + */ + while( (pStem = pCur->pStem)!=0 ){ + if( fuzzerAdvance(pCur, pStem) ){ + pCur->pStem = 0; + pStem = fuzzerInsert(pCur, pStem); + if( (rc = fuzzerSeen(pCur, pStem))!=0 ){ + if( rc<0 ) return SQLITE_NOMEM; + continue; + } + return SQLITE_OK; /* New word found */ + } + pCur->pStem = 0; + pStem->pNext = pCur->pDone; + pCur->pDone = pStem; + if( fuzzerLowestCostStem(pCur) ){ + rc = fuzzerSeen(pCur, pCur->pStem); + if( rc<0 ) return SQLITE_NOMEM; + if( rc==0 ){ + return SQLITE_OK; + } + } + } + + /* Reach this point only if queue has been exhausted and there is + ** nothing left to be output. */ + pCur->rLimit = (fuzzer_cost)0; + return SQLITE_OK; +} + +/* +** Called to "rewind" a cursor back to the beginning so that +** it starts its output over again. Always called at least once +** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call. +*/ +static int fuzzerFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor; + const char *zWord = 0; + fuzzer_stem *pStem; + + fuzzerClearCursor(pCur, 1); + pCur->rLimit = 2147483647; + if( idxNum==1 ){ + zWord = (const char*)sqlite3_value_text(argv[0]); + }else if( idxNum==2 ){ + pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]); + }else if( idxNum==3 ){ + zWord = (const char*)sqlite3_value_text(argv[0]); + pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]); + } + if( zWord==0 ) zWord = ""; + pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0); + if( pStem==0 ) return SQLITE_NOMEM; + pCur->nullRule.pNext = pCur->pVtab->pRule; + pCur->nullRule.rCost = 0; + pCur->nullRule.nFrom = 0; + pCur->nullRule.nTo = 0; + pCur->nullRule.zFrom = ""; + pStem->pRule = &pCur->nullRule; + pStem->n = pStem->nBasis; + pCur->iRowid = 1; + return SQLITE_OK; +} + +/* +** Only the word and distance columns have values. All other columns +** return NULL +*/ +static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + if( i==0 ){ + /* the "word" column */ + if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ + return SQLITE_NOMEM; + } + sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT); + }else if( i==1 ){ + /* the "distance" column */ + sqlite3_result_int(ctx, pCur->pStem->rCostX); + }else{ + /* All other columns are NULL */ + sqlite3_result_null(ctx); + } + return SQLITE_OK; +} + +/* +** The rowid. +*/ +static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + *pRowid = pCur->iRowid; + return SQLITE_OK; +} + +/* +** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal +** that the cursor has nothing more to output. +*/ +static int fuzzerEof(sqlite3_vtab_cursor *cur){ + fuzzer_cursor *pCur = (fuzzer_cursor*)cur; + return pCur->rLimit<=(fuzzer_cost)0; +} + +/* +** Search for terms of these forms: +** +** word MATCH $str +** distance < $value +** distance <= $value +** +** The distance< and distance<= are both treated as distance<=. +** The query plan number is as follows: +** +** 0: None of the terms above are found +** 1: There is a "word MATCH" term with $str in filter.argv[0]. +** 2: There is a "distance<" term with $value in filter.argv[0]. +** 3: Both "word MATCH" and "distance<" with $str in argv[0] and +** $value in argv[1]. +*/ +static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + int iPlan = 0; + int iDistTerm = -1; + int i; + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( (iPlan & 1)==0 + && pConstraint->iColumn==0 + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH + ){ + iPlan |= 1; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + if( (iPlan & 2)==0 + && pConstraint->iColumn==1 + && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT + || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) + ){ + iPlan |= 2; + iDistTerm = i; + } + } + if( iPlan==2 ){ + pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1; + }else if( iPlan==3 ){ + pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2; + } + pIdxInfo->idxNum = iPlan; + if( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].iColumn==1 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + pIdxInfo->estimatedCost = (double)10000; + + return SQLITE_OK; +} + +/* +** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed. +** +** On an insert, the cFrom, cTo, and cost columns are used to construct +** a new rule. All other columns are ignored. The rule is ignored +** if cFrom and cTo are identical. A NULL value for cFrom or cTo is +** interpreted as an empty string. The cost must be positive. +*/ +static int fuzzerUpdate( + sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + fuzzer_vtab *p = (fuzzer_vtab*)pVTab; + fuzzer_rule *pRule; + const char *zFrom; + int nFrom; + const char *zTo; + int nTo; + fuzzer_cost rCost; + if( argc!=7 ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table", + p->zClassName); + return SQLITE_CONSTRAINT; + } + if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table", + p->zClassName); + return SQLITE_CONSTRAINT; + } + zFrom = (char*)sqlite3_value_text(argv[4]); + if( zFrom==0 ) zFrom = ""; + zTo = (char*)sqlite3_value_text(argv[5]); + if( zTo==0 ) zTo = ""; + if( strcmp(zFrom,zTo)==0 ){ + /* Silently ignore null transformations */ + return SQLITE_OK; + } + rCost = sqlite3_value_int(argv[6]); + if( rCost<=0 ){ + sqlite3_free(pVTab->zErrMsg); + pVTab->zErrMsg = sqlite3_mprintf("cost must be positive"); + return SQLITE_CONSTRAINT; + } + nFrom = strlen(zFrom); + nTo = strlen(zTo); + pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo ); + if( pRule==0 ){ + return SQLITE_NOMEM; + } + pRule->zFrom = &pRule->zTo[nTo+1]; + pRule->nFrom = nFrom; + memcpy(pRule->zFrom, zFrom, nFrom+1); + memcpy(pRule->zTo, zTo, nTo+1); + pRule->nTo = nTo; + pRule->rCost = rCost; + pRule->pNext = p->pNewRule; + p->pNewRule = pRule; + return SQLITE_OK; +} + +/* +** A virtual table module that provides read-only access to a +** Tcl global variable namespace. +*/ +static sqlite3_module fuzzerModule = { + 0, /* iVersion */ + fuzzerConnect, + fuzzerConnect, + fuzzerBestIndex, + fuzzerDisconnect, + fuzzerDisconnect, + fuzzerOpen, /* xOpen - open a cursor */ + fuzzerClose, /* xClose - close a cursor */ + fuzzerFilter, /* xFilter - configure scan constraints */ + fuzzerNext, /* xNext - advance a cursor */ + fuzzerEof, /* xEof - check for end of scan */ + fuzzerColumn, /* xColumn - read data */ + fuzzerRowid, /* xRowid - read data */ + fuzzerUpdate, /* xUpdate - INSERT */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +/* +** Register the fuzzer virtual table +*/ +int fuzzer_register(sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0); +#endif + return rc; +} + +#ifdef SQLITE_TEST +#include <tcl.h> +/* +** Decode a pointer to an sqlite3 object. +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* +** Register the echo virtual table module. +*/ +static int register_fuzzer_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + fuzzer_register(db); + return TCL_OK; +} + + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetestfuzzer_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_fuzzer_module", register_fuzzer_module, 0 }, + }; + int i; + for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ + Tcl_CreateObjCommand(interp, aObjCmd[i].zName, + aObjCmd[i].xProc, aObjCmd[i].clientData, 0); + } + return TCL_OK; +} + +#endif /* SQLITE_TEST */ 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 index dbac01d69..c570b71e8 100644 --- a/src/test_syscall.c +++ b/src/test_syscall.c @@ -214,6 +214,12 @@ static int ts_open(const char *zFile, int flags, int mode){ */ 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); @@ -593,6 +599,24 @@ static int test_syscall_list( return TCL_OK; } +static int test_syscall_defaultvfs( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_vfs *pVfs; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 2, objv, ""); + return TCL_ERROR; + } + + pVfs = sqlite3_vfs_find(0); + Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1)); + return TCL_OK; +} + static int test_syscall( void * clientData, Tcl_Interp *interp, @@ -603,13 +627,14 @@ static int test_syscall( 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 }, + { "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 }, + { "defaultvfs", test_syscall_defaultvfs }, { 0, 0 } }; int iCmd; diff --git a/src/test_wholenumber.c b/src/test_wholenumber.c new file mode 100644 index 000000000..150dc95ac --- /dev/null +++ b/src/test_wholenumber.c @@ -0,0 +1,311 @@ +/* +** 2011 April 02 +** +** 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 a virtual table that returns the whole numbers +** between 1 and 4294967295, inclusive. +** +** Example: +** +** CREATE VIRTUAL TABLE nums USING wholenumber; +** SELECT value FROM nums WHERE value<10; +** +** Results in: +** +** 1 2 3 4 5 6 7 8 9 +*/ +#include "sqlite3.h" +#include <assert.h> +#include <string.h> + +#ifndef SQLITE_OMIT_VIRTUALTABLE + + +/* A wholenumber cursor object */ +typedef struct wholenumber_cursor wholenumber_cursor; +struct wholenumber_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + unsigned iValue; /* Current value */ + unsigned mxValue; /* Maximum value */ +}; + +/* Methods for the wholenumber module */ +static int wholenumberConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + sqlite3_vtab *pNew; + pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + if( pNew==0 ) return SQLITE_NOMEM; + sqlite3_declare_vtab(db, "CREATE TABLE x(value)"); + memset(pNew, 0, sizeof(*pNew)); + return SQLITE_OK; +} +/* Note that for this virtual table, the xCreate and xConnect +** methods are identical. */ + +static int wholenumberDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} +/* The xDisconnect and xDestroy methods are also the same */ + + +/* +** Open a new wholenumber cursor. +*/ +static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + wholenumber_cursor *pCur; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *ppCursor = &pCur->base; + return SQLITE_OK; +} + +/* +** Close a wholenumber cursor. +*/ +static int wholenumberClose(sqlite3_vtab_cursor *cur){ + sqlite3_free(cur); + return SQLITE_OK; +} + + +/* +** Advance a cursor to its next row of output +*/ +static int wholenumberNext(sqlite3_vtab_cursor *cur){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + pCur->iValue++; + return SQLITE_OK; +} + +/* +** Return the value associated with a wholenumber. +*/ +static int wholenumberColumn( + sqlite3_vtab_cursor *cur, + sqlite3_context *ctx, + int i +){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + sqlite3_result_int64(ctx, pCur->iValue); + return SQLITE_OK; +} + +/* +** The rowid. +*/ +static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + *pRowid = pCur->iValue; + return SQLITE_OK; +} + +/* +** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal +** that the cursor has nothing more to output. +*/ +static int wholenumberEof(sqlite3_vtab_cursor *cur){ + wholenumber_cursor *pCur = (wholenumber_cursor*)cur; + return pCur->iValue>pCur->mxValue || pCur->iValue==0; +} + +/* +** Called to "rewind" a cursor back to the beginning so that +** it starts its output over again. Always called at least once +** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call. +** +** idxNum Constraints +** ------ --------------------- +** 0 (none) +** 1 value > $argv0 +** 2 value >= $argv0 +** 4 value < $argv0 +** 8 value <= $argv0 +** +** 5 value > $argv0 AND value < $argv1 +** 6 value >= $argv0 AND value < $argv1 +** 9 value > $argv0 AND value <= $argv1 +** 10 value >= $argv0 AND value <= $argv1 +*/ +static int wholenumberFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor; + sqlite3_int64 v; + int i = 0; + pCur->iValue = 1; + pCur->mxValue = 0xffffffff; /* 4294967295 */ + if( idxNum & 3 ){ + v = sqlite3_value_int64(argv[0]) + (idxNum&1); + if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v; + i++; + } + if( idxNum & 12 ){ + v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1); + if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v; + } + return SQLITE_OK; +} + +/* +** Search for terms of these forms: +** +** (1) value > $value +** (2) value >= $value +** (4) value < $value +** (8) value <= $value +** +** idxNum is an ORed combination of 1 or 2 with 4 or 8. +*/ +static int wholenumberBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int i; + int idxNum = 0; + int argvIdx = 1; + int ltIdx = -1; + int gtIdx = -1; + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){ + idxNum |= 1; + ltIdx = i; + } + if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){ + idxNum |= 2; + ltIdx = i; + } + if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ + idxNum |= 4; + gtIdx = i; + } + if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){ + idxNum |= 8; + gtIdx = i; + } + } + pIdxInfo->idxNum = idxNum; + if( ltIdx>=0 ){ + pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++; + pIdxInfo->aConstraintUsage[ltIdx].omit = 1; + } + if( gtIdx>=0 ){ + pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx; + pIdxInfo->aConstraintUsage[gtIdx].omit = 1; + } + if( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].desc==0 + ){ + pIdxInfo->orderByConsumed = 1; + } + pIdxInfo->estimatedCost = (double)1; + return SQLITE_OK; +} + +/* +** A virtual table module that provides read-only access to a +** Tcl global variable namespace. +*/ +static sqlite3_module wholenumberModule = { + 0, /* iVersion */ + wholenumberConnect, + wholenumberConnect, + wholenumberBestIndex, + wholenumberDisconnect, + wholenumberDisconnect, + wholenumberOpen, /* xOpen - open a cursor */ + wholenumberClose, /* xClose - close a cursor */ + wholenumberFilter, /* xFilter - configure scan constraints */ + wholenumberNext, /* xNext - advance a cursor */ + wholenumberEof, /* xEof - check for end of scan */ + wholenumberColumn, /* xColumn - read data */ + wholenumberRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +/* +** Register the wholenumber virtual table +*/ +int wholenumber_register(sqlite3 *db){ + int rc = SQLITE_OK; +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0); +#endif + return rc; +} + +#ifdef SQLITE_TEST +#include <tcl.h> +/* +** Decode a pointer to an sqlite3 object. +*/ +extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); + +/* +** Register the echo virtual table module. +*/ +static int register_wholenumber_module( + ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + sqlite3 *db; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB"); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + wholenumber_register(db); + return TCL_OK; +} + + +/* +** Register commands with the TCL interpreter. +*/ +int Sqlitetestwholenumber_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + void *clientData; + } aObjCmd[] = { + { "register_wholenumber_module", register_wholenumber_module, 0 }, + }; + int i; + for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ + Tcl_CreateObjCommand(interp, aObjCmd[i].zName, + aObjCmd[i].xProc, aObjCmd[i].clientData, 0); + } + return TCL_OK; +} + +#endif /* SQLITE_TEST */ diff --git a/src/vdbe.c b/src/vdbe.c index e85dc85f6..b0e66317c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -581,7 +581,7 @@ int sqlite3VdbeExec( /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -1404,6 +1404,7 @@ case OP_Function: { ctx.pColl = pOp[-1].p4.pColl; } (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ + sqlite3VdbeMutexResync(p); if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function @@ -1435,6 +1436,15 @@ case OP_Function: { if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } + +#if 0 + /* The app-defined function has done something that as caused this + ** statement to expire. (Perhaps the function called sqlite3_exec() + ** with a CREATE TABLE statement.) + */ + if( p->expired ) rc = SQLITE_ABORT; +#endif + REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; @@ -2660,6 +2670,7 @@ case OP_Savepoint: { if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); + sqlite3VdbeMutexResync(p); db->flags = (db->flags | SQLITE_InternChanges); } } @@ -2799,7 +2810,7 @@ case OP_Transaction: { Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ @@ -2855,7 +2866,7 @@ case OP_ReadCookie: { /* out2-prerelease */ assert( pOp->p3<SQLITE_N_BTREE_META ); assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); - assert( (p->btreeMask & (1<<iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); pOut->u.i = iMeta; @@ -2876,7 +2887,7 @@ case OP_SetCookie: { /* in3 */ Db *pDb; assert( pOp->p2<SQLITE_N_BTREE_META ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); pIn3 = &aMem[pOp->p3]; @@ -2924,7 +2935,7 @@ case OP_VerifyCookie: { Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); @@ -2950,6 +2961,7 @@ case OP_VerifyCookie: { */ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ sqlite3ResetInternalSchema(db, pOp->p1); + sqlite3VdbeMutexResync(p); } p->expired = 1; @@ -3028,7 +3040,7 @@ case OP_OpenWrite: { p2 = pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDb<db->nDb ); - assert( (p->btreeMask & (1<<iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); pDb = &db->aDb[iDb]; pX = pDb->pBt; assert( pX!=0 ); @@ -4565,7 +4577,7 @@ case OP_Destroy: { /* out2-prerelease */ }else{ iDb = pOp->p3; assert( iCnt==1 ); - assert( (p->btreeMask & (1<<iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); pOut->flags = MEM_Int; pOut->u.i = iMoved; @@ -4601,7 +4613,7 @@ case OP_Clear: { int nChange; nChange = 0; - assert( (p->btreeMask & (1<<pOp->p2))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); @@ -4646,7 +4658,7 @@ case OP_CreateTable: { /* out2-prerelease */ pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ @@ -4674,26 +4686,22 @@ case OP_ParseSchema: { char *zSql; InitData initData; + /* Any prepared statement that invokes this opcode will hold mutexes + ** on every btree. This is a prerequisite for invoking + ** sqlite3InitCallback(). + */ +#ifdef SQLITE_DEBUG + for(iDb=0; iDb<db->nDb; iDb++){ + assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); + } +#endif + assert( p->btreeMask == ~(yDbMask)0 ); + + iDb = pOp->p1; assert( iDb>=0 && iDb<db->nDb ); - - /* Although the mutex on the BtShared object that corresponds to - ** database iDb (the database containing the sqlite_master table - ** read by this instruction) is currently held, it is necessary to - ** obtain the mutexes on all attached databases before checking if - ** the schema of iDb is loaded. This is because, at the start of - ** the sqlite3_exec() call below, SQLite will invoke - ** sqlite3BtreeEnterAll(). If all mutexes are not already held, the - ** iDb mutex may be temporarily released to avoid deadlock. If - ** this happens, then some other thread may delete the in-memory - ** schema of database iDb before the SQL statement runs. The schema - ** will not be reloaded becuase the db->init.busy flag is set. This - ** can result in a "no such table: sqlite_master" or "malformed - ** database schema" error being returned to the user. - */ - assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); - sqlite3BtreeEnterAll(db); - if( ALWAYS(DbHasProperty(db, iDb, DB_SchemaLoaded)) ){ + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); + /* Used to be a conditional */ { zMaster = SCHEMA_TABLE(iDb); initData.db = db; initData.iDb = pOp->p1; @@ -4714,7 +4722,6 @@ case OP_ParseSchema: { db->init.busy = 0; } } - sqlite3BtreeLeaveAll(db); if( rc==SQLITE_NOMEM ){ goto no_mem; } @@ -4815,7 +4822,7 @@ case OP_IntegrityCk: { } aRoot[j] = 0; assert( pOp->p5<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p5))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 ); z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot, (int)pnErr->u.i, &nErr); sqlite3DbFree(db, aRoot); @@ -5239,11 +5246,25 @@ case OP_AggStep: { ctx.pColl = pOp[-1].p4.pColl; } (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ + sqlite3VdbeMutexResync(p); if( ctx.isError ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); rc = ctx.isError; } + + /* The app-defined function has done something that as caused this + ** statement to expire. (Perhaps the function called sqlite3_exec() + ** with a CREATE TABLE statement.) + */ +#if 0 + if( p->expired ){ + rc = SQLITE_ABORT; + break; + } +#endif + sqlite3VdbeMemRelease(&ctx.s); + break; } @@ -5265,8 +5286,11 @@ case OP_AggFinal: { pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); + sqlite3VdbeMutexResync(p); if( rc ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); + }else if( p->expired ){ + rc = SQLITE_ABORT; } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); @@ -5343,7 +5367,7 @@ case OP_JournalMode: { /* out2-prerelease */ /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called - ** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes + ** when the statement is prepared and so p->btreeMask!=0. All mutexes ** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree() ** is not called when the statement is prepared because it requires the ** iDb index of the database as a parameter, and the database has not @@ -5352,12 +5376,11 @@ case OP_JournalMode: { /* out2-prerelease */ ** No other mutexes are required by the ATTACH command so this is safe ** to do. */ - assert( (p->btreeMask & (1<<pOp->p1))!=0 || p->aMutex.nMutex==0 ); - if( p->aMutex.nMutex==0 ){ + if( p->btreeMask==0 ){ /* This occurs right after ATTACH. Get a mutex on the newly ATTACHed ** database. */ sqlite3VdbeUsesBtree(p, pOp->p1); - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); } pBt = db->aDb[pOp->p1].pBt; @@ -5457,7 +5480,7 @@ case OP_IncrVacuum: { /* jump */ Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ @@ -5506,7 +5529,7 @@ case OP_TableLock: { if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); - assert( (p->btreeMask & (1<<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 ); assert( isWriteLock==0 || isWriteLock==1 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); if( (rc&0xFF)==SQLITE_LOCKED ){ @@ -5995,13 +6018,16 @@ vdbe_error_halt: sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; - if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0); + if( resetSchemaOnFault ){ + sqlite3ResetInternalSchema(db, 0); + sqlite3VdbeMutexResync(p); + } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 911c29faf..05136cbeb 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -302,10 +302,10 @@ struct Vdbe { u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ - tAttachMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + u32 iMutexCounter; /* Mutex counter upon sqlite3VdbeEnter() */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ - BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif @@ -408,6 +408,9 @@ int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); void sqlite3VdbePreUpdateHook( Vdbe *, VdbeCursor *, int, const char*, Table *, i64, int); +void sqlite3VdbeEnter(Vdbe*); +void sqlite3VdbeLeave(Vdbe*); +void sqlite3VdbeMutexResync(Vdbe*); #ifdef SQLITE_DEBUG void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); @@ -419,12 +422,6 @@ int sqlite3VdbeCheckFk(Vdbe *, int); # define sqlite3VdbeCheckFk(p,i) 0 #endif -#ifndef SQLITE_OMIT_SHARED_CACHE -void sqlite3VdbeMutexArrayEnter(Vdbe *p); -#else -# define sqlite3VdbeMutexArrayEnter(p) -#endif - int sqlite3VdbeMemTranslate(Mem*, u8); #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index a4b0df8f4..16eeb5ce1 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -158,6 +158,11 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; p->expired = 0; + if( op==OP_ParseSchema ){ + /* Any program that uses the OP_ParseSchema opcode needs to lock + ** all btrees. */ + p->btreeMask = ~(yDbMask)0; + } #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); @@ -458,7 +463,7 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ assert( aOp && !p->db->mallocFailed ); /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ - assert( p->aMutex.nMutex==0 ); + assert( p->btreeMask==0 ); resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; @@ -946,22 +951,131 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** -** The prepared statement has to know in advance which Btree objects -** will be used so that it can acquire mutexes on them all in sorted -** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired -** in order (and released in reverse order) to avoid deadlocks. +** The prepared statements need to know in advance the complete set of +** attached databases that they will be using. A mask of these databases +** is maintained in p->btreeMask and is used for locking and other purposes. */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ - tAttachMask mask; - assert( i>=0 && i<p->db->nDb && i<sizeof(tAttachMask)*8 ); + assert( i>=0 && i<p->db->nDb && i<sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); - mask = ((u32)1)<<i; - if( (p->btreeMask & mask)==0 ){ - p->btreeMask |= mask; - sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt); + p->btreeMask |= ((yDbMask)1)<<i; +} + +/* +** Compute the sum of all mutex counters for all btrees in the +** given prepared statement. +*/ +#ifndef SQLITE_OMIT_SHARED_CACHE +static u32 mutexCounterSum(Vdbe *p){ + u32 cntSum = 0; +#ifdef SQLITE_DEBUG + int i; + yDbMask mask; + sqlite3 *db = p->db; + Db *aDb = db->aDb; + int nDb = db->nDb; + for(i=0, mask=1; i<nDb; i++, mask += mask){ + if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + cntSum += sqlite3BtreeMutexCounter(aDb[i].pBt); + } + } +#else + UNUSED_PARAMETER(p); +#endif + return cntSum; +} +#endif + +/* +** If SQLite is compiled to support shared-cache mode and to be threadsafe, +** this routine obtains the mutex associated with each BtShared structure +** that may be accessed by the VM passed as an argument. In doing so it also +** sets the BtShared.db member of each of the BtShared structures, ensuring +** that the correct busy-handler callback is invoked if required. +** +** If SQLite is not threadsafe but does support shared-cache mode, then +** sqlite3BtreeEnter() is invoked to set the BtShared.db variables +** of all of BtShared structures accessible via the database handle +** associated with the VM. +** +** If SQLite is not threadsafe and does not support shared-cache mode, this +** function is a no-op. +** +** The p->btreeMask field is a bitmask of all btrees that the prepared +** statement p will ever use. Let N be the number of bits in p->btreeMask +** corresponding to btrees that use shared cache. Then the runtime of +** this routine is N*N. But as N is rarely more than 1, this should not +** be a problem. +*/ +void sqlite3VdbeEnter(Vdbe *p){ +#ifndef SQLITE_OMIT_SHARED_CACHE + int i; + yDbMask mask; + sqlite3 *db = p->db; + Db *aDb = db->aDb; + int nDb = db->nDb; + for(i=0, mask=1; i<nDb; i++, mask += mask){ + if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeEnter(aDb[i].pBt); + } + } + p->iMutexCounter = mutexCounterSum(p); +#else + UNUSED_PARAMETER(p); +#endif +} + +/* +** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). +*/ +void sqlite3VdbeLeave(Vdbe *p){ +#ifndef SQLITE_OMIT_SHARED_CACHE + int i; + yDbMask mask; + sqlite3 *db = p->db; + Db *aDb = db->aDb; + int nDb = db->nDb; + + /* Assert that the all mutexes have been held continously since + ** the most recent sqlite3VdbeEnter() or sqlite3VdbeMutexResync(). + */ + assert( mutexCounterSum(p) == p->iMutexCounter ); + + for(i=0, mask=1; i<nDb; i++, mask += mask){ + if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeLeave(aDb[i].pBt); + } } +#else + UNUSED_PARAMETER(p); +#endif } +/* +** Recompute the sum of the mutex counters on all btrees used by the +** prepared statement p. +** +** Call this routine while holding a sqlite3VdbeEnter() after doing something +** that might cause one or more of the individual mutexes held by the +** prepared statement to be released. Calling sqlite3BtreeEnter() on +** any BtShared mutex which is not used by the prepared statement is one +** way to cause one or more of the mutexes in the prepared statement +** to be temporarily released. The anti-deadlocking logic in +** sqlite3BtreeEnter() can cause mutexes to be released temporarily then +** reacquired. +** +** Calling this routine is an acknowledgement that some of the individual +** mutexes in the prepared statement might have been released and reacquired. +** So checks to verify that mutex-protected content did not change +** unexpectedly should accompany any call to this routine. +*/ +void sqlite3VdbeMutexResync(Vdbe *p){ +#if !defined(SQLITE_OMIT_SHARED_CACHE) && defined(SQLITE_DEBUG) + p->iMutexCounter = mutexCounterSum(p); +#else + UNUSED_PARAMETER(p); +#endif +} #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* @@ -1961,33 +2075,6 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ } /* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. -** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. Of course only a subset of these structures -** will be accessed by the VM, and we could use Vdbe.btreeMask to figure -** that subset out, but there is no advantage to doing so. -** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. -*/ -#ifndef SQLITE_OMIT_SHARED_CACHE -void sqlite3VdbeMutexArrayEnter(Vdbe *p){ -#if SQLITE_THREADSAFE - sqlite3BtreeMutexArrayEnter(&p->aMutex); -#else - sqlite3BtreeEnterAll(p->db); -#endif -} -#endif - -/* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be ** committed. If there are outstanding deferred foreign key constraint @@ -2059,7 +2146,7 @@ int sqlite3VdbeHalt(Vdbe *p){ int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; @@ -2113,7 +2200,7 @@ int sqlite3VdbeHalt(Vdbe *p){ rc = sqlite3VdbeCheckFk(p, 1); if( rc!=SQLITE_OK ){ if( NEVER(p->readOnly) ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); return SQLITE_ERROR; } rc = SQLITE_CONSTRAINT; @@ -2125,7 +2212,7 @@ int sqlite3VdbeHalt(Vdbe *p){ rc = vdbeCommit(db, p); } if( rc==SQLITE_BUSY && p->readOnly ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; @@ -2197,7 +2284,8 @@ int sqlite3VdbeHalt(Vdbe *p){ } /* Release the locks */ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeMutexResync(p); + sqlite3VdbeLeave(p); } /* We have successfully halted and closed the VM. Record this fact. */ diff --git a/test/analyze7.test b/test/analyze7.test index 4c2fd53e8..a796ab78a 100644 --- a/test/analyze7.test +++ b/test/analyze7.test @@ -18,7 +18,7 @@ source $testdir/tester.tcl # There is nothing to test if ANALYZE is disable for this build. # -ifcapable {!analyze} { +ifcapable {!analyze||!vtab} { finish_test return } @@ -26,23 +26,15 @@ ifcapable {!analyze} { # Generate some test data # do_test analyze7-1.0 { + register_wholenumber_module db 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; + CREATE VIRTUAL TABLE nums USING wholenumber; + INSERT INTO t1 SELECT value, value, value/100, value FROM nums + WHERE value BETWEEN 1 AND 256; EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123; } } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}} @@ -87,9 +79,22 @@ do_test analyze7-3.0 { 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 { - execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} -} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~102 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)}} diff --git a/test/fuzzer1.test b/test/fuzzer1.test new file mode 100644 index 000000000..d0575d2d0 --- /dev/null +++ b/test/fuzzer1.test @@ -0,0 +1,1382 @@ +# 2011 March 25 +# +# 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 TCL interface to the +# SQLite library. +# +# The focus of the tests is the word-fuzzer virtual table. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !vtab { + finish_test + return +} + +register_fuzzer_module db +do_test fuzzer1-1.0 { + catchsql {CREATE VIRTUAL TABLE fault1 USING fuzzer;} +} {1 {fuzzer virtual tables must be TEMP}} +do_test fuzzer1-1.1 { + db eval {CREATE VIRTUAL TABLE temp.f1 USING fuzzer;} +} {} +do_test fuzzer1-1.2 { + db eval { + INSERT INTO f1(cfrom, cto, cost) VALUES('e','a',1); + INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',10); + INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',100); + } +} {} + +do_test fuzzer1-1.3 { + db eval { + SELECT word, distance FROM f1 WHERE word MATCH 'abcde' + } +} {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210} + +do_test fuzzer1-2.0 { + execsql { + CREATE VIRTUAL TABLE temp.f2 USING fuzzer; + -- costs based on English letter frequencies + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','e',24); + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','o',47); + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','u',50); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','a',23); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','i',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','o',37); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','e',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','y',33); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','a',41); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','e',46); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','u',57); + INSERT INTO f2(cFrom,cTo,cost) VALUES('u','o',58); + INSERT INTO f2(cFrom,cTo,cost) VALUES('y','i',33); + + INSERT INTO f2(cFrom,cTo,cost) VALUES('t','th',70); + INSERT INTO f2(cFrom,cTo,cost) VALUES('th','t',66); + + + INSERT INTO f2(cFrom,cTo,cost) VALUES('a','',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','b',106); + INSERT INTO f2(cFrom,cTo,cost) VALUES('b','',106); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','c',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('c','',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','d',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('d','',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','e',83); + INSERT INTO f2(cFrom,cTo,cost) VALUES('e','',83); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','f',97); + INSERT INTO f2(cFrom,cTo,cost) VALUES('f','',97); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','g',99); + INSERT INTO f2(cFrom,cTo,cost) VALUES('g','',99); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','h',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('h','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','i',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('i','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','j',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('j','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','k',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('k','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','l',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('l','',89); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','m',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('m','',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','n',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('n','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','o',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('o','',85); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','p',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('p','',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','q',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('q','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','r',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('r','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','s',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('s','',86); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','t',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('t','',84); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','u',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('u','',94); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','v',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('v','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','w',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('w','',96); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','x',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('x','',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','y',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('y','',100); + INSERT INTO f2(cFrom,cTo,cost) VALUES('','z',120); + INSERT INTO f2(cFrom,cTo,cost) VALUES('z','',120); + + -- Street names for the 28269 ZIPCODE. + -- + CREATE TEMP TABLE streetname(n TEXT UNIQUE); + INSERT INTO streetname VALUES('abbotsinch'); + INSERT INTO streetname VALUES('abbottsgate'); + INSERT INTO streetname VALUES('abbywood'); + INSERT INTO streetname VALUES('abner'); + INSERT INTO streetname VALUES('acacia ridge'); + INSERT INTO streetname VALUES('acorn creek'); + INSERT INTO streetname VALUES('acorn forest'); + INSERT INTO streetname VALUES('adel'); + INSERT INTO streetname VALUES('ainslie'); + INSERT INTO streetname VALUES('airways'); + INSERT INTO streetname VALUES('alabaster'); + INSERT INTO streetname VALUES('alba'); + INSERT INTO streetname VALUES('albertine'); + INSERT INTO streetname VALUES('alden glen'); + INSERT INTO streetname VALUES('alderson'); + INSERT INTO streetname VALUES('allen'); + INSERT INTO streetname VALUES('allen a brown'); + INSERT INTO streetname VALUES('allness glen'); + INSERT INTO streetname VALUES('aloysia'); + INSERT INTO streetname VALUES('alpine'); + INSERT INTO streetname VALUES('alwyn'); + INSERT INTO streetname VALUES('amaranthus'); + INSERT INTO streetname VALUES('amber glen'); + INSERT INTO streetname VALUES('amber leigh way'); + INSERT INTO streetname VALUES('amber meadows'); + INSERT INTO streetname VALUES('amberway'); + INSERT INTO streetname VALUES('ame'); + INSERT INTO streetname VALUES('amesbury hill'); + INSERT INTO streetname VALUES('anderson'); + INSERT INTO streetname VALUES('andrew thomas'); + INSERT INTO streetname VALUES('anduin falls'); + INSERT INTO streetname VALUES('ankeny'); + INSERT INTO streetname VALUES('annandale'); + INSERT INTO streetname VALUES('annbick'); + INSERT INTO streetname VALUES('antelope'); + INSERT INTO streetname VALUES('anzack'); + INSERT INTO streetname VALUES('apple glen'); + INSERT INTO streetname VALUES('applevalley'); + INSERT INTO streetname VALUES('appley mead'); + INSERT INTO streetname VALUES('aragorn'); + INSERT INTO streetname VALUES('arbor creek'); + INSERT INTO streetname VALUES('arbor day'); + INSERT INTO streetname VALUES('arbor meadows'); + INSERT INTO streetname VALUES('arbor spring'); + INSERT INTO streetname VALUES('arborview'); + INSERT INTO streetname VALUES('arklow'); + INSERT INTO streetname VALUES('armitage'); + INSERT INTO streetname VALUES('arvin'); + INSERT INTO streetname VALUES('ash cove'); + INSERT INTO streetname VALUES('ashford leigh'); + INSERT INTO streetname VALUES('ashmont'); + INSERT INTO streetname VALUES('atlas'); + INSERT INTO streetname VALUES('atwater'); + INSERT INTO streetname VALUES('auburn hill'); + INSERT INTO streetname VALUES('aulton link'); + INSERT INTO streetname VALUES('austin dekota'); + INSERT INTO streetname VALUES('austin knoll'); + INSERT INTO streetname VALUES('auten'); + INSERT INTO streetname VALUES('autumn harvest'); + INSERT INTO streetname VALUES('autumn oak'); + INSERT INTO streetname VALUES('autumn ridge'); + INSERT INTO streetname VALUES('avalon forest'); + INSERT INTO streetname VALUES('avalon loop'); + INSERT INTO streetname VALUES('avon farm'); + INSERT INTO streetname VALUES('avonhurst'); + INSERT INTO streetname VALUES('avonlea'); + INSERT INTO streetname VALUES('aynrand'); + INSERT INTO streetname VALUES('azure valley'); + INSERT INTO streetname VALUES('baberton'); + INSERT INTO streetname VALUES('baffin'); + INSERT INTO streetname VALUES('baggins'); + INSERT INTO streetname VALUES('balata'); + INSERT INTO streetname VALUES('ballantray'); + INSERT INTO streetname VALUES('ballston'); + INSERT INTO streetname VALUES('balsam tree'); + INSERT INTO streetname VALUES('bambi'); + INSERT INTO streetname VALUES('banwell'); + INSERT INTO streetname VALUES('barbee'); + INSERT INTO streetname VALUES('barefoot forest'); + INSERT INTO streetname VALUES('barnview'); + INSERT INTO streetname VALUES('baroda'); + INSERT INTO streetname VALUES('barson'); + INSERT INTO streetname VALUES('baskerville'); + INSERT INTO streetname VALUES('battle creek'); + INSERT INTO streetname VALUES('baucom'); + INSERT INTO streetname VALUES('bay pines'); + INSERT INTO streetname VALUES('beaker'); + INSERT INTO streetname VALUES('beard'); + INSERT INTO streetname VALUES('beardsley'); + INSERT INTO streetname VALUES('bearoak'); + INSERT INTO streetname VALUES('beauvista'); + INSERT INTO streetname VALUES('beaver creek'); + INSERT INTO streetname VALUES('beaver hollow'); + INSERT INTO streetname VALUES('bedlington'); + INSERT INTO streetname VALUES('beech cove'); + INSERT INTO streetname VALUES('beech crest'); + INSERT INTO streetname VALUES('beith'); + INSERT INTO streetname VALUES('bell glen'); + INSERT INTO streetname VALUES('bellmore'); + INSERT INTO streetname VALUES('bells mill'); + INSERT INTO streetname VALUES('bellville'); + INSERT INTO streetname VALUES('belmar place'); + INSERT INTO streetname VALUES('bembridge'); + INSERT INTO streetname VALUES('bennett neely'); + INSERT INTO streetname VALUES('bentgrass run'); + INSERT INTO streetname VALUES('benthaven'); + INSERT INTO streetname VALUES('bernardy'); + INSERT INTO streetname VALUES('bernbrook shadow'); + INSERT INTO streetname VALUES('berrybrook'); + INSERT INTO streetname VALUES('berrybush'); + INSERT INTO streetname VALUES('berwick'); + INSERT INTO streetname VALUES('betterton'); + INSERT INTO streetname VALUES('bickham'); + INSERT INTO streetname VALUES('billingham'); + INSERT INTO streetname VALUES('birchcroft'); + INSERT INTO streetname VALUES('birchstone'); + INSERT INTO streetname VALUES('birdwell'); + INSERT INTO streetname VALUES('bisaner'); + INSERT INTO streetname VALUES('bitterbush'); + INSERT INTO streetname VALUES('bitterroot'); + INSERT INTO streetname VALUES('black fox'); + INSERT INTO streetname VALUES('black maple'); + INSERT INTO streetname VALUES('black trail'); + INSERT INTO streetname VALUES('blackbird'); + INSERT INTO streetname VALUES('blake a dare'); + INSERT INTO streetname VALUES('blasdell'); + INSERT INTO streetname VALUES('blue aster'); + INSERT INTO streetname VALUES('blue finch'); + INSERT INTO streetname VALUES('blue lilac'); + INSERT INTO streetname VALUES('blue sky'); + INSERT INTO streetname VALUES('blue tick'); + INSERT INTO streetname VALUES('bob beatty'); + INSERT INTO streetname VALUES('bobcat'); + INSERT INTO streetname VALUES('bolton'); + INSERT INTO streetname VALUES('boomerang'); + INSERT INTO streetname VALUES('boulder'); + INSERT INTO streetname VALUES('boxer'); + INSERT INTO streetname VALUES('boxmeer'); + INSERT INTO streetname VALUES('brachnell view'); + INSERT INTO streetname VALUES('bradford lake'); + INSERT INTO streetname VALUES('bradwell'); + INSERT INTO streetname VALUES('brady'); + INSERT INTO streetname VALUES('braids bend'); + INSERT INTO streetname VALUES('bralers'); + INSERT INTO streetname VALUES('brandie glen'); + INSERT INTO streetname VALUES('brandy ridge'); + INSERT INTO streetname VALUES('brandybuck'); + INSERT INTO streetname VALUES('branthurst'); + INSERT INTO streetname VALUES('brassy creek'); + INSERT INTO streetname VALUES('brathay'); + INSERT INTO streetname VALUES('brawer farm'); + INSERT INTO streetname VALUES('breezy morn'); + INSERT INTO streetname VALUES('brenda'); + INSERT INTO streetname VALUES('brenly'); + INSERT INTO streetname VALUES('brenock'); + INSERT INTO streetname VALUES('brianwood'); + INSERT INTO streetname VALUES('briar rose'); + INSERT INTO streetname VALUES('briarcrest'); + INSERT INTO streetname VALUES('briarthorne'); + INSERT INTO streetname VALUES('brick dust'); + INSERT INTO streetname VALUES('bridgepath'); + INSERT INTO streetname VALUES('bridle ridge'); + INSERT INTO streetname VALUES('briggs'); + INSERT INTO streetname VALUES('brightleaf'); + INSERT INTO streetname VALUES('brigstock'); + INSERT INTO streetname VALUES('broad ridge'); + INSERT INTO streetname VALUES('brock'); + INSERT INTO streetname VALUES('brockhampton'); + INSERT INTO streetname VALUES('broken pine'); + INSERT INTO streetname VALUES('brompton'); + INSERT INTO streetname VALUES('brook falls'); + INSERT INTO streetname VALUES('brookings'); + INSERT INTO streetname VALUES('browne'); + INSERT INTO streetname VALUES('brownes creek'); + INSERT INTO streetname VALUES('brownes ferry'); + INSERT INTO streetname VALUES('brownestone view'); + INSERT INTO streetname VALUES('brumit'); + INSERT INTO streetname VALUES('bryn athyn'); + INSERT INTO streetname VALUES('buck'); + INSERT INTO streetname VALUES('bucklebury'); + INSERT INTO streetname VALUES('buckminister'); + INSERT INTO streetname VALUES('buckspring'); + INSERT INTO streetname VALUES('burch'); + INSERT INTO streetname VALUES('burch shire'); + INSERT INTO streetname VALUES('burkston'); + INSERT INTO streetname VALUES('burmith'); + INSERT INTO streetname VALUES('burnaby'); + INSERT INTO streetname VALUES('butterfly'); + INSERT INTO streetname VALUES('cabin creek'); + INSERT INTO streetname VALUES('cairns mill'); + INSERT INTO streetname VALUES('callender'); + INSERT INTO streetname VALUES('cambellton'); + INSERT INTO streetname VALUES('cambridge bay'); + INSERT INTO streetname VALUES('canary'); + INSERT INTO streetname VALUES('canbury'); + INSERT INTO streetname VALUES('candle leaf'); + INSERT INTO streetname VALUES('canipe'); + INSERT INTO streetname VALUES('canipe farm'); + INSERT INTO streetname VALUES('cannon'); + INSERT INTO streetname VALUES('canopy'); + INSERT INTO streetname VALUES('canso'); + INSERT INTO streetname VALUES('canterbrook'); + INSERT INTO streetname VALUES('cardinal glen'); + INSERT INTO streetname VALUES('cardinal point'); + INSERT INTO streetname VALUES('cardinals nest'); + INSERT INTO streetname VALUES('carlota'); + INSERT INTO streetname VALUES('carmathen'); + INSERT INTO streetname VALUES('carver'); + INSERT INTO streetname VALUES('carver pond'); + INSERT INTO streetname VALUES('casa loma'); + INSERT INTO streetname VALUES('caselton'); + INSERT INTO streetname VALUES('castello'); + INSERT INTO streetname VALUES('castle ridge'); + INSERT INTO streetname VALUES('castleglen'); + INSERT INTO streetname VALUES('castlemaine'); + INSERT INTO streetname VALUES('cavett'); + INSERT INTO streetname VALUES('caymus'); + INSERT INTO streetname VALUES('cedardale ridge'); + INSERT INTO streetname VALUES('cedarhurst'); + INSERT INTO streetname VALUES('cemkey way'); + INSERT INTO streetname VALUES('cerise'); + INSERT INTO streetname VALUES('chaceview'); + INSERT INTO streetname VALUES('chadsworth'); + INSERT INTO streetname VALUES('chadwell'); + INSERT INTO streetname VALUES('champions crest'); + INSERT INTO streetname VALUES('chandler haven'); + INSERT INTO streetname VALUES('chapel crossing'); + INSERT INTO streetname VALUES('chapel ridge'); + INSERT INTO streetname VALUES('charles crawford'); + INSERT INTO streetname VALUES('charminster'); + INSERT INTO streetname VALUES('chasewind'); + INSERT INTO streetname VALUES('chavel'); + INSERT INTO streetname VALUES('chelsea jade'); + INSERT INTO streetname VALUES('chestnut knoll'); + INSERT INTO streetname VALUES('cheviot'); + INSERT INTO streetname VALUES('chickadee'); + INSERT INTO streetname VALUES('chidley'); + INSERT INTO streetname VALUES('chimney ridge'); + INSERT INTO streetname VALUES('chimney springs'); + INSERT INTO streetname VALUES('chinaberry'); + INSERT INTO streetname VALUES('chinemist'); + INSERT INTO streetname VALUES('chinquapin'); + INSERT INTO streetname VALUES('chiswell'); + INSERT INTO streetname VALUES('christenbury'); + INSERT INTO streetname VALUES('christenbury hills'); + INSERT INTO streetname VALUES('churchill'); + INSERT INTO streetname VALUES('cindy'); + INSERT INTO streetname VALUES('cinnamon teal'); + INSERT INTO streetname VALUES('citadel'); + INSERT INTO streetname VALUES('clare olivia'); + INSERT INTO streetname VALUES('clarke creek'); + INSERT INTO streetname VALUES('clarke ridge'); + INSERT INTO streetname VALUES('clear day'); + INSERT INTO streetname VALUES('clear stream'); + INSERT INTO streetname VALUES('cleve brown'); + INSERT INTO streetname VALUES('cliff cameron'); + INSERT INTO streetname VALUES('cliffvale'); + INSERT INTO streetname VALUES('cloverside'); + INSERT INTO streetname VALUES('clymer'); + INSERT INTO streetname VALUES('coatbridge'); + INSERT INTO streetname VALUES('cobble glen'); + INSERT INTO streetname VALUES('cochran farm'); + INSERT INTO streetname VALUES('cochrane'); + INSERT INTO streetname VALUES('coleridge'); + INSERT INTO streetname VALUES('coleshire'); + INSERT INTO streetname VALUES('collins'); + INSERT INTO streetname VALUES('colvard'); + INSERT INTO streetname VALUES('colvard park'); + INSERT INTO streetname VALUES('condor'); + INSERT INTO streetname VALUES('conner ridge'); + INSERT INTO streetname VALUES('connery'); + INSERT INTO streetname VALUES('cooper run'); + INSERT INTO streetname VALUES('coopers ridge'); + INSERT INTO streetname VALUES('copper hill'); + INSERT INTO streetname VALUES('coppermine'); + INSERT INTO streetname VALUES('cornelia'); + INSERT INTO streetname VALUES('corner'); + INSERT INTO streetname VALUES('cornerstone'); + INSERT INTO streetname VALUES('cottage oaks'); + INSERT INTO streetname VALUES('cougar'); + INSERT INTO streetname VALUES('coves end'); + INSERT INTO streetname VALUES('cragland'); + INSERT INTO streetname VALUES('crail'); + INSERT INTO streetname VALUES('cranberry nook'); + INSERT INTO streetname VALUES('crawford brook'); + INSERT INTO streetname VALUES('crayton'); + INSERT INTO streetname VALUES('creek breeze'); + INSERT INTO streetname VALUES('crescent ridge'); + INSERT INTO streetname VALUES('crescent view'); + INSERT INTO streetname VALUES('cresta'); + INSERT INTO streetname VALUES('crestfield'); + INSERT INTO streetname VALUES('crestland'); + INSERT INTO streetname VALUES('crestwick'); + INSERT INTO streetname VALUES('crisfield'); + INSERT INTO streetname VALUES('crisp wood'); + INSERT INTO streetname VALUES('croft haven'); + INSERT INTO streetname VALUES('crofton springs'); + INSERT INTO streetname VALUES('cross'); + INSERT INTO streetname VALUES('crosspoint center'); + INSERT INTO streetname VALUES('crownvista'); + INSERT INTO streetname VALUES('crystal arms'); + INSERT INTO streetname VALUES('crystal crest'); + INSERT INTO streetname VALUES('crystal leaf'); + INSERT INTO streetname VALUES('cunningham park'); + INSERT INTO streetname VALUES('cypress pond'); + INSERT INTO streetname VALUES('daffodil'); + INSERT INTO streetname VALUES('daisyfield'); + INSERT INTO streetname VALUES('dalecrest'); + INSERT INTO streetname VALUES('dannelly park'); + INSERT INTO streetname VALUES('daphne'); + INSERT INTO streetname VALUES('daria'); + INSERT INTO streetname VALUES('dartmouth'); + INSERT INTO streetname VALUES('datha'); + INSERT INTO streetname VALUES('david cox'); + INSERT INTO streetname VALUES('davis'); + INSERT INTO streetname VALUES('davis crossing'); + INSERT INTO streetname VALUES('davis lake'); + INSERT INTO streetname VALUES('davis ridge'); + INSERT INTO streetname VALUES('dawnmist'); + INSERT INTO streetname VALUES('daybreak'); + INSERT INTO streetname VALUES('dearmon'); + INSERT INTO streetname VALUES('dearview'); + INSERT INTO streetname VALUES('deaton hill'); + INSERT INTO streetname VALUES('deer cross'); + INSERT INTO streetname VALUES('deerton'); + INSERT INTO streetname VALUES('degrasse'); + INSERT INTO streetname VALUES('delamere'); + INSERT INTO streetname VALUES('dellfield'); + INSERT INTO streetname VALUES('dellinger'); + INSERT INTO streetname VALUES('demington'); + INSERT INTO streetname VALUES('denmeade'); + INSERT INTO streetname VALUES('derita'); + INSERT INTO streetname VALUES('derita woods'); + INSERT INTO streetname VALUES('deruyter'); + INSERT INTO streetname VALUES('dervish'); + INSERT INTO streetname VALUES('devas'); + INSERT INTO streetname VALUES('devon croft'); + INSERT INTO streetname VALUES('devonbridge'); + INSERT INTO streetname VALUES('devongate'); + INSERT INTO streetname VALUES('devonhill'); + INSERT INTO streetname VALUES('dewmorn'); + INSERT INTO streetname VALUES('distribution center'); + INSERT INTO streetname VALUES('dominion crest'); + INSERT INTO streetname VALUES('dominion green'); + INSERT INTO streetname VALUES('dominion village'); + INSERT INTO streetname VALUES('dorshire'); + INSERT INTO streetname VALUES('double creek crossing'); + INSERT INTO streetname VALUES('dow'); + INSERT INTO streetname VALUES('downfield wood'); + INSERT INTO streetname VALUES('downing creek'); + INSERT INTO streetname VALUES('driscol'); + INSERT INTO streetname VALUES('driwood'); + INSERT INTO streetname VALUES('dry brook'); + INSERT INTO streetname VALUES('dumont'); + INSERT INTO streetname VALUES('dunblane'); + INSERT INTO streetname VALUES('dunfield'); + INSERT INTO streetname VALUES('dunoon'); + INSERT INTO streetname VALUES('dunslow'); + INSERT INTO streetname VALUES('dunstaff'); + INSERT INTO streetname VALUES('durham'); + INSERT INTO streetname VALUES('durston'); + INSERT INTO streetname VALUES('dusty cedar'); + INSERT INTO streetname VALUES('dusty trail'); + INSERT INTO streetname VALUES('dutchess'); + INSERT INTO streetname VALUES('duxford'); + INSERT INTO streetname VALUES('eagle creek'); + INSERT INTO streetname VALUES('eagles field'); + INSERT INTO streetname VALUES('eargle'); + INSERT INTO streetname VALUES('earlswood'); + INSERT INTO streetname VALUES('early mist'); + INSERT INTO streetname VALUES('earthenware'); + INSERT INTO streetname VALUES('eastfield park'); + INSERT INTO streetname VALUES('eastfield village'); + INSERT INTO streetname VALUES('easy'); + INSERT INTO streetname VALUES('eben'); + INSERT INTO streetname VALUES('edgepine'); + INSERT INTO streetname VALUES('edgewier'); + INSERT INTO streetname VALUES('edinburgh'); + INSERT INTO streetname VALUES('edinmeadow'); + INSERT INTO streetname VALUES('edmonton'); + INSERT INTO streetname VALUES('edwin jones'); + INSERT INTO streetname VALUES('elberon'); + INSERT INTO streetname VALUES('elderslie'); + INSERT INTO streetname VALUES('elementary view'); + INSERT INTO streetname VALUES('elendil'); + INSERT INTO streetname VALUES('elizabeth'); + INSERT INTO streetname VALUES('elm cove'); + INSERT INTO streetname VALUES('elrond'); + INSERT INTO streetname VALUES('elsenham'); + INSERT INTO streetname VALUES('elven'); + INSERT INTO streetname VALUES('emma lynn'); + INSERT INTO streetname VALUES('english setter'); + INSERT INTO streetname VALUES('enoch'); + INSERT INTO streetname VALUES('equipment'); + INSERT INTO streetname VALUES('ernest russell'); + INSERT INTO streetname VALUES('ernie'); + INSERT INTO streetname VALUES('esmeralda'); + INSERT INTO streetname VALUES('evergreen hollow'); + INSERT INTO streetname VALUES('eversfield'); + INSERT INTO streetname VALUES('ewen'); + INSERT INTO streetname VALUES('ewert cut'); + INSERT INTO streetname VALUES('exbury'); + INSERT INTO streetname VALUES('fair grounds park'); + INSERT INTO streetname VALUES('fairbourne'); + INSERT INTO streetname VALUES('fairchase'); + INSERT INTO streetname VALUES('faircreek'); + INSERT INTO streetname VALUES('fairglen'); + INSERT INTO streetname VALUES('fairlea'); + INSERT INTO streetname VALUES('fairmead'); + INSERT INTO streetname VALUES('fairmeadows'); + INSERT INTO streetname VALUES('fairstone'); + INSERT INTO streetname VALUES('fairvista'); + INSERT INTO streetname VALUES('fairway point'); + INSERT INTO streetname VALUES('falconcrest'); + INSERT INTO streetname VALUES('falls ridge'); + INSERT INTO streetname VALUES('falmouth'); + INSERT INTO streetname VALUES('far west'); + INSERT INTO streetname VALUES('farlow'); + INSERT INTO streetname VALUES('farris wheel'); + INSERT INTO streetname VALUES('fawndale'); + INSERT INTO streetname VALUES('feather bend'); + INSERT INTO streetname VALUES('fernledge'); + INSERT INTO streetname VALUES('fernmoss'); + INSERT INTO streetname VALUES('ferrell commons'); + INSERT INTO streetname VALUES('fieldstone'); + INSERT INTO streetname VALUES('fillian'); + INSERT INTO streetname VALUES('fincher'); + INSERT INTO streetname VALUES('foggy meadow'); + INSERT INTO streetname VALUES('fordyce'); + INSERT INTO streetname VALUES('forest grove'); + INSERT INTO streetname VALUES('forest path'); + INSERT INTO streetname VALUES('forestridge commons'); + INSERT INTO streetname VALUES('forestrock'); + INSERT INTO streetname VALUES('fortunes ridge'); + INSERT INTO streetname VALUES('founders club'); + INSERT INTO streetname VALUES('fountaingrass'); + INSERT INTO streetname VALUES('fox chase'); + INSERT INTO streetname VALUES('fox glen'); + INSERT INTO streetname VALUES('fox hill'); + INSERT INTO streetname VALUES('fox point'); + INSERT INTO streetname VALUES('fox trot'); + INSERT INTO streetname VALUES('foxbriar'); + INSERT INTO streetname VALUES('frank little'); + INSERT INTO streetname VALUES('franzia'); + INSERT INTO streetname VALUES('french woods'); + INSERT INTO streetname VALUES('frostmoor'); + INSERT INTO streetname VALUES('frye'); + INSERT INTO streetname VALUES('furlong'); + INSERT INTO streetname VALUES('galena view'); + INSERT INTO streetname VALUES('gallery pointe'); + INSERT INTO streetname VALUES('gammon'); + INSERT INTO streetname VALUES('garden grove'); + INSERT INTO streetname VALUES('gardendale'); + INSERT INTO streetname VALUES('garganey'); + INSERT INTO streetname VALUES('garnet field'); + INSERT INTO streetname VALUES('garrison'); + INSERT INTO streetname VALUES('garvin'); + INSERT INTO streetname VALUES('garvis'); + INSERT INTO streetname VALUES('gaskill'); + INSERT INTO streetname VALUES('gemstone'); + INSERT INTO streetname VALUES('gibbon'); + INSERT INTO streetname VALUES('gibbon terrace'); + INSERT INTO streetname VALUES('gibbons link'); + INSERT INTO streetname VALUES('gillman'); + INSERT INTO streetname VALUES('gladwood'); + INSERT INTO streetname VALUES('gladwyne'); + INSERT INTO streetname VALUES('glamorgan'); + INSERT INTO streetname VALUES('glaze'); + INSERT INTO streetname VALUES('glen brook'); + INSERT INTO streetname VALUES('glen cove'); + INSERT INTO streetname VALUES('glen hope'); + INSERT INTO streetname VALUES('glen manor'); + INSERT INTO streetname VALUES('glen olden'); + INSERT INTO streetname VALUES('glencairn'); + INSERT INTO streetname VALUES('glendock'); + INSERT INTO streetname VALUES('glenolden'); + INSERT INTO streetname VALUES('glenover'); + INSERT INTO streetname VALUES('glenshire'); + INSERT INTO streetname VALUES('glenstone'); + INSERT INTO streetname VALUES('gold dust'); + INSERT INTO streetname VALUES('golden pond'); + INSERT INTO streetname VALUES('goldenblush'); + INSERT INTO streetname VALUES('goldenfield'); + INSERT INTO streetname VALUES('goose landing'); + INSERT INTO streetname VALUES('gorham gate'); + INSERT INTO streetname VALUES('grabill'); + INSERT INTO streetname VALUES('graburns ford'); + INSERT INTO streetname VALUES('graham'); + INSERT INTO streetname VALUES('grahamson'); + INSERT INTO streetname VALUES('granard'); + INSERT INTO streetname VALUES('grand teton'); + INSERT INTO streetname VALUES('grande heights'); + INSERT INTO streetname VALUES('grandeur'); + INSERT INTO streetname VALUES('granite creek'); + INSERT INTO streetname VALUES('grasset'); + INSERT INTO streetname VALUES('graypark'); + INSERT INTO streetname VALUES('grays ridge'); + INSERT INTO streetname VALUES('great bear'); + INSERT INTO streetname VALUES('green clover'); + INSERT INTO streetname VALUES('green hedge'); + INSERT INTO streetname VALUES('green meadow'); + INSERT INTO streetname VALUES('green pasture'); + INSERT INTO streetname VALUES('greene'); + INSERT INTO streetname VALUES('greenloch'); + INSERT INTO streetname VALUES('greenock ridge'); + INSERT INTO streetname VALUES('greenware'); + INSERT INTO streetname VALUES('greenway village'); + INSERT INTO streetname VALUES('grenelefe village'); + INSERT INTO streetname VALUES('grey dogwood'); + INSERT INTO streetname VALUES('greyhound'); + INSERT INTO streetname VALUES('greylock ridge'); + INSERT INTO streetname VALUES('grosbeak'); + INSERT INTO streetname VALUES('grove'); + INSERT INTO streetname VALUES('groveton'); + INSERT INTO streetname VALUES('groveview'); + INSERT INTO streetname VALUES('hackberry creek'); + INSERT INTO streetname VALUES('hackberry grove'); + INSERT INTO streetname VALUES('hackett'); + INSERT INTO streetname VALUES('haddington'); + INSERT INTO streetname VALUES('hagler'); + INSERT INTO streetname VALUES('halcott'); + INSERT INTO streetname VALUES('half dome'); + INSERT INTO streetname VALUES('hallam'); + INSERT INTO streetname VALUES('hamilton russell'); + INSERT INTO streetname VALUES('hampton place'); + INSERT INTO streetname VALUES('hankins'); + INSERT INTO streetname VALUES('harburn forest'); + INSERT INTO streetname VALUES('harringham'); + INSERT INTO streetname VALUES('harrington woods'); + INSERT INTO streetname VALUES('harris corners'); + INSERT INTO streetname VALUES('harris cove'); + INSERT INTO streetname VALUES('harris glen'); + INSERT INTO streetname VALUES('harris hill'); + INSERT INTO streetname VALUES('harris oak'); + INSERT INTO streetname VALUES('harris pointe'); + INSERT INTO streetname VALUES('harris pond'); + INSERT INTO streetname VALUES('harris ridge'); + INSERT INTO streetname VALUES('harris technology'); + INSERT INTO streetname VALUES('harris woods'); + INSERT INTO streetname VALUES('hartfield downs'); + INSERT INTO streetname VALUES('hattie little'); + INSERT INTO streetname VALUES('hatwynn'); + INSERT INTO streetname VALUES('hawkins'); + INSERT INTO streetname VALUES('hawksnest'); + INSERT INTO streetname VALUES('haybridge'); + INSERT INTO streetname VALUES('hayden'); + INSERT INTO streetname VALUES('hazelcroft'); + INSERT INTO streetname VALUES('hazlitt'); + INSERT INTO streetname VALUES('hazy valley'); + INSERT INTO streetname VALUES('hearst'); + INSERT INTO streetname VALUES('heathcrest'); + INSERT INTO streetname VALUES('heathcroft'); + INSERT INTO streetname VALUES('hedge maple'); + INSERT INTO streetname VALUES('hedgecrest'); + INSERT INTO streetname VALUES('hedingham'); + INSERT INTO streetname VALUES('heman'); + INSERT INTO streetname VALUES('henderson'); + INSERT INTO streetname VALUES('henderson oaks'); + INSERT INTO streetname VALUES('henderson valley'); + INSERT INTO streetname VALUES('hendry'); + INSERT INTO streetname VALUES('heritage hills'); + INSERT INTO streetname VALUES('heritage woods'); + INSERT INTO streetname VALUES('heron cove'); + INSERT INTO streetname VALUES('heron glen'); + INSERT INTO streetname VALUES('hewitt'); + INSERT INTO streetname VALUES('hey rock'); + INSERT INTO streetname VALUES('heysham'); + INSERT INTO streetname VALUES('hickory cove'); + INSERT INTO streetname VALUES('hidden meadow'); + INSERT INTO streetname VALUES('high glen'); + INSERT INTO streetname VALUES('high laurel'); + INSERT INTO streetname VALUES('high valley'); + INSERT INTO streetname VALUES('highcroft'); + INSERT INTO streetname VALUES('highland'); + INSERT INTO streetname VALUES('highland commons'); + INSERT INTO streetname VALUES('highland creek'); + INSERT INTO streetname VALUES('highland glen'); + INSERT INTO streetname VALUES('highland park'); + INSERT INTO streetname VALUES('highlander'); + INSERT INTO streetname VALUES('highstream'); + INSERT INTO streetname VALUES('hilltop'); + INSERT INTO streetname VALUES('hobbitshire'); + INSERT INTO streetname VALUES('hoffman'); + INSERT INTO streetname VALUES('hogans way'); + INSERT INTO streetname VALUES('holbert'); + INSERT INTO streetname VALUES('hollow ridge'); + INSERT INTO streetname VALUES('holly vista'); + INSERT INTO streetname VALUES('hollywood'); + INSERT INTO streetname VALUES('hoover'); + INSERT INTO streetname VALUES('hopkins'); + INSERT INTO streetname VALUES('horace mann'); + INSERT INTO streetname VALUES('hornbeam'); + INSERT INTO streetname VALUES('horse pasture'); + INSERT INTO streetname VALUES('hosta'); + INSERT INTO streetname VALUES('howard'); + INSERT INTO streetname VALUES('hubbard'); + INSERT INTO streetname VALUES('hubbard falls'); + INSERT INTO streetname VALUES('hubbard woods'); + INSERT INTO streetname VALUES('hucks'); + INSERT INTO streetname VALUES('hunters creek'); + INSERT INTO streetname VALUES('hunters pointe'); + INSERT INTO streetname VALUES('hunters spring'); + INSERT INTO streetname VALUES('hunters whip'); + INSERT INTO streetname VALUES('huntmeadow'); + INSERT INTO streetname VALUES('hutchison mcdonald'); + INSERT INTO streetname VALUES('ingleton'); + INSERT INTO streetname VALUES('insdale'); + INSERT INTO streetname VALUES('interstate 85 service'); + INSERT INTO streetname VALUES('iola'); + INSERT INTO streetname VALUES('iredell'); + INSERT INTO streetname VALUES('iron brigade'); + INSERT INTO streetname VALUES('irwin valley'); + INSERT INTO streetname VALUES('irwin wood'); + INSERT INTO streetname VALUES('ivy brook'); + INSERT INTO streetname VALUES('ivy ridge'); + INSERT INTO streetname VALUES('jack russell'); + INSERT INTO streetname VALUES('jackson'); + INSERT INTO streetname VALUES('jacob martin'); + INSERT INTO streetname VALUES('jamison'); + INSERT INTO streetname VALUES('jane'); + INSERT INTO streetname VALUES('jaspar crest'); + INSERT INTO streetname VALUES('jessica'); + INSERT INTO streetname VALUES('jimmy oehler'); + INSERT INTO streetname VALUES('jocelyn'); + INSERT INTO streetname VALUES('johnston mill'); + INSERT INTO streetname VALUES('johnston oehler'); + INSERT INTO streetname VALUES('judal'); + INSERT INTO streetname VALUES('junipeous'); + INSERT INTO streetname VALUES('juniper'); + INSERT INTO streetname VALUES('juniperus'); + INSERT INTO streetname VALUES('kalispell'); + INSERT INTO streetname VALUES('karylsturn'); + INSERT INTO streetname VALUES('katelyn'); + INSERT INTO streetname VALUES('kayron'); + INSERT INTO streetname VALUES('keaton'); + INSERT INTO streetname VALUES('keble'); + INSERT INTO streetname VALUES('keels'); + INSERT INTO streetname VALUES('keith'); + INSERT INTO streetname VALUES('keithwood'); + INSERT INTO streetname VALUES('kelden walker'); + INSERT INTO streetname VALUES('kelsey emma'); + INSERT INTO streetname VALUES('kendrick'); + INSERT INTO streetname VALUES('kenmont'); + INSERT INTO streetname VALUES('kennerly cove'); + INSERT INTO streetname VALUES('kenninghall'); + INSERT INTO streetname VALUES('kent village'); + INSERT INTO streetname VALUES('kestral ridge'); + INSERT INTO streetname VALUES('kestrel'); + INSERT INTO streetname VALUES('kilmartin'); + INSERT INTO streetname VALUES('kilty'); + INSERT INTO streetname VALUES('kinglet'); + INSERT INTO streetname VALUES('kingsland'); + INSERT INTO streetname VALUES('kingsnorth'); + INSERT INTO streetname VALUES('kinsmore'); + INSERT INTO streetname VALUES('kirkgard'); + INSERT INTO streetname VALUES('kirkmont'); + INSERT INTO streetname VALUES('knightsgate'); + INSERT INTO streetname VALUES('kobuk'); + INSERT INTO streetname VALUES('kotlik'); + INSERT INTO streetname VALUES('kotz'); + INSERT INTO streetname VALUES('kyndall walk'); + INSERT INTO streetname VALUES('laborde'); + INSERT INTO streetname VALUES('lady bank'); + INSERT INTO streetname VALUES('lagrande'); + INSERT INTO streetname VALUES('lake'); + INSERT INTO streetname VALUES('lakeridge commons'); + INSERT INTO streetname VALUES('lakeview'); + INSERT INTO streetname VALUES('lakewood edge'); + INSERT INTO streetname VALUES('lakota'); + INSERT INTO streetname VALUES('lambrook'); + INSERT INTO streetname VALUES('lampkin'); + INSERT INTO streetname VALUES('lampkin park'); + INSERT INTO streetname VALUES('langham'); + INSERT INTO streetname VALUES('lanzerac manor'); + INSERT INTO streetname VALUES('larkmead forest'); + INSERT INTO streetname VALUES('lattice'); + INSERT INTO streetname VALUES('laurel crest'); + INSERT INTO streetname VALUES('laurel ridge'); + INSERT INTO streetname VALUES('laurel run'); + INSERT INTO streetname VALUES('laurenfield'); + INSERT INTO streetname VALUES('laveta'); + INSERT INTO streetname VALUES('lazy day'); + INSERT INTO streetname VALUES('leawood run'); + INSERT INTO streetname VALUES('lee marie'); + INSERT INTO streetname VALUES('legacy lake'); + INSERT INTO streetname VALUES('legacy park'); + INSERT INTO streetname VALUES('legato'); + INSERT INTO streetname VALUES('legolas'); + INSERT INTO streetname VALUES('leigh glen'); + INSERT INTO streetname VALUES('lence'); + INSERT INTO streetname VALUES('lenox hill'); + INSERT INTO streetname VALUES('leonine'); + INSERT INTO streetname VALUES('leslie'); + INSERT INTO streetname VALUES('lester hill'); + INSERT INTO streetname VALUES('levisey'); + INSERT INTO streetname VALUES('liberty bell'); + INSERT INTO streetname VALUES('linden berry'); + INSERT INTO streetname VALUES('lisbon'); + INSERT INTO streetname VALUES('little stoney'); + INSERT INTO streetname VALUES('livengood'); + INSERT INTO streetname VALUES('lochway'); + INSERT INTO streetname VALUES('lockman'); + INSERT INTO streetname VALUES('loganville'); + INSERT INTO streetname VALUES('lone tree'); + INSERT INTO streetname VALUES('long creek park'); + INSERT INTO streetname VALUES('long forest'); + INSERT INTO streetname VALUES('looking glass'); + INSERT INTO streetname VALUES('lookout point'); + INSERT INTO streetname VALUES('lowen'); + INSERT INTO streetname VALUES('lusby'); + INSERT INTO streetname VALUES('lyleton'); + INSERT INTO streetname VALUES('lynn lee'); + INSERT INTO streetname VALUES('lynnewood glen'); + INSERT INTO streetname VALUES('machrie'); + INSERT INTO streetname VALUES('mackinac'); + INSERT INTO streetname VALUES('maddox'); + INSERT INTO streetname VALUES('madison park'); + INSERT INTO streetname VALUES('mallard'); + INSERT INTO streetname VALUES('mallard cove'); + INSERT INTO streetname VALUES('mallard forest'); + INSERT INTO streetname VALUES('mallard grove'); + INSERT INTO streetname VALUES('mallard hill'); + INSERT INTO streetname VALUES('mallard park'); + INSERT INTO streetname VALUES('mallard ridge'); + INSERT INTO streetname VALUES('mallard view'); + INSERT INTO streetname VALUES('manbey'); + INSERT INTO streetname VALUES('manning'); + INSERT INTO streetname VALUES('mantario'); + INSERT INTO streetname VALUES('maple'); + INSERT INTO streetname VALUES('maple cove'); + INSERT INTO streetname VALUES('maple park'); + INSERT INTO streetname VALUES('marathon hill'); + INSERT INTO streetname VALUES('marbury'); + INSERT INTO streetname VALUES('marett'); + INSERT INTO streetname VALUES('marigold'); + INSERT INTO streetname VALUES('marionwood'); + INSERT INTO streetname VALUES('marshbank'); + INSERT INTO streetname VALUES('mason'); + INSERT INTO streetname VALUES('mayapple'); + INSERT INTO streetname VALUES('maylandia'); + INSERT INTO streetname VALUES('mayspring'); + INSERT INTO streetname VALUES('mcadam'); + INSERT INTO streetname VALUES('mcchesney'); + INSERT INTO streetname VALUES('mccurdy'); + INSERT INTO streetname VALUES('mcgrath'); + INSERT INTO streetname VALUES('mckendree'); + INSERT INTO streetname VALUES('mclaughlin'); + INSERT INTO streetname VALUES('mctaggart'); + INSERT INTO streetname VALUES('meadow green'); + INSERT INTO streetname VALUES('meadow knoll'); + INSERT INTO streetname VALUES('meadow post'); + INSERT INTO streetname VALUES('meadowmont'); + INSERT INTO streetname VALUES('meadowmont view'); + INSERT INTO streetname VALUES('meadowview hills'); + INSERT INTO streetname VALUES('melshire'); + INSERT INTO streetname VALUES('melstrand'); + INSERT INTO streetname VALUES('mentone'); + INSERT INTO streetname VALUES('meridale crossing'); + INSERT INTO streetname VALUES('merion hills'); + INSERT INTO streetname VALUES('merlot'); + INSERT INTO streetname VALUES('mersham'); + INSERT INTO streetname VALUES('metromont'); + INSERT INTO streetname VALUES('metromont industrial'); + INSERT INTO streetname VALUES('michaw'); + INSERT INTO streetname VALUES('milhaven'); + INSERT INTO streetname VALUES('milhof'); + INSERT INTO streetname VALUES('millstream ridge'); + INSERT INTO streetname VALUES('mineral ridge'); + INSERT INTO streetname VALUES('mint thistle'); + INSERT INTO streetname VALUES('mintleaf'); + INSERT INTO streetname VALUES('mintvale'); + INSERT INTO streetname VALUES('misty'); + INSERT INTO streetname VALUES('misty arbor'); + INSERT INTO streetname VALUES('misty creek'); + INSERT INTO streetname VALUES('misty oaks'); + INSERT INTO streetname VALUES('misty wood'); + INSERT INTO streetname VALUES('mitzi deborah'); + INSERT INTO streetname VALUES('mobile'); + INSERT INTO streetname VALUES('molly elizabeth'); + INSERT INTO streetname VALUES('monmouth'); + INSERT INTO streetname VALUES('montrose'); + INSERT INTO streetname VALUES('moonlight'); + INSERT INTO streetname VALUES('moose'); + INSERT INTO streetname VALUES('morning dew'); + INSERT INTO streetname VALUES('morningsong'); + INSERT INTO streetname VALUES('morningview'); + INSERT INTO streetname VALUES('morsey'); + INSERT INTO streetname VALUES('moss glen'); + INSERT INTO streetname VALUES('mossy bank'); + INSERT INTO streetname VALUES('motor sport'); + INSERT INTO streetname VALUES('mountain laurel'); + INSERT INTO streetname VALUES('mourning dove'); + INSERT INTO streetname VALUES('mozart'); + INSERT INTO streetname VALUES('munsing'); + INSERT INTO streetname VALUES('murray'); + INSERT INTO streetname VALUES('nathan'); + INSERT INTO streetname VALUES('netherhall'); + INSERT INTO streetname VALUES('netherton'); + INSERT INTO streetname VALUES('neuhoff'); + INSERT INTO streetname VALUES('nevin'); + INSERT INTO streetname VALUES('nevin brook'); + INSERT INTO streetname VALUES('nevin glen'); + INSERT INTO streetname VALUES('nevin place'); + INSERT INTO streetname VALUES('new england'); + INSERT INTO streetname VALUES('new house'); + INSERT INTO streetname VALUES('newbary'); + INSERT INTO streetname VALUES('newchurch'); + INSERT INTO streetname VALUES('newfane'); + INSERT INTO streetname VALUES('newgard'); + INSERT INTO streetname VALUES('nicholas'); + INSERT INTO streetname VALUES('nicole'); + INSERT INTO streetname VALUES('nobility'); + INSERT INTO streetname VALUES('norcroft'); + INSERT INTO streetname VALUES('northridge'); + INSERT INTO streetname VALUES('northside'); + INSERT INTO streetname VALUES('northwoods business'); + INSERT INTO streetname VALUES('norway'); + INSERT INTO streetname VALUES('nottinghill'); + INSERT INTO streetname VALUES('numenore'); + INSERT INTO streetname VALUES('nyewood'); + INSERT INTO streetname VALUES('oak'); + INSERT INTO streetname VALUES('oak cove'); + INSERT INTO streetname VALUES('oak pasture'); + INSERT INTO streetname VALUES('oakburn'); + INSERT INTO streetname VALUES('oakwinds'); + INSERT INTO streetname VALUES('oakwood'); + INSERT INTO streetname VALUES('obrien'); + INSERT INTO streetname VALUES('ocala'); + INSERT INTO streetname VALUES('old bridge'); + INSERT INTO streetname VALUES('old fox'); + INSERT INTO streetname VALUES('old potters'); + INSERT INTO streetname VALUES('old statesville'); + INSERT INTO streetname VALUES('old steine'); + INSERT INTO streetname VALUES('old stoney creek'); + INSERT INTO streetname VALUES('old sugar creek'); + INSERT INTO streetname VALUES('old timber'); + INSERT INTO streetname VALUES('old wagon'); + INSERT INTO streetname VALUES('old willow'); + INSERT INTO streetname VALUES('oldenway'); + INSERT INTO streetname VALUES('oneida'); + INSERT INTO streetname VALUES('ontario'); + INSERT INTO streetname VALUES('oriole'); + INSERT INTO streetname VALUES('orofino'); + INSERT INTO streetname VALUES('orr'); + INSERT INTO streetname VALUES('osage'); + INSERT INTO streetname VALUES('osceola'); + INSERT INTO streetname VALUES('osprey knoll'); + INSERT INTO streetname VALUES('oxford hill'); + INSERT INTO streetname VALUES('painted fern'); + INSERT INTO streetname VALUES('painted pony'); + INSERT INTO streetname VALUES('paisley'); + INSERT INTO streetname VALUES('pale moss'); + INSERT INTO streetname VALUES('palladium'); + INSERT INTO streetname VALUES('palmutum'); + INSERT INTO streetname VALUES('palustris'); + INSERT INTO streetname VALUES('panglemont'); + INSERT INTO streetname VALUES('panther'); + INSERT INTO streetname VALUES('panthersville'); + INSERT INTO streetname VALUES('paper whites'); + INSERT INTO streetname VALUES('park'); + INSERT INTO streetname VALUES('parker green'); + INSERT INTO streetname VALUES('parkhouse'); + INSERT INTO streetname VALUES('passour ridge'); + INSERT INTO streetname VALUES('pasture view'); + INSERT INTO streetname VALUES('patricia ann'); + INSERT INTO streetname VALUES('patton'); + INSERT INTO streetname VALUES('patton ridge'); + INSERT INTO streetname VALUES('pawpaw'); + INSERT INTO streetname VALUES('peach'); + INSERT INTO streetname VALUES('peakwood'); + INSERT INTO streetname VALUES('pebble creek'); + INSERT INTO streetname VALUES('pecan cove'); + INSERT INTO streetname VALUES('pedigree'); + INSERT INTO streetname VALUES('pelorus'); + INSERT INTO streetname VALUES('penmore'); + INSERT INTO streetname VALUES('pensfold'); + INSERT INTO streetname VALUES('pepperstone'); + INSERT INTO streetname VALUES('peregrine'); + INSERT INTO streetname VALUES('periwinkle'); + INSERT INTO streetname VALUES('perkins'); + INSERT INTO streetname VALUES('pete brown'); + INSERT INTO streetname VALUES('phillips'); + INSERT INTO streetname VALUES('pickway'); + INSERT INTO streetname VALUES('piercy woods'); + INSERT INTO streetname VALUES('pierpoint'); + INSERT INTO streetname VALUES('pine'); + INSERT INTO streetname VALUES('pine branch'); + INSERT INTO streetname VALUES('pine meadow'); + INSERT INTO streetname VALUES('pineleaf'); + INSERT INTO streetname VALUES('pinewood'); + INSERT INTO streetname VALUES('pintail'); + INSERT INTO streetname VALUES('pipestone'); + INSERT INTO streetname VALUES('placer maple'); + INSERT INTO streetname VALUES('plover'); + INSERT INTO streetname VALUES('plum'); + INSERT INTO streetname VALUES('po box'); + INSERT INTO streetname VALUES('pochard'); + INSERT INTO streetname VALUES('pointview'); + INSERT INTO streetname VALUES('polk and white'); + INSERT INTO streetname VALUES('pond valley'); + INSERT INTO streetname VALUES('pondridge'); + INSERT INTO streetname VALUES('pope farm'); + INSERT INTO streetname VALUES('poplar grove'); + INSERT INTO streetname VALUES('poplar springs'); + INSERT INTO streetname VALUES('portola'); + INSERT INTO streetname VALUES('potters glen'); + INSERT INTO streetname VALUES('powatan'); + INSERT INTO streetname VALUES('prairie valley'); + INSERT INTO streetname VALUES('prescott'); + INSERT INTO streetname VALUES('presmann'); + INSERT INTO streetname VALUES('prestigious'); + INSERT INTO streetname VALUES('princess'); + INSERT INTO streetname VALUES('prosperity'); + INSERT INTO streetname VALUES('prosperity church'); + INSERT INTO streetname VALUES('prosperity commons'); + INSERT INTO streetname VALUES('prosperity park'); + INSERT INTO streetname VALUES('prosperity point'); + INSERT INTO streetname VALUES('prosperity ridge'); + INSERT INTO streetname VALUES('prosperity view'); + INSERT INTO streetname VALUES('purple finch'); + INSERT INTO streetname VALUES('quail'); + INSERT INTO streetname VALUES('queensbury'); + INSERT INTO streetname VALUES('quinn'); + INSERT INTO streetname VALUES('racine'); + INSERT INTO streetname VALUES('radbourne'); + INSERT INTO streetname VALUES('raddington'); + INSERT INTO streetname VALUES('raku'); + INSERT INTO streetname VALUES('rancliffe'); + INSERT INTO streetname VALUES('ravencrest'); + INSERT INTO streetname VALUES('reames'); + INSERT INTO streetname VALUES('rebecca run'); + INSERT INTO streetname VALUES('red bluff'); + INSERT INTO streetname VALUES('red clay'); + INSERT INTO streetname VALUES('red clover'); + INSERT INTO streetname VALUES('red rose'); + INSERT INTO streetname VALUES('red shed'); + INSERT INTO streetname VALUES('red tail'); + INSERT INTO streetname VALUES('redbridge'); + INSERT INTO streetname VALUES('redstart'); + INSERT INTO streetname VALUES('redstone view'); + INSERT INTO streetname VALUES('reedmont'); + INSERT INTO streetname VALUES('reeves'); + INSERT INTO streetname VALUES('regal'); + INSERT INTO streetname VALUES('reinbeck'); + INSERT INTO streetname VALUES('retriever'); + INSERT INTO streetname VALUES('ribbonwalk'); + INSERT INTO streetname VALUES('richardson park'); + INSERT INTO streetname VALUES('richfield'); + INSERT INTO streetname VALUES('riddings'); + INSERT INTO streetname VALUES('ridge'); + INSERT INTO streetname VALUES('ridge cliff'); + INSERT INTO streetname VALUES('ridge path'); + INSERT INTO streetname VALUES('ridge peak'); + INSERT INTO streetname VALUES('ridgefield'); + INSERT INTO streetname VALUES('ridgeline'); + INSERT INTO streetname VALUES('ridgeview commons'); + INSERT INTO streetname VALUES('riley'); + INSERT INTO streetname VALUES('riley woods'); + INSERT INTO streetname VALUES('rillet'); + INSERT INTO streetname VALUES('rindle'); + INSERT INTO streetname VALUES('rivendell'); + INSERT INTO streetname VALUES('robin'); + INSERT INTO streetname VALUES('robins nest'); + INSERT INTO streetname VALUES('robur'); + INSERT INTO streetname VALUES('robyns glen'); + INSERT INTO streetname VALUES('rock stream'); + INSERT INTO streetname VALUES('rockwell'); + INSERT INTO streetname VALUES('rockwell church'); + INSERT INTO streetname VALUES('rocky brook'); + INSERT INTO streetname VALUES('rocky ford club'); + INSERT INTO streetname VALUES('rotary'); + INSERT INTO streetname VALUES('rouda'); + INSERT INTO streetname VALUES('royal bluff'); + INSERT INTO streetname VALUES('royal celadon'); + INSERT INTO streetname VALUES('rubin lura'); + INSERT INTO streetname VALUES('runswyck'); + INSERT INTO streetname VALUES('ruth ferrell'); + INSERT INTO streetname VALUES('ruth polk'); + INSERT INTO streetname VALUES('ryan jay'); + INSERT INTO streetname VALUES('sackett'); + INSERT INTO streetname VALUES('saddle pace'); + INSERT INTO streetname VALUES('saddle run'); + INSERT INTO streetname VALUES('saddle trail'); + INSERT INTO streetname VALUES('saguaro'); + INSERT INTO streetname VALUES('saint audrey'); + INSERT INTO streetname VALUES('saint bernard'); + INSERT INTO streetname VALUES('saint frances'); + INSERT INTO streetname VALUES('sam roper'); + INSERT INTO streetname VALUES('samara'); + INSERT INTO streetname VALUES('sanders creek'); + INSERT INTO streetname VALUES('saquache'); + INSERT INTO streetname VALUES('sarnia'); + INSERT INTO streetname VALUES('savannah springs'); + INSERT INTO streetname VALUES('sawgrass ridge'); + INSERT INTO streetname VALUES('saxonbury'); + INSERT INTO streetname VALUES('scotch moss'); + INSERT INTO streetname VALUES('seasons'); + INSERT INTO streetname VALUES('serenity'); + INSERT INTO streetname VALUES('seths'); + INSERT INTO streetname VALUES('shadow lawn'); + INSERT INTO streetname VALUES('shadow oaks'); + INSERT INTO streetname VALUES('shadow pine'); + INSERT INTO streetname VALUES('shadyside'); + INSERT INTO streetname VALUES('shallow oak'); + INSERT INTO streetname VALUES('shelley'); + INSERT INTO streetname VALUES('shining oak'); + INSERT INTO streetname VALUES('ship'); + INSERT INTO streetname VALUES('shore haven'); + INSERT INTO streetname VALUES('shuman'); + INSERT INTO streetname VALUES('sidney'); + INSERT INTO streetname VALUES('silver birch'); + INSERT INTO streetname VALUES('silvermere'); + INSERT INTO streetname VALUES('simonton'); + INSERT INTO streetname VALUES('singing hills'); + INSERT INTO streetname VALUES('singing oak'); + INSERT INTO streetname VALUES('sipes'); + INSERT INTO streetname VALUES('six point'); + INSERT INTO streetname VALUES('skycrest'); + INSERT INTO streetname VALUES('skyline'); + INSERT INTO streetname VALUES('small'); + INSERT INTO streetname VALUES('smith corners'); + INSERT INTO streetname VALUES('smithwood'); + INSERT INTO streetname VALUES('snow hill'); + INSERT INTO streetname VALUES('soapstone'); + INSERT INTO streetname VALUES('sobeck'); + INSERT INTO streetname VALUES('socata'); + INSERT INTO streetname VALUES('solace'); + INSERT INTO streetname VALUES('solway'); + INSERT INTO streetname VALUES('song sparrow'); + INSERT INTO streetname VALUES('sorrento'); + INSERT INTO streetname VALUES('spector'); + INSERT INTO streetname VALUES('spin drift'); + INSERT INTO streetname VALUES('spring crest'); + INSERT INTO streetname VALUES('spring lee'); + INSERT INTO streetname VALUES('spring park'); + INSERT INTO streetname VALUES('spring terrace'); + INSERT INTO streetname VALUES('spring trace'); + INSERT INTO streetname VALUES('springhaven'); + INSERT INTO streetname VALUES('squirrel trail'); + INSERT INTO streetname VALUES('stardust'); + INSERT INTO streetname VALUES('stargaze'); + INSERT INTO streetname VALUES('starita'); + INSERT INTO streetname VALUES('starmount'); + INSERT INTO streetname VALUES('statesville'); + INSERT INTO streetname VALUES('steed'); + INSERT INTO streetname VALUES('steelewood'); + INSERT INTO streetname VALUES('steepleglen'); + INSERT INTO streetname VALUES('stephens farm'); + INSERT INTO streetname VALUES('stewarton'); + INSERT INTO streetname VALUES('stone park'); + INSERT INTO streetname VALUES('stonebrook'); + INSERT INTO streetname VALUES('stonefield'); + INSERT INTO streetname VALUES('stoneglen'); + INSERT INTO streetname VALUES('stonemarsh'); + INSERT INTO streetname VALUES('stoney garden'); + INSERT INTO streetname VALUES('stoney run'); + INSERT INTO streetname VALUES('stoney valley'); + INSERT INTO streetname VALUES('stoneykirk'); + INSERT INTO streetname VALUES('stream bank'); + INSERT INTO streetname VALUES('stream ridge'); + INSERT INTO streetname VALUES('suburban'); + INSERT INTO streetname VALUES('suffield'); + INSERT INTO streetname VALUES('sugar creek'); + INSERT INTO streetname VALUES('sugarberry'); + INSERT INTO streetname VALUES('sugarstone'); + INSERT INTO streetname VALUES('summer creek'); + INSERT INTO streetname VALUES('summer valley'); + INSERT INTO streetname VALUES('summercrest'); + INSERT INTO streetname VALUES('summercroft'); + INSERT INTO streetname VALUES('summerford'); + INSERT INTO streetname VALUES('summergold'); + INSERT INTO streetname VALUES('sunbeam'); + INSERT INTO streetname VALUES('sunbridge'); + INSERT INTO streetname VALUES('sunpath'); + INSERT INTO streetname VALUES('sunset'); + INSERT INTO streetname VALUES('sunset ridge'); + INSERT INTO streetname VALUES('sunstone'); + INSERT INTO streetname VALUES('suntrace'); + INSERT INTO streetname VALUES('sunwalk'); + INSERT INTO streetname VALUES('sutters hill'); + INSERT INTO streetname VALUES('suttonview'); + INSERT INTO streetname VALUES('swallow tail'); + INSERT INTO streetname VALUES('swanston'); + INSERT INTO streetname VALUES('sweet grove'); + INSERT INTO streetname VALUES('sweet rose'); + INSERT INTO streetname VALUES('sweetbriar ridge'); + INSERT INTO streetname VALUES('sweetfield'); + INSERT INTO streetname VALUES('sydney overlook'); + INSERT INTO streetname VALUES('sylvan'); + INSERT INTO streetname VALUES('symphony woods'); + INSERT INTO streetname VALUES('tallia'); + INSERT INTO streetname VALUES('tallu'); + INSERT INTO streetname VALUES('talwyn'); + INSERT INTO streetname VALUES('tanager'); + INSERT INTO streetname VALUES('tanager park'); + INSERT INTO streetname VALUES('tangley'); + INSERT INTO streetname VALUES('taranasay'); + INSERT INTO streetname VALUES('tarby'); + INSERT INTO streetname VALUES('tarland'); + INSERT INTO streetname VALUES('tarpway'); + INSERT INTO streetname VALUES('tauten'); + INSERT INTO streetname VALUES('taymouth'); + INSERT INTO streetname VALUES('ten trees'); + INSERT INTO streetname VALUES('terrace view'); + INSERT INTO streetname VALUES('terrier'); + INSERT INTO streetname VALUES('tesh'); + INSERT INTO streetname VALUES('teton'); + INSERT INTO streetname VALUES('tewkesbury'); + INSERT INTO streetname VALUES('thelema'); + INSERT INTO streetname VALUES('thistle bloom'); + INSERT INTO streetname VALUES('thistledown'); + INSERT INTO streetname VALUES('thomas ridge'); + INSERT INTO streetname VALUES('thornbrook'); + INSERT INTO streetname VALUES('tifton grass'); + INSERT INTO streetname VALUES('tigerton'); + INSERT INTO streetname VALUES('tomsie efird'); + INSERT INTO streetname VALUES('tor'); + INSERT INTO streetname VALUES('torphin'); + INSERT INTO streetname VALUES('torrence'); + INSERT INTO streetname VALUES('towering pine'); + INSERT INTO streetname VALUES('towhee'); + INSERT INTO streetname VALUES('toxaway'); + INSERT INTO streetname VALUES('tracy glenn'); + INSERT INTO streetname VALUES('tradition view'); + INSERT INTO streetname VALUES('trailer'); + INSERT INTO streetname VALUES('transport'); + INSERT INTO streetname VALUES('trehurst'); + INSERT INTO streetname VALUES('trexler'); + INSERT INTO streetname VALUES('trillium fields'); + INSERT INTO streetname VALUES('trimbach'); + INSERT INTO streetname VALUES('tucker'); + INSERT INTO streetname VALUES('tullamore'); + INSERT INTO streetname VALUES('tullock creek'); + INSERT INTO streetname VALUES('tunston'); + INSERT INTO streetname VALUES('tupelo'); + INSERT INTO streetname VALUES('turnabout'); + INSERT INTO streetname VALUES('turney'); + INSERT INTO streetname VALUES('turtle cross'); + INSERT INTO streetname VALUES('turtleback'); + INSERT INTO streetname VALUES('twelvestone'); + INSERT INTO streetname VALUES('twin'); + INSERT INTO streetname VALUES('twin brook'); + INSERT INTO streetname VALUES('twin lakes'); + INSERT INTO streetname VALUES('twisted pine'); + INSERT INTO streetname VALUES('tyler finley'); + INSERT INTO streetname VALUES('university station'); + INSERT INTO streetname VALUES('uphill'); + INSERT INTO streetname VALUES('valeview'); + INSERT INTO streetname VALUES('valhalla'); + INSERT INTO streetname VALUES('van'); + INSERT INTO streetname VALUES('vance davis'); + INSERT INTO streetname VALUES('vanhoy'); + INSERT INTO streetname VALUES('veckman'); + INSERT INTO streetname VALUES('victoria'); + INSERT INTO streetname VALUES('victory'); + INSERT INTO streetname VALUES('village glen'); + INSERT INTO streetname VALUES('vireo'); + INSERT INTO streetname VALUES('viscount'); + INSERT INTO streetname VALUES('voeltz'); + INSERT INTO streetname VALUES('wade e morgan'); + INSERT INTO streetname VALUES('wake'); + INSERT INTO streetname VALUES('wales'); + INSERT INTO streetname VALUES('wallace ridge'); + INSERT INTO streetname VALUES('waltham'); + INSERT INTO streetname VALUES('wanamassa'); + INSERT INTO streetname VALUES('warbler wood'); + INSERT INTO streetname VALUES('washington'); + INSERT INTO streetname VALUES('water'); + INSERT INTO streetname VALUES('waterelm'); + INSERT INTO streetname VALUES('waterford hills'); + INSERT INTO streetname VALUES('waterford valley'); + INSERT INTO streetname VALUES('waterloo'); + INSERT INTO streetname VALUES('waterton leas'); + INSERT INTO streetname VALUES('waverly lynn'); + INSERT INTO streetname VALUES('waverlyglen'); + INSERT INTO streetname VALUES('wayside'); + INSERT INTO streetname VALUES('westbury lake'); + INSERT INTO streetname VALUES('westray'); + INSERT INTO streetname VALUES('whistlers chase'); + INSERT INTO streetname VALUES('whistley green'); + INSERT INTO streetname VALUES('whistling oak'); + INSERT INTO streetname VALUES('whitcomb'); + INSERT INTO streetname VALUES('white aspen'); + INSERT INTO streetname VALUES('white cascade'); + INSERT INTO streetname VALUES('white mist'); + INSERT INTO streetname VALUES('white rock'); + INSERT INTO streetname VALUES('white stag'); + INSERT INTO streetname VALUES('whitegate'); + INSERT INTO streetname VALUES('whitehill'); + INSERT INTO streetname VALUES('whitetail'); + INSERT INTO streetname VALUES('whitewood'); + INSERT INTO streetname VALUES('wilburn park'); + INSERT INTO streetname VALUES('wild garden'); + INSERT INTO streetname VALUES('wild rose'); + INSERT INTO streetname VALUES('wilkins terrace'); + INSERT INTO streetname VALUES('william ficklen'); + INSERT INTO streetname VALUES('wiltshire ridge'); + INSERT INTO streetname VALUES('windchase'); + INSERT INTO streetname VALUES('winding jordan'); + INSERT INTO streetname VALUES('windy meadow'); + INSERT INTO streetname VALUES('winghaven'); + INSERT INTO streetname VALUES('wingmont'); + INSERT INTO streetname VALUES('winslow'); + INSERT INTO streetname VALUES('winter pine'); + INSERT INTO streetname VALUES('winter view'); + INSERT INTO streetname VALUES('wolf creek'); + INSERT INTO streetname VALUES('wondering oak'); + INSERT INTO streetname VALUES('woodard'); + INSERT INTO streetname VALUES('woodfire'); + INSERT INTO streetname VALUES('woodland commons'); + INSERT INTO streetname VALUES('woodland hills'); + INSERT INTO streetname VALUES('woodnotch'); + INSERT INTO streetname VALUES('woodstone'); + INSERT INTO streetname VALUES('worsley'); + INSERT INTO streetname VALUES('wren creek'); + INSERT INTO streetname VALUES('wrens nest'); + INSERT INTO streetname VALUES('wrexham'); + INSERT INTO streetname VALUES('wt harris'); + INSERT INTO streetname VALUES('wylie meadow'); + INSERT INTO streetname VALUES('wynborough'); + INSERT INTO streetname VALUES('wynbrook'); + INSERT INTO streetname VALUES('wyndham hill'); + INSERT INTO streetname VALUES('yandem'); + INSERT INTO streetname VALUES('yellow rose'); + INSERT INTO streetname VALUES('yellow spaniel'); + INSERT INTO streetname VALUES('yorkford'); + INSERT INTO streetname VALUES('ziegler'); + INSERT INTO streetname VALUES('zion renaissance'); + + SELECT count(*) FROM streetname; + } +} {1228} + +do_test fuzzer1-2.1 { + execsql { + SELECT n, distance FROM f2, streetname + WHERE f2.word MATCH 'wersley' + AND f2.distance<=150 + AND f2.word=streetname.n + } +} {worsley 37} +do_test fuzzer1-2.2 { + execsql { + SELECT n, distance FROM f2, streetname + WHERE f2.word MATCH 'testledown' + AND f2.distance<=150 + AND f2.word=streetname.n + } +} {thistledown 103} +do_test fuzzer1-2.3 { + execsql { + SELECT DISTINCT streetname.n FROM f2, streetname + WHERE f2.word MATCH 'tayle' + AND f2.distance<=200 + AND streetname.n>=f2.word AND streetname.n<=(f2.word || x'F7BFBFBF') + } +} {steelewood tallia tallu talwyn taymouth thelema trailer {tyler finley}} + + +finish_test diff --git a/test/memdb.test b/test/memdb.test index d987d21e0..1da3d7c58 100644 --- a/test/memdb.test +++ b/test/memdb.test @@ -363,19 +363,13 @@ do_test memdb-6.15 { } } {} -ifcapable subquery { +ifcapable subquery&&vtab { do_test memdb-7.1 { + register_wholenumber_module db execsql { CREATE TABLE t6(x); - INSERT INTO t6 VALUES(1); - INSERT INTO t6 SELECT x+1 FROM t6; - INSERT INTO t6 SELECT x+2 FROM t6; - INSERT INTO t6 SELECT x+4 FROM t6; - INSERT INTO t6 SELECT x+8 FROM t6; - INSERT INTO t6 SELECT x+16 FROM t6; - INSERT INTO t6 SELECT x+32 FROM t6; - INSERT INTO t6 SELECT x+64 FROM t6; - INSERT INTO t6 SELECT x+128 FROM t6; + CREATE VIRTUAL TABLE nums USING wholenumber; + INSERT INTO t6 SELECT value FROM nums WHERE value BETWEEN 1 AND 256; SELECT count(*) FROM (SELECT DISTINCT x FROM t6); } } {256} 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 d1952a30d..2d3a24a8f 100644 --- a/test/oserror.test +++ b/test/oserror.test @@ -100,17 +100,22 @@ do_test 2.1.1 { set ::log [list] file mkdir test.db-wal forcedelete test.db - sqlite3 dbh test.db - catchsql { SELECT * FROM sqlite_master } dbh + list [catch { + sqlite3 dbh test.db + execsql { SELECT * FROM sqlite_master } dbh + } msg] $msg } {1 {disk I/O error}} -do_re_test 2.1.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - } +do_re_test 2.1.2 { + lindex $::log 0 +} {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - } do_test 2.1.3 { - dbh close + catch { dbh close } forcedelete test.db-wal } {} +test_syscall reset sqlite3_shutdown test_sqlite3_log sqlite3_initialize diff --git a/test/pagerfault3.test b/test/pagerfault3.test index 6f63b83f9..3d192e6d4 100644 --- a/test/pagerfault3.test +++ b/test/pagerfault3.test @@ -26,6 +26,7 @@ if {[permutation] == "inmemory_journal"} { # do_test pagerfault3-pre1 { execsql { + PRAGMA auto_vacuum = 0; PRAGMA page_size = 2048; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(1200)); diff --git a/test/syscall.test b/test/syscall.test index 21295a59c..6aca75ec3 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -19,8 +19,12 @@ if {[llength [info commands test_syscall]]==0} { finish_test return } -set testprefix syscall +if {[test_syscall defaultvfs] != "unix"} { + finish_test + return +} +set testprefix syscall #------------------------------------------------------------------------- # Tests for the xSetSystemCall method. @@ -52,12 +56,14 @@ 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 +foreach s { + open close access getcwd stat fstat ftruncate + fcntl read pread write pwrite fchmod fallocate + pread64 pwrite64 +} { + if {[test_syscall exists $s]} {lappend syscall_list $s} +} +do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list] #------------------------------------------------------------------------- # This test verifies that if a call to open() fails and errno is set to @@ -215,8 +221,10 @@ foreach {nByte res} { } { do_test 7.$nByte { create_db_file $nByte - sqlite3 db test.db - catchsql { CREATE TABLE t1(a, b) } + list [catch { + sqlite3 db test.db + execsql { CREATE TABLE t1(a, b) } + } msg] $msg } $res catch { db close } } @@ -245,6 +253,5 @@ foreach {tn hint size} { } $size } - - +test_syscall reset 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/walfault.test b/test/walfault.test index 20c981bb5..3a90f9c69 100644 --- a/test/walfault.test +++ b/test/walfault.test @@ -133,6 +133,7 @@ if {[permutation] != "inmemory_journal"} { faultsim_restore_and_reopen } -body { execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('a', 'b'); @@ -523,6 +524,7 @@ do_faultsim_test walfault-13.3 -prep { do_test walfault-14-pre { faultsim_delete_and_reopen execsql { + PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE abc(a PRIMARY KEY); diff --git a/tool/mkopts.tcl b/tool/mkopts.tcl index e3ddcb9ee..e3ddcb9ee 100755..100644 --- a/tool/mkopts.tcl +++ b/tool/mkopts.tcl 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 |