Add support for using separate worker threads to speed large sorts.
The SQLITE_MAX_WORKER_THREADS and SQLITE_DEFAULT_WORKER_THREADS compile-time options and the SQLITE_LIMIT_WORKER_THREADS argument to sqlite3_limit() and the "PRAGMA threads=N" pragma are added. FossilOrigin-Name: b1c0f0bc1bd8a3477cd7a7ab510f0442ac88b517
This commit is contained in:
commit
0f8f26716c
@ -177,7 +177,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
table.lo threads.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
@ -263,6 +263,7 @@ SRC = \
|
||||
$(TOP)/src/sqliteInt.h \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/trigger.c \
|
||||
@ -737,6 +738,9 @@ status.lo: $(TOP)/src/status.c $(HDR)
|
||||
table.lo: $(TOP)/src/table.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/table.c
|
||||
|
||||
threads.lo: $(TOP)/src/threads.c $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/threads.c
|
||||
|
||||
tokenize.lo: $(TOP)/src/tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/tokenize.c
|
||||
|
||||
|
@ -636,7 +636,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
|
||||
pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \
|
||||
table.lo tokenize.lo trigger.lo \
|
||||
table.lo threads.lo tokenize.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
|
||||
@ -733,6 +733,7 @@ SRC = \
|
||||
$(TOP)\src\sqliteInt.h \
|
||||
$(TOP)\src\sqliteLimit.h \
|
||||
$(TOP)\src\table.c \
|
||||
$(TOP)\src\threads.c \
|
||||
$(TOP)\src\tclsqlite.c \
|
||||
$(TOP)\src\tokenize.c \
|
||||
$(TOP)\src\trigger.c \
|
||||
@ -1216,6 +1217,9 @@ status.lo: $(TOP)\src\status.c $(HDR)
|
||||
table.lo: $(TOP)\src\table.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\table.c
|
||||
|
||||
threads.lo: $(TOP)\src\threads.c $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\threads.c
|
||||
|
||||
tokenize.lo: $(TOP)\src\tokenize.c keywordhash.h $(HDR)
|
||||
$(LTCOMPILE) -c $(TOP)\src\tokenize.c
|
||||
|
||||
|
4
main.mk
4
main.mk
@ -66,7 +66,7 @@ LIBOBJ+= vdbe.o parse.o \
|
||||
notify.o opcodes.o os.o os_unix.o os_win.o \
|
||||
pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
table.o tokenize.o trigger.o \
|
||||
table.o threads.o tokenize.o trigger.o \
|
||||
update.o util.o vacuum.o \
|
||||
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
|
||||
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
|
||||
@ -146,6 +146,7 @@ SRC = \
|
||||
$(TOP)/src/sqliteLimit.h \
|
||||
$(TOP)/src/table.c \
|
||||
$(TOP)/src/tclsqlite.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/trigger.c \
|
||||
$(TOP)/src/utf.c \
|
||||
@ -315,6 +316,7 @@ TESTSRC2 = \
|
||||
$(TOP)/src/pcache.c \
|
||||
$(TOP)/src/pcache1.c \
|
||||
$(TOP)/src/select.c \
|
||||
$(TOP)/src/threads.c \
|
||||
$(TOP)/src/tokenize.c \
|
||||
$(TOP)/src/utf.c \
|
||||
$(TOP)/src/util.c \
|
||||
|
70
manifest
70
manifest
@ -1,9 +1,9 @@
|
||||
C Attempt\sto\smake\sthe\sxDelete\smethod\sof\sthe\sunix\sVFS\smore\srobust\son\sVxWorks.
|
||||
D 2014-09-01T13:37:55.088
|
||||
C Add\ssupport\sfor\susing\sseparate\sworker\sthreads\sto\sspeed\slarge\ssorts.\nThe\sSQLITE_MAX_WORKER_THREADS\sand\sSQLITE_DEFAULT_WORKER_THREADS\scompile-time\noptions\sand\sthe\sSQLITE_LIMIT_WORKER_THREADS\sargument\sto\s\nsqlite3_limit()\sand\sthe\s"PRAGMA\sthreads=N"\spragma\sare\sadded.
|
||||
D 2014-09-01T17:36:46.754
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308
|
||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 5b04e657cf08a9ac7fc47d876c5c8be962c47d6b
|
||||
F Makefile.msc e31dee24038965fb6269d6d61073fd6b7e331dec
|
||||
F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0
|
||||
F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
|
||||
F VERSION 53a0b870e7f16d3b06623c31d233a304c163a6af
|
||||
@ -147,7 +147,7 @@ F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 036a65c9042b2f5013baf85c82629d700fe031c4
|
||||
F main.mk 9b5ccf1097050b1f16681f7d4beeea4f7f7ac2c3
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@ -168,10 +168,10 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c ec9d3f1295dafeb278c3830211cc5584132468f4
|
||||
F src/btree.c 4d0427bab54229030fc5b8577d2e5ffdc8129030
|
||||
F src/btree.c 2a483a8045118faa99867a8679da42754b532318
|
||||
F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8
|
||||
F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3
|
||||
F src/build.c 058e3aadb1376521ff291735237edf4c10f438fb
|
||||
F src/build.c c26b233dcdb1e2c8f468d49236c266f9f3de96d8
|
||||
F src/callback.c b97d0695ffcf6a8710ee445ffe56ee387d4d8a6f
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
|
||||
@ -190,7 +190,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 87c92f4a08e2f70220e3b22a9c3b2482d36a134a
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c 31c2122b7dd05a179049bbf163fd4839f181cbab
|
||||
F src/main.c 900dd06e41d22795cbb23ab0240397f1e2901bf7
|
||||
F src/main.c d2ef03a45552e11813c68326d5edfda992e319d4
|
||||
F src/malloc.c 954de5f998c23237e04474a3f2159bf483bba65a
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
|
||||
@ -209,7 +209,7 @@ F src/os.h 60d419395e32a8029fa380a80a3da2e9030f635e
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
|
||||
F src/os_unix.c 8525ca79457c5b4673a5fda2774ee39fe155f40f
|
||||
F src/os_win.c d067fce558a5032e6e6afe62899e5397bf63cf3e
|
||||
F src/os_win.c 08ce5616a5755da9400931fb39146e4a97801a2a
|
||||
F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21
|
||||
F src/pager.c 3e732d2bbdd8d8d95fed0c5ae7e718d73153c4c5
|
||||
F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428
|
||||
@ -217,23 +217,23 @@ F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0
|
||||
F src/pcache.c 3b3791297e8977002e56b4a9b8916f2039abad9b
|
||||
F src/pcache.h 9b559127b83f84ff76d735c8262f04853be0c59a
|
||||
F src/pcache1.c c5af6403a55178c9d1c09e4f77b0f9c88822762c
|
||||
F src/pragma.c d10ef67c4de79f78188b965b4b7988aff1d66f2e
|
||||
F src/pragma.c 14bcdb504128a476cce5bbc086d5226c5e46c225
|
||||
F src/prepare.c 3842c1dfc0b053458e3adcf9f6efc48e03e3fe3d
|
||||
F src/printf.c 00986c86ddfffefc2fd3c73667ff51b3b9709c74
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c 0ea356d32a5e884add23d1b9b4e8736681dd5697
|
||||
F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
|
||||
F src/select.c 1c4667571f2c9e339b5a5c5b152a9ea7b0bc4163
|
||||
F src/select.c 89e569b263535662f54b537eb9118b2c554ae7aa
|
||||
F src/shell.c 713cef4d73c05fc8e12f4960072329d767a05d50
|
||||
F src/sqlite.h.in ed9d35990c61f0388ca6405706455c4095310553
|
||||
F src/sqlite.h.in 74b42237f0d2b010779cc1b1a00190452b31a2ec
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h d8a9be2aa123a78c90ad4aba09b23e7dd3f8cc9f
|
||||
F src/sqliteInt.h 6244ee9052752e26d1275ab20c9b774385aa57d2
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 7d100e2e7aad614bb3d7026a41a0e3827dbaaebc
|
||||
F src/test1.c 14409a611e9c27c6c522c610bbff5561f05c1558
|
||||
F src/test1.c 363a5089230a92cf0aaa7a2945da7f2bf3b0a8d3
|
||||
F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
|
||||
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
|
||||
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
|
||||
@ -246,7 +246,7 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
|
||||
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
|
||||
F src/test_config.c 42fb068a038c8684741522f551325228b1389e63
|
||||
F src/test_config.c d5f00627c4f47515a57f905806558153cccd7253
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@ -257,7 +257,7 @@ F src/test_intarray.c db4614c2262a06abc4409dc048d59c580c38320f
|
||||
F src/test_intarray.h 9dc57417fb65bc7835cc18548852cc08cc062202
|
||||
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
|
||||
F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4
|
||||
F src/test_malloc.c 1ff5b1243d96124c9a180f3b89424820a1f337f3
|
||||
F src/test_malloc.c 5368fb1de77246da1ae0ff59cba0d30cb0e5812f
|
||||
F src/test_multiplex.c ca90057438b63bf0840ebb84d0ef050624519a76
|
||||
F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3
|
||||
F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f
|
||||
@ -278,20 +278,21 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
|
||||
F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 22dded4283dc4b25422f6444cdcb8d6b1ea0b5ff
|
||||
F src/tokenize.c ae45399d6252b4d736af43bee1576ce7bff86aec
|
||||
F src/trigger.c 4bddd12803275aa98f1c7ce0118fceb02b2167f6
|
||||
F src/update.c ea336ce7b8b3fc5e316ba8f082e6445babf81059
|
||||
F src/utf.c 77abb5e6d27f3d236e50f7c8fff1d00e15262359
|
||||
F src/util.c 068dcd26354a3898ccc64ad5c4bdb95a7a15d33a
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c fd193824d1cc4a71c631eb792ce837aab530210a
|
||||
F src/vdbe.c 90db7ad740b6d3f7ab446e6244dbc17ce495cca6
|
||||
F src/vdbe.h c63fad052c9e7388d551e556e119c0bcf6bebdf8
|
||||
F src/vdbeInt.h 4653bb420abb7acdc215659cdcedd3a59f336191
|
||||
F src/vdbeInt.h cdc8e421f85beb1ac9b4669ec5beadab6faa15e0
|
||||
F src/vdbeapi.c 09677a53dd8c71bcd670b0bd073bb9aefa02b441
|
||||
F src/vdbeaux.c dba006f67c9fd1b1d07ee7fb0fb38aa1905161d1
|
||||
F src/vdbeaux.c cef5d34a64ae3a65b56d96d3fd663246ec8e1c36
|
||||
F src/vdbeblob.c 848238dc73e93e48432991bb5651bf87d865eca4
|
||||
F src/vdbemem.c 921d5468a68ac06f369810992e84ca22cc730a62
|
||||
F src/vdbesort.c f7f5563bf7d4695ca8f3203f3bf9de96d04ed0b3
|
||||
F src/vdbesort.c 02646a9f86421776ae5d7594f620f9ed669d3698
|
||||
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
|
||||
F src/vtab.c 019dbfd0406a7447c990e1f7bd1dfcdb8895697f
|
||||
F src/wal.c 264df50a1b33124130b23180ded2e2c5663c652a
|
||||
@ -677,7 +678,7 @@ F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95
|
||||
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
|
||||
F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8
|
||||
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
|
||||
F test/malloc.test 4eb83876dfe4915766c179b687b8640437f14abf
|
||||
F test/malloc.test 96939d2d1a6f39667bbebe5bc27c6525f2ab614e
|
||||
F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a
|
||||
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
|
||||
F test/malloc5.test fafce0aa9157060445cd1a56ad50fc79d82f28c3
|
||||
@ -685,7 +686,7 @@ F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
|
||||
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
|
||||
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
|
||||
F test/malloc9.test 2307c6ee3703b0a21391f3ea92388b4b73f9105e
|
||||
F test/mallocA.test 1ba0367fb5434e7bc2fa4afcb30b14174d91b160
|
||||
F test/mallocA.test c049224adeb0244b8f6eb770c1fa6ac40f9b3518
|
||||
F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be
|
||||
F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6
|
||||
F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4
|
||||
@ -755,7 +756,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test bc474bafb022cc5014ef3a9c3d5ab61d6d6f587c
|
||||
F test/permutations.test cef25f5e8499a15846eccd06785f17f4180407ab
|
||||
F test/pragma.test 19d0241a007bcdd77fc2606ec60fc60357e7fc8b
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
@ -837,7 +838,11 @@ F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
|
||||
F test/skipscan5.test d8b9692b702745a0e41c23f9da6beac81df01196
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
|
||||
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
|
||||
F test/sort.test 15e1d3014abc3f6d4357ed81b93b82117aefd235
|
||||
F test/sort2.test 269f4f50c6e468cc32b302ae7ff0add8338ec6de
|
||||
F test/sort3.test 6178ade30810ac9166fcdf14b7065e49c0f534e2
|
||||
F test/sort4.test 6c37d85f7cd28d50cce222fcab84ccd771e105cb
|
||||
F test/sortfault.test b8e35177f97438b930ee87c9419ca2599e8073e1
|
||||
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
|
||||
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
|
||||
F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8
|
||||
@ -846,7 +851,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c d29c8048beb7ea9254191f3fde9414709166a920
|
||||
F test/speedtest1.c 83f6b3318f7ee60e52b978b5a5e5dd7e83dfb7ee
|
||||
F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49
|
||||
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
|
||||
F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de
|
||||
@ -866,7 +871,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl b4ff83a8b069633f4aca788236d10a7086112a49
|
||||
F test/tester.tcl ed77454e6c7b40eb501db7e79d1c6fbfd3eebbff
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@ -1153,10 +1158,10 @@ F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
|
||||
F tool/mkautoconfamal.sh 5dc5010e2e748a9e1bba67baca5956a2c2deda7b
|
||||
F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 78a77b2c554d534c6f2dc903130186ed15715460
|
||||
F tool/mkpragmatab.tcl cce51d8f60c7f145d8fccabe6b5dfdedf31c5f5c
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 1712d3d71256ca1f297046619c89e77a4d7c8f6d
|
||||
F tool/mksqlite3c.tcl ba274df71f5e6534b0a913c7c48eabfcbd0934b6
|
||||
F tool/mksqlite3c-noext.tcl 88a1e3b0c769773fb7a9ebb363ffc603a4ac21d8
|
||||
F tool/mksqlite3c.tcl e72c0c97fe1a105fa9616483e652949be2199fe6
|
||||
F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
|
||||
F tool/mksqlite3internalh.tcl b6514145a7d5321b47e64e19b8116cc44f973eb1
|
||||
F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105
|
||||
@ -1188,7 +1193,8 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 839c7996eecd5480152c514555b9aa1121a69ce0
|
||||
R aa6b1a81952b002633810b3bb14c199e
|
||||
P b0f6b91f36b503d8ba8d5257bb194f8c1afb4833 33fa0410499900dd8beb44b9a8ffbd9f4b68c8d8
|
||||
R 91d41afdb912bf253c979082c1abeeae
|
||||
T +closed 33fa0410499900dd8beb44b9a8ffbd9f4b68c8d8
|
||||
U drh
|
||||
Z 8d5a13ba98607c42fffb4d1d92841eac
|
||||
Z 8ac04e78c2df72246f6a97d0d813c221
|
||||
|
@ -1 +1 @@
|
||||
b0f6b91f36b503d8ba8d5257bb194f8c1afb4833
|
||||
b1c0f0bc1bd8a3477cd7a7ab510f0442ac88b517
|
@ -4653,7 +4653,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
|
||||
if( pIdxKey ){
|
||||
xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
|
||||
pIdxKey->isCorrupt = 0;
|
||||
pIdxKey->errCode = 0;
|
||||
assert( pIdxKey->default_rc==1
|
||||
|| pIdxKey->default_rc==0
|
||||
|| pIdxKey->default_rc==-1
|
||||
@ -4777,7 +4777,10 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
c = xRecordCompare(nCell, pCellKey, pIdxKey, 0);
|
||||
sqlite3_free(pCellKey);
|
||||
}
|
||||
assert( pIdxKey->isCorrupt==0 || c==0 );
|
||||
assert(
|
||||
(pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
|
||||
&& (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
|
||||
);
|
||||
if( c<0 ){
|
||||
lwr = idx+1;
|
||||
}else if( c>0 ){
|
||||
@ -4787,7 +4790,7 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
*pRes = 0;
|
||||
rc = SQLITE_OK;
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
if( pIdxKey->isCorrupt ) rc = SQLITE_CORRUPT;
|
||||
if( pIdxKey->errCode ) rc = SQLITE_CORRUPT;
|
||||
goto moveto_finish;
|
||||
}
|
||||
if( lwr>upr ) break;
|
||||
|
@ -2679,7 +2679,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
|
||||
/* Open the sorter cursor if we are to use one. */
|
||||
iSorter = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nKeyCol, (char*)
|
||||
sqlite3KeyInfoRef(pKey), P4_KEYINFO);
|
||||
|
||||
/* Open the table. Loop through all rows of the table, inserting index
|
||||
@ -3028,7 +3028,7 @@ Index *sqlite3CreateIndex(
|
||||
pParse->checkSchema = 1;
|
||||
goto exit_create_index;
|
||||
}
|
||||
assert( pTab->nCol<=0x7fff && j<=0x7fff );
|
||||
assert( j<=0x7fff );
|
||||
pIndex->aiColumn[i] = (i16)j;
|
||||
if( pListItem->pExpr ){
|
||||
int nColl;
|
||||
|
17
src/main.c
17
src/main.c
@ -2078,6 +2078,7 @@ static const int aHardLimit[] = {
|
||||
SQLITE_MAX_LIKE_PATTERN_LENGTH,
|
||||
SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
|
||||
SQLITE_MAX_TRIGGER_DEPTH,
|
||||
SQLITE_MAX_WORKER_THREADS,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2113,6 +2114,9 @@ static const int aHardLimit[] = {
|
||||
#if SQLITE_MAX_TRIGGER_DEPTH<1
|
||||
# error SQLITE_MAX_TRIGGER_DEPTH must be at least 1
|
||||
#endif
|
||||
#if SQLITE_MAX_WORKER_THREADS<0 || SQLITE_MAX_WORKER_THREADS>50
|
||||
# error SQLITE_MAX_WORKER_THREADS must be between 0 and 50
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@ -2146,7 +2150,8 @@ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
|
||||
SQLITE_MAX_LIKE_PATTERN_LENGTH );
|
||||
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
|
||||
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
|
||||
assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) );
|
||||
assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS );
|
||||
assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) );
|
||||
|
||||
|
||||
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
|
||||
@ -2493,10 +2498,12 @@ static int openDatabase(
|
||||
|
||||
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
|
||||
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
|
||||
db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
|
||||
db->autoCommit = 1;
|
||||
db->nextAutovac = -1;
|
||||
db->szMmap = sqlite3GlobalConfig.szMmap;
|
||||
db->nextPagesize = 0;
|
||||
db->nMaxSorterMmap = 0x7FFFFFFF;
|
||||
db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
|
||||
#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
|
||||
| SQLITE_AutoIndex
|
||||
@ -3361,6 +3368,13 @@ int sqlite3_test_control(int op, ...){
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, nMax); */
|
||||
case SQLITE_TESTCTRL_SORTER_MMAP: {
|
||||
sqlite3 *db = va_arg(ap, sqlite3*);
|
||||
db->nMaxSorterMmap = va_arg(ap, int);
|
||||
break;
|
||||
}
|
||||
|
||||
/* sqlite3_test_control(SQLITE_TESTCTRL_ISINIT);
|
||||
**
|
||||
** Return SQLITE_OK if SQLite has been initialized and SQLITE_ERROR if
|
||||
@ -3370,7 +3384,6 @@ int sqlite3_test_control(int op, ...){
|
||||
if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
va_end(ap);
|
||||
#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
||||
|
11
src/os_win.c
11
src/os_win.c
@ -943,11 +943,7 @@ static struct win_syscall {
|
||||
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
|
||||
DWORD))aSyscall[63].pCurrent)
|
||||
|
||||
#if SQLITE_OS_WINRT
|
||||
{ "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
|
||||
#else
|
||||
{ "WaitForSingleObjectEx", (SYSCALL)0, 0 },
|
||||
#endif
|
||||
|
||||
#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
|
||||
BOOL))aSyscall[64].pCurrent)
|
||||
@ -1290,6 +1286,13 @@ void sqlite3_win32_sleep(DWORD milliseconds){
|
||||
#endif
|
||||
}
|
||||
|
||||
DWORD sqlite3Win32Wait(HANDLE hObject){
|
||||
DWORD rc;
|
||||
while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
|
||||
TRUE))==WAIT_IO_COMPLETION ){}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
|
||||
** or WinCE. Return false (zero) for Win95, Win98, or WinME.
|
||||
|
43
src/pragma.c
43
src/pragma.c
@ -61,14 +61,15 @@
|
||||
#define PragTyp_TABLE_INFO 30
|
||||
#define PragTyp_TEMP_STORE 31
|
||||
#define PragTyp_TEMP_STORE_DIRECTORY 32
|
||||
#define PragTyp_WAL_AUTOCHECKPOINT 33
|
||||
#define PragTyp_WAL_CHECKPOINT 34
|
||||
#define PragTyp_ACTIVATE_EXTENSIONS 35
|
||||
#define PragTyp_HEXKEY 36
|
||||
#define PragTyp_KEY 37
|
||||
#define PragTyp_REKEY 38
|
||||
#define PragTyp_LOCK_STATUS 39
|
||||
#define PragTyp_PARSER_TRACE 40
|
||||
#define PragTyp_THREADS 33
|
||||
#define PragTyp_WAL_AUTOCHECKPOINT 34
|
||||
#define PragTyp_WAL_CHECKPOINT 35
|
||||
#define PragTyp_ACTIVATE_EXTENSIONS 36
|
||||
#define PragTyp_HEXKEY 37
|
||||
#define PragTyp_KEY 38
|
||||
#define PragTyp_REKEY 39
|
||||
#define PragTyp_LOCK_STATUS 40
|
||||
#define PragTyp_PARSER_TRACE 41
|
||||
#define PragFlag_NeedSchema 0x01
|
||||
static const struct sPragmaNames {
|
||||
const char *const zName; /* Name of pragma */
|
||||
@ -418,6 +419,10 @@ static const struct sPragmaNames {
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ 0 },
|
||||
#endif
|
||||
{ /* zName: */ "threads",
|
||||
/* ePragTyp: */ PragTyp_THREADS,
|
||||
/* ePragFlag: */ 0,
|
||||
/* iArg: */ 0 },
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
|
||||
{ /* zName: */ "user_version",
|
||||
/* ePragTyp: */ PragTyp_HEADER_VALUE,
|
||||
@ -465,7 +470,7 @@ static const struct sPragmaNames {
|
||||
/* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
|
||||
#endif
|
||||
};
|
||||
/* Number of pragmas: 56 on by default, 69 total. */
|
||||
/* Number of pragmas: 57 on by default, 70 total. */
|
||||
/* End of the automatically generated pragma table.
|
||||
***************************************************************************/
|
||||
|
||||
@ -2273,6 +2278,26 @@ void sqlite3Pragma(
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** PRAGMA threads
|
||||
** PRAGMA threads = N
|
||||
**
|
||||
** Configure the maximum number of worker threads. Return the new
|
||||
** maximum, which might be less than requested.
|
||||
*/
|
||||
case PragTyp_THREADS: {
|
||||
sqlite3_int64 N;
|
||||
if( zRight
|
||||
&& sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK
|
||||
&& N>=0
|
||||
){
|
||||
sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
|
||||
}
|
||||
returnSingleInt(pParse, "threads",
|
||||
sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
/*
|
||||
** Report the current state of file logs for all databases
|
||||
|
146
src/select.c
146
src/select.c
@ -455,28 +455,43 @@ static KeyInfo *keyInfoFromExprList(
|
||||
);
|
||||
|
||||
/*
|
||||
** Insert code into "v" that will push the record in register regData
|
||||
** into the sorter.
|
||||
** Generate code that will push the record in registers regData
|
||||
** through regData+nData-1 onto the sorter.
|
||||
*/
|
||||
static void pushOntoSorter(
|
||||
Parse *pParse, /* Parser context */
|
||||
SortCtx *pSort, /* Information about the ORDER BY clause */
|
||||
Select *pSelect, /* The whole SELECT statement */
|
||||
int regData /* Register holding data to be sorted */
|
||||
int regData, /* First register holding data to be sorted */
|
||||
int nData, /* Number of elements in the data array */
|
||||
int nPrefixReg /* No. of reg prior to regData available for use */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int nExpr = pSort->pOrderBy->nExpr;
|
||||
int regRecord = ++pParse->nMem;
|
||||
int regBase = pParse->nMem+1;
|
||||
int nOBSat = pSort->nOBSat;
|
||||
int op;
|
||||
Vdbe *v = pParse->pVdbe; /* Stmt under construction */
|
||||
int bSeq = ((pSort->sortFlags & SORTFLAG_UseSorter)==0);
|
||||
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
|
||||
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
|
||||
int regBase; /* Regs for sorter record */
|
||||
int regRecord = ++pParse->nMem; /* Assembled sorter record */
|
||||
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
|
||||
int op; /* Opcode to add sorter record to sorter */
|
||||
|
||||
pParse->nMem += nExpr+2; /* nExpr+2 registers allocated at regBase */
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nExpr+2-nOBSat,regRecord);
|
||||
assert( bSeq==0 || bSeq==1 );
|
||||
if( nPrefixReg ){
|
||||
assert( nPrefixReg==nExpr+bSeq );
|
||||
regBase = regData - nExpr - bSeq;
|
||||
}else{
|
||||
regBase = pParse->nMem + 1;
|
||||
pParse->nMem += nBase;
|
||||
}
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
|
||||
if( bSeq ){
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
||||
}
|
||||
if( nPrefixReg==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Move, regData, regBase+nExpr+bSeq, nData);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
|
||||
if( nOBSat>0 ){
|
||||
int regPrevKey; /* The first nOBSat columns of the previous row */
|
||||
int addrFirst; /* Address of the OP_IfNot opcode */
|
||||
@ -487,12 +502,17 @@ static void pushOntoSorter(
|
||||
|
||||
regPrevKey = pParse->nMem+1;
|
||||
pParse->nMem += pSort->nOBSat;
|
||||
nKey = nExpr - pSort->nOBSat + 1;
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr); VdbeCoverage(v);
|
||||
nKey = nExpr - pSort->nOBSat + bSeq;
|
||||
if( bSeq ){
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
|
||||
}else{
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_SequenceTest, pSort->iECursor);
|
||||
}
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
|
||||
pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
|
||||
if( pParse->db->mallocFailed ) return;
|
||||
pOp->p2 = nKey + 1;
|
||||
pOp->p2 = nKey + nData;
|
||||
pKI = pOp->p4.pKeyInfo;
|
||||
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||
@ -626,6 +646,7 @@ static void selectInnerLoop(
|
||||
int eDest = pDest->eDest; /* How to dispose of results */
|
||||
int iParm = pDest->iSDParm; /* First argument to disposal method */
|
||||
int nResultCol; /* Number of result columns */
|
||||
int nPrefixReg = 0; /* Number of extra registers before regResult */
|
||||
|
||||
assert( v );
|
||||
assert( pEList!=0 );
|
||||
@ -641,6 +662,11 @@ static void selectInnerLoop(
|
||||
nResultCol = pEList->nExpr;
|
||||
|
||||
if( pDest->iSdst==0 ){
|
||||
if( pSort ){
|
||||
nPrefixReg = pSort->pOrderBy->nExpr;
|
||||
if( !(pSort->sortFlags & SORTFLAG_UseSorter) ) nPrefixReg++;
|
||||
pParse->nMem += nPrefixReg;
|
||||
}
|
||||
pDest->iSdst = pParse->nMem+1;
|
||||
pParse->nMem += nResultCol;
|
||||
}else if( pDest->iSdst+nResultCol > pParse->nMem ){
|
||||
@ -757,10 +783,10 @@ static void selectInnerLoop(
|
||||
case SRT_DistFifo:
|
||||
case SRT_Table:
|
||||
case SRT_EphemTab: {
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
|
||||
testcase( eDest==SRT_Table );
|
||||
testcase( eDest==SRT_EphemTab );
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
|
||||
#ifndef SQLITE_OMIT_CTE
|
||||
if( eDest==SRT_DistFifo ){
|
||||
/* If the destination is DistFifo, then cursor (iParm+1) is open
|
||||
@ -775,7 +801,7 @@ static void selectInnerLoop(
|
||||
}
|
||||
#endif
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg);
|
||||
}else{
|
||||
int r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
|
||||
@ -783,7 +809,7 @@ static void selectInnerLoop(
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -801,7 +827,7 @@ static void selectInnerLoop(
|
||||
** ORDER BY in this case since the order of entries in the set
|
||||
** does not matter. But there might be a LIMIT clause, in which
|
||||
** case the order does matter */
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
||||
@ -827,7 +853,7 @@ static void selectInnerLoop(
|
||||
case SRT_Mem: {
|
||||
assert( nResultCol==1 );
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
|
||||
}else{
|
||||
assert( regResult==iParm );
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
@ -841,10 +867,7 @@ static void selectInnerLoop(
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
if( pSort ){
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg);
|
||||
}else if( eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
||||
}else{
|
||||
@ -1124,46 +1147,62 @@ static void generateSortTail(
|
||||
int addr;
|
||||
int addrOnce = 0;
|
||||
int iTab;
|
||||
int pseudoTab = 0;
|
||||
ExprList *pOrderBy = pSort->pOrderBy;
|
||||
int eDest = pDest->eDest;
|
||||
int iParm = pDest->iSDParm;
|
||||
int regRow;
|
||||
int regRowid;
|
||||
int nKey;
|
||||
int iSortTab; /* Sorter cursor to read from */
|
||||
int nSortData; /* Trailing values to read from sorter */
|
||||
u8 p5; /* p5 parameter for 1st OP_Column */
|
||||
int i;
|
||||
int bSeq; /* True if sorter record includes seq. no. */
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
struct ExprList_item *aOutEx = p->pEList->a;
|
||||
#endif
|
||||
|
||||
if( pSort->labelBkOut ){
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
|
||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
iTab = pSort->iECursor;
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
pseudoTab = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn);
|
||||
regRowid = 0;
|
||||
regRow = pDest->iSdst;
|
||||
nSortData = nColumn;
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
nSortData = 1;
|
||||
}
|
||||
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
int regSortOut = ++pParse->nMem;
|
||||
int ptab2 = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, nKey+2);
|
||||
iSortTab = pParse->nTab++;
|
||||
if( pSort->labelBkOut ){
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
||||
VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ptab2, nKey+1, regRow);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
p5 = OPFLAG_CLEARCACHE;
|
||||
bSeq = 0;
|
||||
}else{
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, nKey+1, regRow);
|
||||
iSortTab = iTab;
|
||||
p5 = 0;
|
||||
bSeq = 1;
|
||||
}
|
||||
for(i=0; i<nSortData; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
|
||||
if( i==0 ) sqlite3VdbeChangeP5(v, p5);
|
||||
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Table:
|
||||
@ -1192,17 +1231,9 @@ static void generateSortTail(
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
int i;
|
||||
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
for(i=0; i<nColumn; i++){
|
||||
assert( regRow!=pDest->iSdst+i );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iSdst+i);
|
||||
if( i==0 ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
}
|
||||
}
|
||||
if( eDest==SRT_Output ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
|
||||
sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
|
||||
@ -1212,9 +1243,10 @@ static void generateSortTail(
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
|
||||
if( regRowid ){
|
||||
sqlite3ReleaseTempReg(pParse, regRow);
|
||||
sqlite3ReleaseTempReg(pParse, regRowid);
|
||||
}
|
||||
/* The bottom of the loop
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrContinue);
|
||||
@ -4755,8 +4787,9 @@ int sqlite3Select(
|
||||
sSort.iECursor = pParse->nTab++;
|
||||
sSort.addrSortIndex =
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
sSort.iECursor, sSort.pOrderBy->nExpr+2, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO
|
||||
);
|
||||
}else{
|
||||
sSort.addrSortIndex = -1;
|
||||
}
|
||||
@ -4887,7 +4920,7 @@ int sqlite3Select(
|
||||
sNC.pSrcList = pTabList;
|
||||
sNC.pAggInfo = &sAggInfo;
|
||||
sAggInfo.mnReg = pParse->nMem+1;
|
||||
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
|
||||
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
|
||||
sAggInfo.pGroupBy = pGroupBy;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pEList);
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
|
||||
@ -4980,8 +5013,8 @@ int sqlite3Select(
|
||||
|
||||
groupBySort = 1;
|
||||
nGroupBy = pGroupBy->nExpr;
|
||||
nCol = nGroupBy + 1;
|
||||
j = nGroupBy+1;
|
||||
nCol = nGroupBy;
|
||||
j = nGroupBy;
|
||||
for(i=0; i<sAggInfo.nColumn; i++){
|
||||
if( sAggInfo.aCol[i].iSorterColumn>=j ){
|
||||
nCol++;
|
||||
@ -4991,8 +5024,7 @@ int sqlite3Select(
|
||||
regBase = sqlite3GetTempRange(pParse, nCol);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
|
||||
j = nGroupBy+1;
|
||||
j = nGroupBy;
|
||||
for(i=0; i<sAggInfo.nColumn; i++){
|
||||
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
|
||||
if( pCol->iSorterColumn>=j ){
|
||||
|
@ -3073,6 +3073,10 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
**
|
||||
** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
|
||||
** <dd>The maximum depth of recursion for triggers.</dd>)^
|
||||
**
|
||||
** [[SQLITE_LIMIT_WORKER_THREADS]] ^(<dt>SQLITE_LIMIT_WORKER_THREADS</dt>
|
||||
** <dd>The maximum number of separate worker threads that a single
|
||||
** [database connection] may start to help it with a computation.</dd>)^
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_LIMIT_LENGTH 0
|
||||
@ -3086,6 +3090,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
|
||||
#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
|
||||
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
|
||||
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
|
||||
#define SQLITE_LIMIT_WORKER_THREADS 11
|
||||
|
||||
/*
|
||||
** CAPI3REF: Compiling An SQL Statement
|
||||
@ -6160,7 +6165,8 @@ int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
|
||||
#define SQLITE_TESTCTRL_BYTEORDER 22
|
||||
#define SQLITE_TESTCTRL_ISINIT 23
|
||||
#define SQLITE_TESTCTRL_LAST 23
|
||||
#define SQLITE_TESTCTRL_SORTER_MMAP 24
|
||||
#define SQLITE_TESTCTRL_LAST 24
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
|
@ -434,6 +434,27 @@
|
||||
# define SQLITE_TEMP_STORE_xc 1 /* Exclude from ctime.c */
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if
|
||||
** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it
|
||||
** to zero.
|
||||
*/
|
||||
#if SQLITE_TEMP_STORE==3 || SQLITE_THREADSAFE==0
|
||||
# undef SQLITE_MAX_WORKER_THREADS
|
||||
# define SQLITE_MAX_WORKER_THREADS 0
|
||||
#endif
|
||||
#ifndef SQLITE_MAX_WORKER_THREADS
|
||||
# define SQLITE_MAX_WORKER_THREADS 8
|
||||
#endif
|
||||
#ifndef SQLITE_DEFAULT_WORKER_THREADS
|
||||
# define SQLITE_DEFAULT_WORKER_THREADS 0
|
||||
#endif
|
||||
#if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS
|
||||
# undef SQLITE_MAX_WORKER_THREADS
|
||||
# define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** GCC does not define the offsetof() macro so we'll have to do it
|
||||
** ourselves.
|
||||
@ -817,6 +838,7 @@ typedef struct PrintfArguments PrintfArguments;
|
||||
typedef struct RowSet RowSet;
|
||||
typedef struct Savepoint Savepoint;
|
||||
typedef struct Select Select;
|
||||
typedef struct SQLiteThread SQLiteThread;
|
||||
typedef struct SelectDest SelectDest;
|
||||
typedef struct SrcList SrcList;
|
||||
typedef struct StrAccum StrAccum;
|
||||
@ -919,7 +941,7 @@ struct Schema {
|
||||
** The number of different kinds of things that can be limited
|
||||
** using the sqlite3_limit() interface.
|
||||
*/
|
||||
#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1)
|
||||
#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1)
|
||||
|
||||
/*
|
||||
** Lookaside malloc is a set of fixed-size buffers that can be used
|
||||
@ -996,6 +1018,7 @@ struct sqlite3 {
|
||||
int nChange; /* Value returned by sqlite3_changes() */
|
||||
int nTotalChange; /* Value returned by sqlite3_total_changes() */
|
||||
int aLimit[SQLITE_N_LIMIT]; /* Limits */
|
||||
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
|
||||
struct sqlite3InitInfo { /* Information used during initialization */
|
||||
int newTnum; /* Rootpage of table being initialized */
|
||||
u8 iDb; /* Which db file is being initialized */
|
||||
@ -1659,7 +1682,7 @@ struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
i8 default_rc; /* Comparison result if keys are equal */
|
||||
u8 isCorrupt; /* Corruption detected by xRecordCompare() */
|
||||
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
|
||||
Mem *aMem; /* Values */
|
||||
int r1; /* Value to return if (lhs > rhs) */
|
||||
int r2; /* Value to return if (rhs < lhs) */
|
||||
@ -3703,4 +3726,12 @@ SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
|
||||
#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
|
||||
#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */
|
||||
|
||||
/*
|
||||
** Threading interface
|
||||
*/
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
|
||||
int sqlite3ThreadJoin(SQLiteThread*, void**);
|
||||
#endif
|
||||
|
||||
#endif /* _SQLITEINT_H_ */
|
||||
|
164
src/test1.c
164
src/test1.c
@ -2717,6 +2717,46 @@ bad_args:
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: add_test_utf16bin_collate <db ptr>
|
||||
**
|
||||
** Add a utf-16 collation sequence named "utf16bin" to the database
|
||||
** handle. This collation sequence compares arguments in the same way as the
|
||||
** built-in collation "binary".
|
||||
*/
|
||||
static int test_utf16bin_collate_func(
|
||||
void *pCtx,
|
||||
int nA, const void *zA,
|
||||
int nB, const void *zB
|
||||
){
|
||||
int nCmp = (nA>nB ? nB : nA);
|
||||
int res = memcmp(zA, zB, nCmp);
|
||||
if( res==0 ) res = nA - nB;
|
||||
return res;
|
||||
}
|
||||
static int test_utf16bin_collate(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ) goto bad_args;
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
|
||||
rc = sqlite3_create_collation(db, "utf16bin", SQLITE_UTF16, 0,
|
||||
test_utf16bin_collate_func
|
||||
);
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
return TCL_OK;
|
||||
|
||||
bad_args:
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
** When the collation needed callback is invoked, record the name of
|
||||
** the requested collating function here. The recorded name is linked
|
||||
@ -5895,6 +5935,7 @@ static int test_test_control(
|
||||
int i;
|
||||
} aVerb[] = {
|
||||
{ "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT },
|
||||
{ "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP },
|
||||
};
|
||||
int iVerb;
|
||||
int iFlag;
|
||||
@ -5922,6 +5963,19 @@ static int test_test_control(
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, val);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_TESTCTRL_SORTER_MMAP: {
|
||||
int val;
|
||||
sqlite3 *db;
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "DB LIMIT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR;
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR;
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
@ -6335,6 +6389,113 @@ static int tclLoadStaticExtensionCmd(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sorter_test_fakeheap BOOL
|
||||
**
|
||||
*/
|
||||
static int sorter_test_fakeheap(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int bArg;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "BOOL");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[1], &bArg) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( bArg ){
|
||||
if( sqlite3GlobalConfig.pHeap==0 ){
|
||||
sqlite3GlobalConfig.pHeap = SQLITE_INT_TO_PTR(-1);
|
||||
}
|
||||
}else{
|
||||
if( sqlite3GlobalConfig.pHeap==SQLITE_INT_TO_PTR(-1) ){
|
||||
sqlite3GlobalConfig.pHeap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Tcl_ResetResult(interp);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sorter_test_sort4_helper DB SQL1 NSTEP SQL2
|
||||
**
|
||||
** Compile SQL statement $SQL1 and step it $NSTEP times. For each row,
|
||||
** check that the leftmost and rightmost columns returned are both integers,
|
||||
** and that both contain the same value.
|
||||
**
|
||||
** Then execute statement $SQL2. Check that the statement returns the same
|
||||
** set of integers in the same order as in the previous step (using $SQL1).
|
||||
*/
|
||||
static int sorter_test_sort4_helper(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zSql1;
|
||||
const char *zSql2;
|
||||
int nStep;
|
||||
int iStep;
|
||||
int iCksum1 = 0;
|
||||
int iCksum2 = 0;
|
||||
int rc;
|
||||
int iB;
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
if( objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB SQL1 NSTEP SQL2");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zSql1 = Tcl_GetString(objv[2]);
|
||||
if( Tcl_GetIntFromObj(interp, objv[3], &nStep) ) return TCL_ERROR;
|
||||
zSql2 = Tcl_GetString(objv[4]);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql1, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
iB = sqlite3_column_count(pStmt)-1;
|
||||
for(iStep=0; iStep<nStep && SQLITE_ROW==sqlite3_step(pStmt); iStep++){
|
||||
int a = sqlite3_column_int(pStmt, 0);
|
||||
if( a!=sqlite3_column_int(pStmt, iB) ){
|
||||
Tcl_AppendResult(interp, "data error: (a!=b)", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
iCksum1 += (iCksum1 << 3) + a;
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql2, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
for(iStep=0; SQLITE_ROW==sqlite3_step(pStmt); iStep++){
|
||||
int a = sqlite3_column_int(pStmt, 0);
|
||||
iCksum2 += (iCksum2 << 3) + a;
|
||||
}
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_OK ) goto sql_error;
|
||||
|
||||
if( iCksum1!=iCksum2 ){
|
||||
Tcl_AppendResult(interp, "checksum mismatch", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
sql_error:
|
||||
Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
@ -6537,6 +6698,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "add_test_collate", test_collate, 0 },
|
||||
{ "add_test_collate_needed", test_collate_needed, 0 },
|
||||
{ "add_test_function", test_function, 0 },
|
||||
{ "add_test_utf16bin_collate", test_utf16bin_collate, 0 },
|
||||
#endif
|
||||
{ "sqlite3_test_errstr", test_errstr, 0 },
|
||||
{ "tcl_variable_type", tcl_variable_type, 0 },
|
||||
@ -6570,6 +6732,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "getrusage", test_getrusage },
|
||||
#endif
|
||||
{ "load_static_extension", tclLoadStaticExtensionCmd },
|
||||
{ "sorter_test_fakeheap", sorter_test_fakeheap },
|
||||
{ "sorter_test_sort4_helper", sorter_test_sort4_helper },
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
int i;
|
||||
|
@ -103,6 +103,10 @@ static void set_options(Tcl_Interp *interp){
|
||||
Tcl_SetVar2(interp, "sqlite_options", "mmap", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
Tcl_SetVar2(interp, "sqlite_options", "worker_threads",
|
||||
STRINGVALUE(SQLITE_MAX_WORKER_THREADS), TCL_GLOBAL_ONLY
|
||||
);
|
||||
|
||||
#if 1 /* def SQLITE_MEMDEBUG */
|
||||
Tcl_SetVar2(interp, "sqlite_options", "memdebug", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
@ -1253,6 +1253,7 @@ static int test_config_cis(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_dump_memsys3 FILENAME
|
||||
** sqlite3_dump_memsys5 FILENAME
|
||||
|
262
src/threads.c
Normal file
262
src/threads.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
** 2012 July 21
|
||||
**
|
||||
** 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 presents a simple cross-platform threading interface for
|
||||
** use internally by SQLite.
|
||||
**
|
||||
** A "thread" can be created using sqlite3ThreadCreate(). This thread
|
||||
** runs independently of its creator until it is joined using
|
||||
** sqlite3ThreadJoin(), at which point it terminates.
|
||||
**
|
||||
** Threads do not have to be real. It could be that the work of the
|
||||
** "thread" is done by the main thread at either the sqlite3ThreadCreate()
|
||||
** or sqlite3ThreadJoin() call. This is, in fact, what happens in
|
||||
** single threaded systems. Nothing in SQLite requires multiple threads.
|
||||
** This interface exists so that applications that want to take advantage
|
||||
** of multiple cores can do so, while also allowing applications to stay
|
||||
** single-threaded if desired.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
#if SQLITE_MAX_WORKER_THREADS>0
|
||||
|
||||
/********************************* Unix Pthreads ****************************/
|
||||
#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
|
||||
|
||||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||||
#include <pthread.h>
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
pthread_t tid; /* Thread ID */
|
||||
int done; /* Set to true when thread finishes */
|
||||
void *pOut; /* Result returned by the thread */
|
||||
void *(*xTask)(void*); /* The thread routine */
|
||||
void *pIn; /* Argument to the thread */
|
||||
};
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
int rc;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
/* This routine is never used in single-threaded mode */
|
||||
assert( sqlite3GlobalConfig.bCoreMutex!=0 );
|
||||
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
if( sqlite3FaultSim(200) ){
|
||||
rc = 1;
|
||||
}else{
|
||||
rc = pthread_create(&p->tid, 0, xTask, pIn);
|
||||
}
|
||||
if( rc ){
|
||||
p->done = 1;
|
||||
p->pOut = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
int rc;
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( NEVER(p==0) ) return SQLITE_NOMEM;
|
||||
if( p->done ){
|
||||
*ppOut = p->pOut;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
|
||||
/******************************** End Unix Pthreads *************************/
|
||||
|
||||
|
||||
/********************************* Win32 Threads ****************************/
|
||||
#if SQLITE_OS_WIN && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
|
||||
|
||||
#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
|
||||
#include <process.h>
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
uintptr_t tid; /* The thread handle */
|
||||
unsigned id; /* The thread identifier */
|
||||
void *(*xTask)(void*); /* The routine to run as a thread */
|
||||
void *pIn; /* Argument to xTask */
|
||||
void *pResult; /* Result of xTask */
|
||||
};
|
||||
|
||||
/* Thread procedure Win32 compatibility shim */
|
||||
static unsigned __stdcall sqlite3ThreadProc(
|
||||
void *pArg /* IN: Pointer to the SQLiteThread structure */
|
||||
){
|
||||
SQLiteThread *p = (SQLiteThread *)pArg;
|
||||
|
||||
assert( p!=0 );
|
||||
#if 0
|
||||
/*
|
||||
** This assert appears to trigger spuriously on certain
|
||||
** versions of Windows, possibly due to _beginthreadex()
|
||||
** and/or CreateThread() not fully setting their thread
|
||||
** ID parameter before starting the thread.
|
||||
*/
|
||||
assert( p->id==GetCurrentThreadId() );
|
||||
#endif
|
||||
assert( p->xTask!=0 );
|
||||
p->pResult = p->xTask(p->pIn);
|
||||
|
||||
_endthreadex(0);
|
||||
return 0; /* NOT REACHED */
|
||||
}
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( sqlite3GlobalConfig.bCoreMutex==0 ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
}else{
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
p->tid = _beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
|
||||
if( p->tid==0 ){
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
}
|
||||
if( p->xTask==0 ){
|
||||
p->id = GetCurrentThreadId();
|
||||
p->pResult = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
DWORD sqlite3Win32Wait(HANDLE hObject); /* os_win.c */
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
DWORD rc;
|
||||
BOOL bRc;
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( NEVER(p==0) ) return SQLITE_NOMEM;
|
||||
if( p->xTask==0 ){
|
||||
assert( p->id==GetCurrentThreadId() );
|
||||
rc = WAIT_OBJECT_0;
|
||||
assert( p->tid==0 );
|
||||
}else{
|
||||
assert( p->id!=0 && p->id!=GetCurrentThreadId() );
|
||||
rc = sqlite3Win32Wait((HANDLE)p->tid);
|
||||
assert( rc!=WAIT_IO_COMPLETION );
|
||||
bRc = CloseHandle((HANDLE)p->tid);
|
||||
assert( bRc );
|
||||
}
|
||||
if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
|
||||
sqlite3_free(p);
|
||||
return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINRT */
|
||||
/******************************** End Win32 Threads *************************/
|
||||
|
||||
|
||||
/********************************* Single-Threaded **************************/
|
||||
#ifndef SQLITE_THREADS_IMPLEMENTED
|
||||
/*
|
||||
** This implementation does not actually create a new thread. It does the
|
||||
** work of the thread in the main thread, when either the thread is created
|
||||
** or when it is joined
|
||||
*/
|
||||
|
||||
/* A running thread */
|
||||
struct SQLiteThread {
|
||||
void *(*xTask)(void*); /* The routine to run as a thread */
|
||||
void *pIn; /* Argument to xTask */
|
||||
void *pResult; /* Result of xTask */
|
||||
};
|
||||
|
||||
/* Create a new thread */
|
||||
int sqlite3ThreadCreate(
|
||||
SQLiteThread **ppThread, /* OUT: Write the thread object here */
|
||||
void *(*xTask)(void*), /* Routine to run in a separate thread */
|
||||
void *pIn /* Argument passed into xTask() */
|
||||
){
|
||||
SQLiteThread *p;
|
||||
|
||||
assert( ppThread!=0 );
|
||||
assert( xTask!=0 );
|
||||
*ppThread = 0;
|
||||
p = sqlite3Malloc(sizeof(*p));
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
|
||||
p->xTask = xTask;
|
||||
p->pIn = pIn;
|
||||
}else{
|
||||
p->xTask = 0;
|
||||
p->pResult = xTask(pIn);
|
||||
}
|
||||
*ppThread = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Get the results of the thread */
|
||||
int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
|
||||
|
||||
assert( ppOut!=0 );
|
||||
if( NEVER(p==0) ) return SQLITE_NOMEM;
|
||||
if( p->xTask ){
|
||||
*ppOut = p->xTask(p->pIn);
|
||||
}else{
|
||||
*ppOut = p->pResult;
|
||||
}
|
||||
sqlite3_free(p);
|
||||
|
||||
#if defined(SQLITE_TEST)
|
||||
{
|
||||
void *pTstAlloc = sqlite3Malloc(10);
|
||||
if (!pTstAlloc) return SQLITE_NOMEM;
|
||||
sqlite3_free(pTstAlloc);
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */
|
||||
/****************************** End Single-Threaded *************************/
|
||||
#endif /* SQLITE_MAX_WORKER_THREADS>0 */
|
33
src/vdbe.c
33
src/vdbe.c
@ -1165,7 +1165,7 @@ case OP_Move: {
|
||||
assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
|
||||
assert( memIsValid(pIn1) );
|
||||
memAboutToChange(p, pOut);
|
||||
VdbeMemReleaseExtern(pOut);
|
||||
sqlite3VdbeMemRelease(pOut);
|
||||
zMalloc = pOut->zMalloc;
|
||||
memcpy(pOut, pIn1, sizeof(Mem));
|
||||
#ifdef SQLITE_DEBUG
|
||||
@ -3362,11 +3362,15 @@ case OP_OpenEphemeral: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SorterOpen P1 P2 * P4 *
|
||||
/* Opcode: SorterOpen P1 P2 P3 P4 *
|
||||
**
|
||||
** This opcode works like OP_OpenEphemeral except that it opens
|
||||
** a transient index that is specifically designed to sort large
|
||||
** tables using an external merge-sort algorithm.
|
||||
**
|
||||
** If argument P3 is non-zero, then it indicates that the sorter may
|
||||
** assume that a stable sort considering the first P3 fields of each
|
||||
** key is sufficient to produce the required results.
|
||||
*/
|
||||
case OP_SorterOpen: {
|
||||
VdbeCursor *pCx;
|
||||
@ -3378,7 +3382,25 @@ case OP_SorterOpen: {
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pCx->pKeyInfo->db==db );
|
||||
assert( pCx->pKeyInfo->enc==ENC(db) );
|
||||
rc = sqlite3VdbeSorterInit(db, pCx);
|
||||
rc = sqlite3VdbeSorterInit(db, pOp->p3, pCx);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SequenceTest P1 P2 * * *
|
||||
** Synopsis: if( cursor[P1].ctr++ ) pc = P2
|
||||
**
|
||||
** P1 is a sorter cursor. If the sequence counter is currently zero, jump
|
||||
** to P2. Regardless of whether or not the jump is taken, increment the
|
||||
** the sequence value.
|
||||
*/
|
||||
case OP_SequenceTest: {
|
||||
VdbeCursor *pC;
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC->pSorter );
|
||||
if( (pC->seqCount++)==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4227,6 +4249,7 @@ case OP_SorterCompare: {
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
nKeyCol = pOp->p4.i;
|
||||
res = 0;
|
||||
rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res);
|
||||
VdbeBranchTaken(res!=0,2);
|
||||
if( res ){
|
||||
@ -4491,7 +4514,7 @@ case OP_Rewind: { /* jump */
|
||||
pC->seekOp = OP_Rewind;
|
||||
#endif
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterRewind(db, pC, &res);
|
||||
rc = sqlite3VdbeSorterRewind(pC, &res);
|
||||
}else{
|
||||
pCrsr = pC->pCursor;
|
||||
assert( pCrsr );
|
||||
@ -4669,7 +4692,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
rc = ExpandBlob(pIn2);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( isSorter(pC) ){
|
||||
rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
|
||||
rc = sqlite3VdbeSorterWrite(pC, pIn2);
|
||||
}else{
|
||||
nKey = pIn2->n;
|
||||
zKey = pIn2->z;
|
||||
|
@ -441,13 +441,13 @@ void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
int sqlite3VdbeTransferError(Vdbe *p);
|
||||
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
|
||||
void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
|
||||
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
|
||||
|
@ -3185,10 +3185,14 @@ void sqlite3VdbeRecordUnpack(
|
||||
** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
|
||||
** in assert() statements to ensure that the optimized code in
|
||||
** sqlite3VdbeRecordCompare() returns results with these two primitives.
|
||||
**
|
||||
** Return true if the result of comparison is equivalent to desiredResult.
|
||||
** Return false if there is a disagreement.
|
||||
*/
|
||||
static int vdbeRecordCompareDebug(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
const UnpackedRecord *pPKey2 /* Right key */
|
||||
const UnpackedRecord *pPKey2, /* Right key */
|
||||
int desiredResult /* Correct answer */
|
||||
){
|
||||
u32 d1; /* Offset into aKey[] of next data element */
|
||||
u32 idx1; /* Offset into aKey[] of next header element */
|
||||
@ -3200,6 +3204,7 @@ static int vdbeRecordCompareDebug(
|
||||
Mem mem1;
|
||||
|
||||
pKeyInfo = pPKey2->pKeyInfo;
|
||||
if( pKeyInfo->db==0 ) return 1;
|
||||
mem1.enc = pKeyInfo->enc;
|
||||
mem1.db = pKeyInfo->db;
|
||||
/* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */
|
||||
@ -3250,7 +3255,7 @@ static int vdbeRecordCompareDebug(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc; /* Invert the result for DESC sort order. */
|
||||
}
|
||||
return rc;
|
||||
goto debugCompareEnd;
|
||||
}
|
||||
i++;
|
||||
}while( idx1<szHdr1 && i<pPKey2->nField );
|
||||
@ -3264,7 +3269,15 @@ static int vdbeRecordCompareDebug(
|
||||
/* rc==0 here means that one of the keys ran out of fields and
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
return pPKey2->default_rc;
|
||||
rc = pPKey2->default_rc;
|
||||
|
||||
debugCompareEnd:
|
||||
if( desiredResult==0 && rc==0 ) return 1;
|
||||
if( desiredResult<0 && rc<0 ) return 1;
|
||||
if( desiredResult>0 && rc>0 ) return 1;
|
||||
if( CORRUPT_DB ) return 1;
|
||||
if( pKeyInfo->db->mallocFailed ) return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3277,7 +3290,8 @@ static int vdbeRecordCompareDebug(
|
||||
static int vdbeCompareMemString(
|
||||
const Mem *pMem1,
|
||||
const Mem *pMem2,
|
||||
const CollSeq *pColl
|
||||
const CollSeq *pColl,
|
||||
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
|
||||
){
|
||||
if( pMem1->enc==pColl->enc ){
|
||||
/* The strings are already in the correct encoding. Call the
|
||||
@ -3300,6 +3314,7 @@ static int vdbeCompareMemString(
|
||||
rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
|
||||
sqlite3VdbeMemRelease(&c1);
|
||||
sqlite3VdbeMemRelease(&c2);
|
||||
if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@ -3382,7 +3397,7 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
|
||||
assert( !pColl || pColl->xCmp );
|
||||
|
||||
if( pColl ){
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl);
|
||||
return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
|
||||
}
|
||||
/* If a NULL pointer was passed as the collate function, fall through
|
||||
** to the blob case and use memcmp(). */
|
||||
@ -3454,8 +3469,10 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
|
||||
** fields that appear in both keys are equal, then pPKey2->default_rc is
|
||||
** returned.
|
||||
**
|
||||
** If database corruption is discovered, set pPKey2->isCorrupt to non-zero
|
||||
** and return 0.
|
||||
** If database corruption is discovered, set pPKey2->errCode to
|
||||
** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
|
||||
** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
|
||||
** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
|
||||
*/
|
||||
int sqlite3VdbeRecordCompare(
|
||||
int nKey1, const void *pKey1, /* Left key */
|
||||
@ -3486,7 +3503,7 @@ int sqlite3VdbeRecordCompare(
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
d1 = szHdr1;
|
||||
if( d1>(unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}
|
||||
i = 0;
|
||||
@ -3565,14 +3582,16 @@ int sqlite3VdbeRecordCompare(
|
||||
testcase( (d1+mem1.n)==(unsigned)nKey1 );
|
||||
testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
|
||||
if( (d1+mem1.n) > (unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}else if( pKeyInfo->aColl[i] ){
|
||||
mem1.enc = pKeyInfo->enc;
|
||||
mem1.db = pKeyInfo->db;
|
||||
mem1.flags = MEM_Str;
|
||||
mem1.z = (char*)&aKey1[d1];
|
||||
rc = vdbeCompareMemString(&mem1, pRhs, pKeyInfo->aColl[i]);
|
||||
rc = vdbeCompareMemString(
|
||||
&mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode
|
||||
);
|
||||
}else{
|
||||
int nCmp = MIN(mem1.n, pRhs->n);
|
||||
rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
|
||||
@ -3592,7 +3611,7 @@ int sqlite3VdbeRecordCompare(
|
||||
testcase( (d1+nStr)==(unsigned)nKey1 );
|
||||
testcase( (d1+nStr+1)==(unsigned)nKey1 );
|
||||
if( (d1+nStr) > (unsigned)nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}else{
|
||||
int nCmp = MIN(nStr, pRhs->n);
|
||||
@ -3612,11 +3631,7 @@ int sqlite3VdbeRecordCompare(
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc;
|
||||
}
|
||||
assert( CORRUPT_DB
|
||||
|| (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| pKeyInfo->db->mallocFailed
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
|
||||
assert( mem1.zMalloc==0 ); /* See comment below */
|
||||
return rc;
|
||||
}
|
||||
@ -3636,7 +3651,7 @@ int sqlite3VdbeRecordCompare(
|
||||
** all the fields up to that point were equal. Return the the default_rc
|
||||
** value. */
|
||||
assert( CORRUPT_DB
|
||||
|| pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
|
||||
|| vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
|
||||
|| pKeyInfo->db->mallocFailed
|
||||
);
|
||||
return pPKey2->default_rc;
|
||||
@ -3735,11 +3750,7 @@ static int vdbeRecordCompareInt(
|
||||
res = pPKey2->default_rc;
|
||||
}
|
||||
|
||||
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
|| CORRUPT_DB
|
||||
);
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3773,7 +3784,7 @@ static int vdbeRecordCompareString(
|
||||
|
||||
nStr = (serial_type-12) / 2;
|
||||
if( (szHdr + nStr) > nKey1 ){
|
||||
pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
|
||||
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
|
||||
return 0; /* Corruption */
|
||||
}
|
||||
nCmp = MIN( pPKey2->aMem[0].n, nStr );
|
||||
@ -3799,9 +3810,7 @@ static int vdbeRecordCompareString(
|
||||
}
|
||||
}
|
||||
|
||||
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|
||||
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|
||||
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|
||||
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res)
|
||||
|| CORRUPT_DB
|
||||
|| pPKey2->pKeyInfo->db->mallocFailed
|
||||
);
|
||||
|
2574
src/vdbesort.c
2574
src/vdbesort.c
File diff suppressed because it is too large
Load Diff
@ -881,6 +881,48 @@ do_malloc_test 39 -tclprep {
|
||||
db close
|
||||
}
|
||||
|
||||
reset_db
|
||||
add_test_utf16bin_collate db
|
||||
do_execsql_test 40.1 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES('fghij');
|
||||
INSERT INTO t1 VALUES('pqrst');
|
||||
INSERT INTO t1 VALUES('abcde');
|
||||
INSERT INTO t1 VALUES('uvwxy');
|
||||
INSERT INTO t1 VALUES('klmno');
|
||||
}
|
||||
do_execsql_test 40.2 {
|
||||
SELECT * FROM t1 ORDER BY 1 COLLATE utf16bin;
|
||||
} {abcde fghij klmno pqrst uvwxy}
|
||||
do_faultsim_test 40.3 -faults oom-trans* -body {
|
||||
execsql {
|
||||
SELECT * FROM t1 ORDER BY 1 COLLATE utf16bin;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {abcde fghij klmno pqrst uvwxy}}
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
reset_db
|
||||
add_test_utf16bin_collate db
|
||||
set big [string repeat x 200]
|
||||
do_execsql_test 41.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a COLLATE utf16bin);
|
||||
INSERT INTO t1 VALUES('fghij' || $::big);
|
||||
INSERT INTO t1 VALUES('pqrst' || $::big);
|
||||
INSERT INTO t1 VALUES('abcde' || $::big);
|
||||
INSERT INTO t1 VALUES('uvwxy' || $::big);
|
||||
INSERT INTO t1 VALUES('klmno' || $::big);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
}
|
||||
do_faultsim_test 41.2 -faults oom* -body {
|
||||
execsql { SELECT * FROM t1 WHERE a = ('abcde' || $::big)}
|
||||
} -test {
|
||||
faultsim_test_result [list 0 "abcde$::big"]
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
@ -25,7 +25,6 @@ if {!$MEMDEBUG} {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
# Construct a test database
|
||||
#
|
||||
forcedelete test.db.bu
|
||||
@ -116,6 +115,28 @@ ifcapable stat3 {
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 7.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
}
|
||||
do_faultsim_test 7 -faults oom-trans* -prep {
|
||||
if {$iFail < 500} { set iFail 2000 }
|
||||
if {$iFail > 1215} { set iFail 2000 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 1000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
}
|
||||
} -test {
|
||||
set res [list 200 100 200 100 200 100 200 100 200 100]
|
||||
faultsim_test_result [list 0 $res]
|
||||
}
|
||||
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
@ -112,7 +112,7 @@ set allquicktests [test_set $alltests -exclude {
|
||||
incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test
|
||||
vtab_err.test walslow.test walcrash.test walcrash3.test
|
||||
walthread.test rtree3.test indexfault.test securedel2.test
|
||||
fts4growth.test fts4growth2.test
|
||||
sort3.test sort4.test fts4growth.test fts4growth2.test
|
||||
}]
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
@ -355,6 +355,12 @@ test_suite "coverage-analyze" -description {
|
||||
analyze.test analyzeB.test mallocA.test
|
||||
}
|
||||
|
||||
test_suite "coverage-sorter" -description {
|
||||
Coverage tests for file vdbesort.c.
|
||||
} -files {
|
||||
sort.test sortfault.test
|
||||
}
|
||||
|
||||
|
||||
lappend ::testsuitelist xxx
|
||||
#-------------------------------------------------------------------------
|
||||
@ -486,7 +492,7 @@ test_suite "multithread" -description {
|
||||
} -files {
|
||||
delete.test delete2.test insert.test rollback.test select1.test
|
||||
select2.test trans.test update.test vacuum.test types.test
|
||||
types2.test types3.test
|
||||
types2.test types3.test sort4.test
|
||||
} -shutdown {
|
||||
catch {db close}
|
||||
sqlite3_shutdown
|
||||
|
177
test/sort.test
177
test/sort.test
@ -8,10 +8,10 @@
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the CREATE TABLE statement.
|
||||
#
|
||||
# $Id: sort.test,v 1.25 2005/11/14 22:29:06 drh Exp $
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the sorter (code in vdbesort.c).
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -464,4 +464,175 @@ do_test sort-12.1 {
|
||||
}
|
||||
} {1 2 xxx 1 3 yyy 1 1 zzz}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the sorter in vdbesort.c sorts in a stable fashion.
|
||||
#
|
||||
do_execsql_test sort-13.0 {
|
||||
CREATE TABLE t10(a, b);
|
||||
}
|
||||
do_test sort-13.1 {
|
||||
db transaction {
|
||||
for {set i 0} {$i < 100000} {incr i} {
|
||||
execsql { INSERT INTO t10 VALUES( $i/10, $i%10 ) }
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_execsql_test sort-13.2 {
|
||||
SELECT a, b FROM t10 ORDER BY a;
|
||||
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
|
||||
do_execsql_test sort-13.3 {
|
||||
PRAGMA cache_size = 5;
|
||||
SELECT a, b FROM t10 ORDER BY a;
|
||||
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Sort some large ( > 4KiB) records.
|
||||
#
|
||||
proc cksum {x} {
|
||||
set i1 1
|
||||
set i2 2
|
||||
binary scan $x c* L
|
||||
foreach {a b} $L {
|
||||
set i1 [expr (($i2<<3) + $a) & 0x7FFFFFFF]
|
||||
set i2 [expr (($i1<<3) + $b) & 0x7FFFFFFF]
|
||||
}
|
||||
list $i1 $i2
|
||||
}
|
||||
db func cksum cksum
|
||||
|
||||
do_execsql_test sort-14.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
CREATE TABLE t11(a, b);
|
||||
INSERT INTO t11 VALUES(randomblob(5000), NULL);
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --2
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --3
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --4
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --5
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --6
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --7
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --8
|
||||
INSERT INTO t11 SELECT randomblob(5000), NULL FROM t11; --9
|
||||
UPDATE t11 SET b = cksum(a);
|
||||
}
|
||||
|
||||
foreach {tn mmap_limit} {
|
||||
1 0
|
||||
2 1000000
|
||||
} {
|
||||
do_test sort-14.$tn {
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $mmap_limit
|
||||
set prev ""
|
||||
db eval { SELECT * FROM t11 ORDER BY b } {
|
||||
if {$b != [cksum $a]} {error "checksum failed"}
|
||||
if {[string compare $b $prev] < 0} {error "sort failed"}
|
||||
set prev $b
|
||||
}
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn mmap_limit nWorker tmpstore coremutex fakeheap softheaplimit} {
|
||||
1 0 3 file true false 0
|
||||
2 0 3 file true true 0
|
||||
3 0 0 file true false 0
|
||||
4 1000000 3 file true false 0
|
||||
5 0 0 memory false true 0
|
||||
6 0 0 file false true 1000000
|
||||
7 0 0 file false true 10000
|
||||
} {
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
if {$coremutex} {
|
||||
sqlite3_config multithread
|
||||
} else {
|
||||
sqlite3_config singlethread
|
||||
}
|
||||
sqlite3_initialize
|
||||
sorter_test_fakeheap $fakeheap
|
||||
sqlite3_soft_heap_limit $softheaplimit
|
||||
|
||||
reset_db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $mmap_limit
|
||||
execsql "PRAGMA temp_store = $tmpstore; PRAGMA threads = $nWorker"
|
||||
|
||||
|
||||
set ten [string repeat X 10300]
|
||||
set one [string repeat y 200]
|
||||
|
||||
if {$softheaplimit} {
|
||||
execsql { PRAGMA cache_size = 20 };
|
||||
} else {
|
||||
execsql { PRAGMA cache_size = 5 };
|
||||
}
|
||||
|
||||
do_execsql_test 15.$tn.1 {
|
||||
WITH rr AS (
|
||||
SELECT 4, $ten UNION ALL
|
||||
SELECT 2, $one UNION ALL
|
||||
SELECT 1, $ten UNION ALL
|
||||
SELECT 3, $one
|
||||
)
|
||||
SELECT * FROM rr ORDER BY 1;
|
||||
} [list 1 $ten 2 $one 3 $one 4 $ten]
|
||||
|
||||
do_execsql_test 15.$tn.2 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
INSERT INTO t1 VALUES(5);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(6);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_execsql_test 15.$tn.3 {
|
||||
WITH rr AS (
|
||||
SELECT 4, $ten UNION ALL
|
||||
SELECT 2, $one
|
||||
)
|
||||
SELECT * FROM rr ORDER BY 1;
|
||||
} [list 2 $one 4 $ten]
|
||||
|
||||
sorter_test_fakeheap 0
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
set t(0) singlethread
|
||||
set t(1) multithread
|
||||
set t(2) serialized
|
||||
sqlite3_config $t($sqlite_options(threadsafe))
|
||||
sqlite3_initialize
|
||||
sqlite3_soft_heap_limit 0
|
||||
|
||||
reset_db
|
||||
do_catchsql_test 16.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, NULL, 3);
|
||||
INSERT INTO t1 VALUES(NULL, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, NULL);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a, c);
|
||||
} {0 {}}
|
||||
reset_db
|
||||
do_catchsql_test 16.2 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, NULL, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(1, 2, NULL);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a, c);
|
||||
} {1 {UNIQUE constraint failed: t1.b, t1.a, t1.c}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
SELECT * FROM sqlite_master ORDER BY sql;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
80
test/sort2.test
Normal file
80
test/sort2.test
Normal file
@ -0,0 +1,80 @@
|
||||
# 2014 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 SQLite library.
|
||||
#
|
||||
# Specifically, the tests in this file attempt to verify that
|
||||
# multi-threaded sorting works.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort2
|
||||
|
||||
foreach {tn script} {
|
||||
1 { }
|
||||
2 {
|
||||
catch { db close }
|
||||
reset_db
|
||||
catch { db eval {PRAGMA threads=7} }
|
||||
}
|
||||
} {
|
||||
|
||||
eval $script
|
||||
|
||||
do_execsql_test $tn.1 {
|
||||
PRAGMA cache_size = 5;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 100000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
} {
|
||||
20000 100 20000 100 20000 100 20000 100 20000 100
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 10000
|
||||
) INSERT INTO t1 SELECT * FROM r;
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.2 {
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, a);
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.3 {
|
||||
CREATE UNIQUE INDEX i2 ON t1(a);
|
||||
}
|
||||
|
||||
do_execsql_test $tn.2.4 { PRAGMA integrity_check } {ok}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test $tn.3 {
|
||||
PRAGMA cache_size = 5;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(100)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(100) FROM r
|
||||
LIMIT 1000000
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
} {
|
||||
200000 100 200000 100 200000 100 200000 100 200000 100
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
67
test/sort3.test
Normal file
67
test/sort3.test
Normal file
@ -0,0 +1,67 @@
|
||||
# 2014 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 SQLite library.
|
||||
#
|
||||
# The tests in this file verify that sorting works when the library is
|
||||
# configured to use mmap(), but the temporary files generated by the
|
||||
# sorter are too large to be completely mapped.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort3
|
||||
|
||||
# Sort roughly 20MB of data. Once with a mmap limit of 5MB and once without.
|
||||
#
|
||||
foreach {itest limit} {
|
||||
1 5000000
|
||||
2 0x7FFFFFFF
|
||||
} {
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $limit
|
||||
do_execsql_test 1.$itest {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(1000)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(1000) FROM r
|
||||
LIMIT 20000
|
||||
)
|
||||
SELECT count(*), sum(length(y)) FROM r GROUP BY (x%5);
|
||||
} {
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
4000 4000000
|
||||
}
|
||||
}
|
||||
|
||||
# Sort more than 2GB of data. At one point this was causing a problem.
|
||||
# This test might take one minute or more to run.
|
||||
#
|
||||
do_execsql_test 2 {
|
||||
PRAGMA cache_size = 20000;
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, randomblob(1000)
|
||||
UNION ALL
|
||||
SELECT x+1, randomblob(1000) FROM r
|
||||
LIMIT 2200000
|
||||
)
|
||||
SELECT count(*), sum(length(y)) FROM r GROUP BY (x%5);
|
||||
} {
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
440000 440000000
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
189
test/sort4.test
Normal file
189
test/sort4.test
Normal file
@ -0,0 +1,189 @@
|
||||
# 2014 May 6.
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# The tests in this file are brute force tests of the multi-threaded
|
||||
# sorter.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sort4
|
||||
|
||||
# Configure the sorter to use 3 background threads.
|
||||
db eval {PRAGMA threads=3}
|
||||
|
||||
# Minimum number of seconds to run for. If the value is 0, each test
|
||||
# is run exactly once. Otherwise, tests are repeated until the timeout
|
||||
# expires.
|
||||
set SORT4TIMEOUT 0
|
||||
if {[permutation] == "multithread"} { set SORT4TIMEOUT 300 }
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Set up a table "t1" containing $nRow rows. Each row contains also
|
||||
# contains blob fields that collectively contain at least $nPayload
|
||||
# bytes of content. The table schema is as follows:
|
||||
#
|
||||
# CREATE TABLE t1(a INTEGER, <extra-columns>, b INTEGER);
|
||||
#
|
||||
# For each row, the values of columns "a" and "b" are set to the same
|
||||
# pseudo-randomly selected integer. The "extra-columns", of which there
|
||||
# are at most eight, are named c0, c1, c2 etc. Column c0 contains a 4
|
||||
# byte string. Column c1 an 8 byte string. Field c2 16 bytes, and so on.
|
||||
#
|
||||
# This table is intended to be used for testing queries of the form:
|
||||
#
|
||||
# SELECT a, <cols>, b FROM t1 ORDER BY a;
|
||||
#
|
||||
# The test code checks that rows are returned in order, and that the
|
||||
# values of "a" and "b" are the same for each row (the idea being that
|
||||
# if field "b" at the end of the sorter record has not been corrupted,
|
||||
# the rest of the record is probably Ok as well).
|
||||
#
|
||||
proc populate_table {nRow nPayload} {
|
||||
set nCol 0
|
||||
|
||||
set n 0
|
||||
for {set nCol 0} {$n < $nPayload} {incr nCol} {
|
||||
incr n [expr (4 << $nCol)]
|
||||
}
|
||||
|
||||
set cols [lrange [list xxx c0 c1 c2 c3 c4 c5 c6 c7] 1 $nCol]
|
||||
set data [lrange [list xxx \
|
||||
randomblob(4) randomblob(8) randomblob(16) randomblob(32) \
|
||||
randomblob(64) randomblob(128) randomblob(256) randomblob(512) \
|
||||
] 1 $nCol]
|
||||
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
|
||||
db transaction {
|
||||
execsql "CREATE TABLE t1(a, [join $cols ,], b);"
|
||||
set insert "INSERT INTO t1 VALUES(:k, [join $data ,], :k)"
|
||||
for {set i 0} {$i < $nRow} {incr i} {
|
||||
set k [expr int(rand()*1000000000)]
|
||||
execsql $insert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Helper for [do_sorter_test]
|
||||
#
|
||||
proc sorter_test {nRow nRead nPayload} {
|
||||
set res [list]
|
||||
|
||||
set nLoad [expr ($nRow > $nRead) ? $nRead : $nRow]
|
||||
|
||||
set nPayload [expr (($nPayload+3)/4) * 4]
|
||||
set cols [list]
|
||||
foreach {mask col} {
|
||||
0x04 c0 0x08 c1 0x10 c2 0x20 c3
|
||||
0x40 c4 0x80 c5 0x100 c6 0x200 c7
|
||||
} {
|
||||
if {$nPayload & $mask} { lappend cols $col }
|
||||
}
|
||||
|
||||
# Create two SELECT statements. Statement $sql1 uses the sorter to sort
|
||||
# $nRow records of a bit over $nPayload bytes each read from the "t1"
|
||||
# table created by [populate_table] proc above. Rows are sorted in order
|
||||
# of the integer field in each "t1" record.
|
||||
#
|
||||
# The second SQL statement sorts the same set of rows as the first, but
|
||||
# uses a LIMIT clause, causing SQLite to use a temp table instead of the
|
||||
# sorter for sorting.
|
||||
#
|
||||
set sql1 "SELECT a, [join $cols ,], b FROM t1 WHERE rowid<=$nRow ORDER BY a"
|
||||
set sql2 "SELECT a FROM t1 WHERE rowid<=$nRow ORDER BY a LIMIT $nRead"
|
||||
|
||||
# Pass the two SQL statements to a helper command written in C. This
|
||||
# command steps statement $sql1 $nRead times and compares the integer
|
||||
# values in the rows returned with the results of executing $sql2. If
|
||||
# the comparison fails (indicating some bug in the sorter), a Tcl
|
||||
# exception is thrown.
|
||||
#
|
||||
sorter_test_sort4_helper db $sql1 $nRead $sql2
|
||||
set {} {}
|
||||
}
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# do_sorter_test <testname> <args>...
|
||||
#
|
||||
# where <args> are any of the following switches:
|
||||
#
|
||||
# -rows N (number of rows to have sorter sort)
|
||||
# -read N (number of rows to read out of sorter)
|
||||
# -payload N (bytes of payload to read with each row)
|
||||
# -cachesize N (Value for "PRAGMA cache_size = ?")
|
||||
# -repeats N (number of times to repeat test)
|
||||
# -fakeheap BOOL (true to use separate allocations for in-memory records)
|
||||
#
|
||||
proc do_sorter_test {tn args} {
|
||||
set a(-rows) 1000
|
||||
set a(-repeats) 1
|
||||
set a(-read) 100
|
||||
set a(-payload) 100
|
||||
set a(-cachesize) 100
|
||||
set a(-fakeheap) 0
|
||||
|
||||
foreach {s val} $args {
|
||||
if {[info exists a($s)]==0} {
|
||||
unset a(-cachesize)
|
||||
set optlist "[join [array names a] ,] or -cachesize"
|
||||
error "Unknown option $s, expected $optlist"
|
||||
}
|
||||
set a($s) $val
|
||||
}
|
||||
if {[permutation] == "memsys3" || [permutation] == "memsys5"} {
|
||||
set a(-fakeheap) 0
|
||||
}
|
||||
if {$a(-fakeheap)} { sorter_test_fakeheap 1 }
|
||||
|
||||
|
||||
db eval "PRAGMA cache_size = $a(-cachesize)"
|
||||
do_test $tn [subst -nocommands {
|
||||
for {set i 0} {[set i] < $a(-repeats)} {incr i} {
|
||||
sorter_test $a(-rows) $a(-read) $a(-payload)
|
||||
}
|
||||
}] {}
|
||||
|
||||
if {$a(-fakeheap)} { sorter_test_fakeheap 0 }
|
||||
}
|
||||
|
||||
proc clock_seconds {} {
|
||||
db one {SELECT strftime('%s')}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Begin tests here.
|
||||
|
||||
# Create a test database.
|
||||
do_test 1 {
|
||||
execsql "PRAGMA page_size = 4096"
|
||||
populate_table 100000 500
|
||||
} {}
|
||||
|
||||
set iTimeLimit [expr [clock_seconds] + $SORT4TIMEOUT]
|
||||
|
||||
for {set t 2} {1} {incr tn} {
|
||||
do_sorter_test $t.2 -repeats 10 -rows 1000 -read 100
|
||||
do_sorter_test $t.3 -repeats 10 -rows 100000 -read 1000
|
||||
do_sorter_test $t.4 -repeats 10 -rows 100000 -read 1000 -payload 500
|
||||
do_sorter_test $t.5 -repeats 10 -rows 100000 -read 100000 -payload 8
|
||||
do_sorter_test $t.6 -repeats 10 -rows 100000 -read 10 -payload 8
|
||||
do_sorter_test $t.7 -repeats 10 -rows 10000 -read 10000 -payload 8 -fakeheap 1
|
||||
do_sorter_test $t.8 -repeats 10 -rows 100000 -read 10000 -cachesize 250
|
||||
|
||||
set iNow [clock_seconds]
|
||||
if {$iNow>=$iTimeLimit} break
|
||||
do_test "$testprefix-([expr $iTimeLimit-$iNow] seconds remain)" {} {}
|
||||
}
|
||||
|
||||
finish_test
|
165
test/sortfault.test
Normal file
165
test/sortfault.test
Normal file
@ -0,0 +1,165 @@
|
||||
# 2014 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 SQLite library.
|
||||
#
|
||||
# Specifically, it tests the effects of fault injection on the sorter
|
||||
# module (code in vdbesort.c).
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix sortfault
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA cache_size = 5;
|
||||
}
|
||||
|
||||
foreach {tn mmap_limit nWorker tmpstore threadsmode fakeheap lookaside} {
|
||||
1 0 0 file multithread false false
|
||||
2 100000 0 file multithread false false
|
||||
3 100000 1 file multithread false false
|
||||
4 2000000 0 file singlethread false true
|
||||
} {
|
||||
if {$sqlite_options(threadsafe)} { set threadsmode singlethread }
|
||||
|
||||
db eval "PRAGMA threads=$nWorker"
|
||||
sqlite3_config $threadsmode
|
||||
if { $lookaside } {
|
||||
sqlite3_config_lookaside 100 500
|
||||
} else {
|
||||
sqlite3_config_lookaside 0 0
|
||||
}
|
||||
sqlite3_initialize
|
||||
sorter_test_fakeheap $fakeheap
|
||||
|
||||
set str [string repeat a 1000]
|
||||
puts $threadsmode
|
||||
|
||||
do_faultsim_test 1.$tn -prep {
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 1, $::str
|
||||
UNION ALL
|
||||
SELECT x+1, $::str FROM r
|
||||
LIMIT 200
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {40 1000 40 1000 40 1000 40 1000 40 1000}}
|
||||
}
|
||||
|
||||
do_faultsim_test 2.$tn -faults oom* -prep {
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
add_test_utf16bin_collate db
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
} -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 100, $::str
|
||||
UNION ALL
|
||||
SELECT x-1, $::str FROM r
|
||||
LIMIT 100
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY y COLLATE utf16bin, (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {20 1000 20 1000 20 1000 20 1000 20 1000}}
|
||||
}
|
||||
|
||||
if {$mmap_limit > 1000000} {
|
||||
set str2 [string repeat $str 10]
|
||||
|
||||
sqlite3_memdebug_vfs_oom_test 0
|
||||
sqlite3 db test.db
|
||||
sqlite3_test_control SQLITE_TESTCTRL_SORTER_MMAP db $::mmap_limit
|
||||
execsql { PRAGMA cache_size = 5 }
|
||||
|
||||
do_faultsim_test 3.$tn -faults oom-trans* -body {
|
||||
execsql {
|
||||
WITH r(x,y) AS (
|
||||
SELECT 300, $::str2
|
||||
UNION ALL
|
||||
SELECT x-1, $::str2 FROM r
|
||||
LIMIT 300
|
||||
)
|
||||
SELECT count(x), length(y) FROM r GROUP BY y, (x%5)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {60 10000 60 10000 60 10000 60 10000 60 10000}}
|
||||
}
|
||||
|
||||
sqlite3_memdebug_vfs_oom_test 1
|
||||
}
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
sqlite3_shutdown
|
||||
set t(0) singlethread
|
||||
set t(1) multithread
|
||||
set t(2) serialized
|
||||
sqlite3_config $t($sqlite_options(threadsafe))
|
||||
sqlite3_config_lookaside 100 500
|
||||
sqlite3_initialize
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
}
|
||||
do_test 4.1 {
|
||||
for {set i 0} {$i < 256} {incr i} {
|
||||
execsql {
|
||||
INSERT INTO t1 SELECT
|
||||
((a<<3) + b) & 2147483647,
|
||||
((b<<3) + c) & 2147483647,
|
||||
((c<<3) + a) & 2147483647
|
||||
FROM t1 ORDER BY rowid DESC LIMIT 1;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 4.2 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE UNIQUE INDEX i1 ON t1(a,b,c) }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
set a [string repeat a 500]
|
||||
set b [string repeat b 500]
|
||||
set c [string repeat c 500]
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES($a, $b, $c);
|
||||
INSERT INTO t1 VALUES($c, $b, $a);
|
||||
}
|
||||
|
||||
do_faultsim_test 5.1 -faults oom* -body {
|
||||
execsql { SELECT * FROM t1 ORDER BY a }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 [list $::a $::b $::c $::c $::b $::a]]
|
||||
}
|
||||
|
||||
finish_test
|
@ -27,6 +27,7 @@ static const char zHelp[] =
|
||||
" --stats Show statistics at the end\n"
|
||||
" --testset T Run test-set T\n"
|
||||
" --trace Turn on SQL tracing\n"
|
||||
" --threads N Use up to N threads for sorting\n"
|
||||
" --utf16be Set text encoding to UTF-16BE\n"
|
||||
" --utf16le Set text encoding to UTF-16LE\n"
|
||||
" --verify Run additional verification steps.\n"
|
||||
@ -1141,6 +1142,7 @@ int main(int argc, char **argv){
|
||||
int nPCache = 0, szPCache = 0;/* --pcache configuration */
|
||||
int nScratch = 0, szScratch=0;/* --scratch configuration */
|
||||
int showStats = 0; /* True for --stats */
|
||||
int nThread = 0; /* --threads value */
|
||||
const char *zTSet = "main"; /* Which --testset torun */
|
||||
int doTrace = 0; /* True for --trace */
|
||||
const char *zEncoding = 0; /* --utf16be or --utf16le */
|
||||
@ -1225,6 +1227,9 @@ int main(int argc, char **argv){
|
||||
zTSet = argv[++i];
|
||||
}else if( strcmp(z,"trace")==0 ){
|
||||
doTrace = 1;
|
||||
}else if( strcmp(z,"threads")==0 ){
|
||||
if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
|
||||
nThread = integerValue(argv[++i]);
|
||||
}else if( strcmp(z,"utf16le")==0 ){
|
||||
zEncoding = "utf16le";
|
||||
}else if( strcmp(z,"utf16be")==0 ){
|
||||
@ -1290,6 +1295,7 @@ int main(int argc, char **argv){
|
||||
/* Set database connection options */
|
||||
sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0);
|
||||
if( doTrace ) sqlite3_trace(g.db, traceCallback, 0);
|
||||
speedtest1_exec("PRAGMA threads=%d", nThread);
|
||||
if( zKey ){
|
||||
speedtest1_exec("PRAGMA key('%s')", zKey);
|
||||
}
|
||||
|
@ -1077,6 +1077,7 @@ proc explain_i {sql {db db}} {
|
||||
foreach opcode {
|
||||
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
|
||||
NoConflict Next Prev VNext VPrev VFilter
|
||||
SorterSort SorterNext
|
||||
} {
|
||||
set color($opcode) $B
|
||||
}
|
||||
@ -1099,6 +1100,7 @@ proc explain_i {sql {db db}} {
|
||||
|
||||
if {$opcode=="Next" || $opcode=="Prev"
|
||||
|| $opcode=="VNext" || $opcode=="VPrev"
|
||||
|| $opcode=="SorterNext"
|
||||
} {
|
||||
for {set i $p2} {$i<$addr} {incr i} {
|
||||
incr x($i) 2
|
||||
|
@ -294,6 +294,8 @@ set pragma_def {
|
||||
IF: defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
|
||||
|
||||
NAME: soft_heap_limit
|
||||
|
||||
NAME: threads
|
||||
}
|
||||
fconfigure stdout -translation lf
|
||||
set name {}
|
||||
|
@ -239,6 +239,7 @@ foreach file {
|
||||
malloc.c
|
||||
printf.c
|
||||
random.c
|
||||
threads.c
|
||||
utf.c
|
||||
util.c
|
||||
hash.c
|
||||
|
@ -253,6 +253,7 @@ foreach file {
|
||||
malloc.c
|
||||
printf.c
|
||||
random.c
|
||||
threads.c
|
||||
utf.c
|
||||
util.c
|
||||
hash.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user