Merge the STAT4 capability into trunk.
FossilOrigin-Name: a32af0abe5fa6d570604fa3534e8230d5b6042fc
This commit is contained in:
commit
2f53b90665
78
manifest
78
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sfor\sbuilds\swith\sboth\sSQLITE_OMIT_WAL\sand\sSQLITE_MAX_MMAP_SIZE=0\sdefined.
|
||||
D 2013-08-26T14:30:25.813
|
||||
C Merge\sthe\sSTAT4\scapability\sinto\strunk.
|
||||
D 2013-08-26T23:18:06.972
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5e41da95d92656a5004b03d3576e8b226858a28e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -156,26 +156,26 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
|
||||
F src/analyze.c a33fcb0b3a399d966951feb9f32115106b3ecc2e
|
||||
F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083
|
||||
F src/analyze.c 10a4f1cda87aec6c56760c66e5feaaf1cdc9a641
|
||||
F src/attach.c fea00cab11c854646a27641a263f5876569a51f9
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c b7cbb0bcc61cbf1e0ed3cfcc79ee12ffcf4f469e
|
||||
F src/btree.c b9b57df546df2636294bfb21a986f5707b417df2
|
||||
F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf
|
||||
F src/btreeInt.h 51cf220a9b9223354770883e93a859dc377aa27f
|
||||
F src/build.c f99a715ff9290996b579d5e1ec8e94239dc9ae5e
|
||||
F src/build.c f63e8929c7f89c0074fbc74929bc946ea117b2f8
|
||||
F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267
|
||||
F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 2317c814866d9aa71fea16b3faf4fdd4d6a49b94
|
||||
F src/expr.c 6bab61c757c4c4c94a92e98d507025d5d18afd3c
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 914a6bbd987d857c41ac9d244efa6641f36faadb
|
||||
F src/func.c d7be77897c91d5b9887beb372f1e6deb515c92af
|
||||
F src/func.c 5b064acd303b3e74f019ab551d423ff6cace4023
|
||||
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
|
||||
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
@ -217,11 +217,11 @@ F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68
|
||||
F src/resolve.c 9d53899cc6e1f4ec0b4632d07e97d57827bf63b9
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c 8b148eb851f384412aea57091659d14b369918ca
|
||||
F src/shell.c 1c317a4c96d61d8d9fdad9fd1811d9b10b8c7f57
|
||||
F src/shell.c 00a23311466829d9b77f0be4f7cedee9328279db
|
||||
F src/sqlite.h.in bd1451ba1ab681022a53bccc3c39580ba094a3ff
|
||||
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 124e4b19cd5533116909ccf203bdcdd04a28f99a
|
||||
F src/sqliteInt.h f3636620ad376f65bdbbf5e183f0e4309cb7526e
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -239,11 +239,11 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
|
||||
F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
|
||||
F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16
|
||||
F src/test_config.c 95bb33e9dcaa340a296c0bf0e0ba3d1a1c8004c0
|
||||
F src/test_config.c 3d148e338b575bd937f7746824f36a9c6682d238
|
||||
F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
|
||||
F src/test_func.c f8235719dff4bf9ffee04c55a190af8782ce9ab5
|
||||
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
|
||||
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
|
||||
F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61
|
||||
@ -273,41 +273,43 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2
|
||||
F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e
|
||||
F src/update.c 7f3fe64d8f3b44c44a1eac293f0f85f87c355b7a
|
||||
F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f
|
||||
F src/update.c 7d9d38e4f341ada7d79035ea969cdefb8b9014d1
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9
|
||||
F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8
|
||||
F src/vdbe.c d37aee2c89e2a589f2b1599e0178eb17a08a3ccb
|
||||
F src/vdbe.c 2b1cd2c7e3d74e6aa99ce2f538bfdd07a00dc2f1
|
||||
F src/vdbe.h 4f554b5627f26710c4c36d919110a3fc611ca5c4
|
||||
F src/vdbeInt.h cbe71b8b36d8b3bba5709cc3f436c7e3b47b7b08
|
||||
F src/vdbeapi.c 96b24b946cf21894f63d9393e821baa2f0a80979
|
||||
F src/vdbeaux.c c7fe2695e256dbf254113c4fe90d3ec9aabe3bbe
|
||||
F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69
|
||||
F src/vdbemem.c 61d5ddb8e4d4e14f625062bf5bcc7ce08bb20af3
|
||||
F src/vdbemem.c 1bec36bf054521df810b2f9cc5078cba929ff10d
|
||||
F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017
|
||||
F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc
|
||||
F src/vtab.c 165ce0e797c2cd23badb104c9f2ae9042d6d942c
|
||||
F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
|
||||
F src/where.c 6e718c39d6b2964f15f6c96ce5938b4652e3538e
|
||||
F src/where.c 18cd15160c089dd7854febeaf5a9c065fce6a95a
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783
|
||||
F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748
|
||||
F test/alter.test 7e771c3c3f401198557dbbcf4a2c21950ba934f3
|
||||
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
|
||||
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
|
||||
F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
|
||||
F test/alter4.test 8e93bf7a7e6919b14b0c9a6c1e4908bcf21b0165
|
||||
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
|
||||
F test/analyze.test f8ab7d15858b4093b06caf5e57e2a5ff7104bdae
|
||||
F test/analyze3.test 53cfd07625d110e70b92b57a6ff656ea844dfbee
|
||||
F test/analyze.test 1772936d66471c65221e437b6d1999c3a03166c4
|
||||
F test/analyze3.test 412f690dfe95b337475e3e78a84a85d25f6f125d
|
||||
F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
|
||||
F test/analyze5.test 3e57f192307be931f9ab2f6ff456f9063358ac77
|
||||
F test/analyze6.test cdbf9887d40ab41301f570fe85c6e1525dd3b1c9
|
||||
F test/analyze7.test 7de3ab66e1981303e783102a012d62cb876bf1d5
|
||||
F test/analyze8.test ea4972c76820ac8d6a0754e6f5b851292f0f5a61
|
||||
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
|
||||
F test/analyze6.test 19151da2c4e918905d2081b74ac5c4d47fc850ab
|
||||
F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
|
||||
F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
|
||||
F test/analyze9.test 3095a9ebfea4a2b1f9db60375320ae7f219595ba
|
||||
F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944
|
||||
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
|
||||
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
|
||||
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
|
||||
@ -319,7 +321,7 @@ F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966
|
||||
F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e
|
||||
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
|
||||
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
|
||||
F test/auth.test 4a4c3b034fff7750513520defa910f376c96ab49
|
||||
F test/auth.test 9bea29041871807d9f289ee679d05d3ed103642f
|
||||
F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
|
||||
@ -411,7 +413,7 @@ F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
|
||||
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/date.test f3228180c87bbe5d39c9397bf001c0095c3821b9
|
||||
F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b
|
||||
F test/dbstatus.test aee30c3f337e6c217ff06df59fb8fe6e6448dce2
|
||||
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
|
||||
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
@ -590,7 +592,7 @@ F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
|
||||
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
|
||||
F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
|
||||
F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33
|
||||
F test/index6.test 6a754747f444fecb7f4ae50ce4ac7a0d269e090d
|
||||
F test/index6.test e96324d8c1ade4b30a4c6cee14b1fc2e5a367cda
|
||||
F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
@ -649,7 +651,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 47006c8d70f29b030652e251cb9d35ba60289198
|
||||
F test/mallocA.test 71e4b57e640c017cf2833e51fe6e8e43e8575b73
|
||||
F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be
|
||||
F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6
|
||||
F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4
|
||||
@ -714,7 +716,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 461ef4ea10db02cd421dfe5f988eac3e99b5cd9a
|
||||
F test/permutations.test c5e7ae8a18cb8a0ced38dbbc9e2463536c1de45b
|
||||
F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
|
||||
F test/pragma2.test 224f0381f9411a78ae685cac24c13656a62021b7
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
@ -809,7 +811,7 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806
|
||||
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
|
||||
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
|
||||
F test/table.test 30423211108121884588d24d6776c7f38702ad7b
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
@ -864,7 +866,7 @@ F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
|
||||
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
|
||||
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
|
||||
F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447
|
||||
F test/tkt-cbd054fa6b.test 9c27ed07b333eed458e5d4543f91ecdcf05aeb19
|
||||
F test/tkt-cbd054fa6b.test 06ccd57af3c0c7895d0f7dc844f13c51f8258885
|
||||
F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d
|
||||
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
|
||||
F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30
|
||||
@ -1047,7 +1049,7 @@ F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8
|
||||
F test/where8.test 6f95896633cf2d307b5263145b942b7d33e837c6
|
||||
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
||||
F test/where9.test 9a7fda4a4512abc26a855e8b2b6572b200f6019b
|
||||
F test/where9.test 74245dea86592a744b758dff2e7daf0a07bade7d
|
||||
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
|
||||
@ -1055,7 +1057,7 @@ F test/whereD.test 6c2feb79ef1f68381b07f39017fe5f9b96da8d62
|
||||
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
|
||||
F test/whereF.test 136a7301512d72a08a272806c8767066311b7bc1
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/wild001.test 384db4b30fbe82eaaefe8921f8a702c09e26db27
|
||||
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
|
||||
@ -1105,7 +1107,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae
|
||||
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
|
||||
P f64cd21e2e23ed7cff48f7dafa5e76adde9321c2
|
||||
R 2c7ed4c326daab71e6477ad6fcbe3595
|
||||
U dan
|
||||
Z 1fc299d2108dcbca170e4fd8db67c42b
|
||||
P edd5dbdc3239fc88799b822941603fcc828ecbb6 f86b75b6c7290ee6ddb3636090b00e99fc68c45e
|
||||
R f565ac94f1ba86f7abdb8be442158ff1
|
||||
U drh
|
||||
Z 3380b8636b10d59476656dd5f78e188d
|
||||
|
@ -1 +1 @@
|
||||
edd5dbdc3239fc88799b822941603fcc828ecbb6
|
||||
a32af0abe5fa6d570604fa3534e8230d5b6042fc
|
@ -687,7 +687,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
** can handle (i.e. not CURRENT_TIME etc.)
|
||||
*/
|
||||
if( pDflt ){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3_value *pVal = 0;
|
||||
if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
|
||||
db->mallocFailed = 1;
|
||||
return;
|
||||
|
1439
src/analyze.c
1439
src/analyze.c
File diff suppressed because it is too large
Load Diff
@ -2508,6 +2508,7 @@ static int lockBtree(BtShared *pBt){
|
||||
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
|
||||
pBt->pPage1 = pPage1;
|
||||
pBt->nPage = nPage;
|
||||
assert( pPage1->leaf==0 || pPage1->leaf==1 );
|
||||
return SQLITE_OK;
|
||||
|
||||
page1_init_failed:
|
||||
|
@ -2024,7 +2024,7 @@ static void sqlite3ClearStatTables(
|
||||
){
|
||||
int i;
|
||||
const char *zDbName = pParse->db->aDb[iDb].zName;
|
||||
for(i=1; i<=3; i++){
|
||||
for(i=1; i<=4; i++){
|
||||
char zTab[24];
|
||||
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
|
||||
if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
|
||||
|
@ -117,7 +117,9 @@ static const char * const azCompileOpt[] = {
|
||||
#ifdef SQLITE_ENABLE_RTREE
|
||||
"ENABLE_RTREE",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4)
|
||||
"ENABLE_STAT4",
|
||||
#elif defined(SQLITE_ENABLE_STAT3)
|
||||
"ENABLE_STAT3",
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
@ -1715,4 +1715,7 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
sqlite3AlterFunctions();
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
|
||||
sqlite3AnalyzeFunctions();
|
||||
#endif
|
||||
}
|
||||
|
@ -1280,7 +1280,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
|
||||
if( strcmp(zTable, "sqlite_sequence")==0 ){
|
||||
zPrepStmt = "DELETE FROM sqlite_sequence;\n";
|
||||
}else if( strcmp(zTable, "sqlite_stat1")==0 ){
|
||||
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
|
||||
fprintf(p->out, "ANALYZE sqlite_master;\n");
|
||||
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
|
||||
return 0;
|
||||
|
@ -1550,9 +1550,10 @@ struct Index {
|
||||
unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
|
||||
unsigned bUnordered:1; /* Use this index for == or IN queries only */
|
||||
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
|
||||
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
|
||||
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
|
||||
IndexSample *aSample; /* Samples of the left-most key */
|
||||
#endif
|
||||
};
|
||||
@ -1563,16 +1564,11 @@ struct Index {
|
||||
** analyze.c source file for additional information.
|
||||
*/
|
||||
struct IndexSample {
|
||||
union {
|
||||
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
|
||||
double r; /* Value if eType is SQLITE_FLOAT */
|
||||
i64 i; /* Value if eType is SQLITE_INTEGER */
|
||||
} u;
|
||||
u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
|
||||
int nByte; /* Size in byte of text or blob. */
|
||||
tRowcnt nEq; /* Est. number of rows where the key equals this sample */
|
||||
tRowcnt nLt; /* Est. number of rows where key is less than this sample */
|
||||
tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
|
||||
void *p; /* Pointer to sampled record */
|
||||
int n; /* Size of record in bytes */
|
||||
tRowcnt *anEq; /* Est. number of rows where the key equals this sample */
|
||||
tRowcnt *anLt; /* Est. number of rows where key is less than this sample */
|
||||
tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3044,9 +3040,6 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
|
||||
void sqlite3ValueFree(sqlite3_value*);
|
||||
sqlite3_value *sqlite3ValueNew(sqlite3 *);
|
||||
char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
|
||||
#endif
|
||||
int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
|
||||
void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
@ -3063,6 +3056,7 @@ extern int sqlite3PendingByte;
|
||||
void sqlite3RootPageMoved(sqlite3*, int, int, int);
|
||||
void sqlite3Reindex(Parse*, Token*, Token*);
|
||||
void sqlite3AlterFunctions(void);
|
||||
void sqlite3AnalyzeFunctions(void);
|
||||
void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
|
||||
int sqlite3GetToken(const unsigned char *, int *);
|
||||
void sqlite3NestedParse(Parse*, const char*, ...);
|
||||
@ -3113,6 +3107,9 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
|
||||
void sqlite3BackupRestart(sqlite3_backup *);
|
||||
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
|
||||
|
||||
int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord*);
|
||||
|
||||
/*
|
||||
** The interface to the LEMON-generated parser
|
||||
*/
|
||||
|
@ -458,7 +458,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
|
||||
|
146
src/test_func.c
146
src/test_func.c
@ -18,6 +18,9 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
|
||||
|
||||
/*
|
||||
** Allocate nByte bytes of space using sqlite3_malloc(). If the
|
||||
@ -458,6 +461,147 @@ static void real2hex(
|
||||
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_extract(record, field)
|
||||
**
|
||||
** This function implements an SQL user-function that accepts a blob
|
||||
** containing a formatted database record as the first argument. The
|
||||
** second argument is the index of the field within that record to
|
||||
** extract and return.
|
||||
*/
|
||||
static void test_extract(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
u8 *pRec;
|
||||
u8 *pEndHdr; /* Points to one byte past record header */
|
||||
u8 *pHdr; /* Current point in record header */
|
||||
u8 *pBody; /* Current point in record data */
|
||||
u64 nHdr; /* Bytes in record header */
|
||||
int iIdx; /* Required field */
|
||||
int iCurrent = 0; /* Current field */
|
||||
|
||||
assert( argc==2 );
|
||||
pRec = (u8*)sqlite3_value_blob(argv[0]);
|
||||
iIdx = sqlite3_value_int(argv[1]);
|
||||
|
||||
pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
|
||||
pBody = pEndHdr = &pRec[nHdr];
|
||||
|
||||
for(iCurrent=0; pHdr<pEndHdr && iCurrent<=iIdx; iCurrent++){
|
||||
u64 iSerialType;
|
||||
Mem mem;
|
||||
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.db = db;
|
||||
mem.enc = ENC(db);
|
||||
pHdr += sqlite3GetVarint(pHdr, &iSerialType);
|
||||
pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
|
||||
sqlite3VdbeMemStoreType(&mem);
|
||||
|
||||
if( iCurrent==iIdx ){
|
||||
sqlite3_result_value(context, &mem);
|
||||
}
|
||||
|
||||
sqlite3DbFree(db, mem.zMalloc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** tclcmd: test_decode(record)
|
||||
**
|
||||
** This function implements an SQL user-function that accepts a blob
|
||||
** containing a formatted database record as its only argument. It returns
|
||||
** a tcl list (type SQLITE_TEXT) containing each of the values stored
|
||||
** in the record.
|
||||
*/
|
||||
static void test_decode(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
u8 *pRec;
|
||||
u8 *pEndHdr; /* Points to one byte past record header */
|
||||
u8 *pHdr; /* Current point in record header */
|
||||
u8 *pBody; /* Current point in record data */
|
||||
u64 nHdr; /* Bytes in record header */
|
||||
Tcl_Obj *pRet; /* Return value */
|
||||
|
||||
pRet = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pRet);
|
||||
|
||||
assert( argc==1 );
|
||||
pRec = (u8*)sqlite3_value_blob(argv[0]);
|
||||
|
||||
pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
|
||||
pBody = pEndHdr = &pRec[nHdr];
|
||||
while( pHdr<pEndHdr ){
|
||||
Tcl_Obj *pVal = 0;
|
||||
u64 iSerialType;
|
||||
Mem mem;
|
||||
|
||||
memset(&mem, 0, sizeof(mem));
|
||||
mem.db = db;
|
||||
mem.enc = ENC(db);
|
||||
pHdr += sqlite3GetVarint(pHdr, &iSerialType);
|
||||
pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
|
||||
|
||||
sqlite3VdbeMemStoreType(&mem);
|
||||
switch( sqlite3_value_type(&mem) ){
|
||||
case SQLITE_TEXT:
|
||||
pVal = Tcl_NewStringObj((const char*)sqlite3_value_text(&mem), -1);
|
||||
break;
|
||||
|
||||
case SQLITE_BLOB: {
|
||||
char hexdigit[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
int n = sqlite3_value_bytes(&mem);
|
||||
u8 *z = (u8*)sqlite3_value_blob(&mem);
|
||||
int i;
|
||||
pVal = Tcl_NewStringObj("x'", -1);
|
||||
for(i=0; i<n; i++){
|
||||
char hex[3];
|
||||
hex[0] = hexdigit[((z[i] >> 4) & 0x0F)];
|
||||
hex[1] = hexdigit[(z[i] & 0x0F)];
|
||||
hex[2] = '\0';
|
||||
Tcl_AppendStringsToObj(pVal, hex, 0);
|
||||
}
|
||||
Tcl_AppendStringsToObj(pVal, "'", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case SQLITE_FLOAT:
|
||||
pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_INTEGER:
|
||||
pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem));
|
||||
break;
|
||||
|
||||
case SQLITE_NULL:
|
||||
pVal = Tcl_NewStringObj("NULL", -1);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
}
|
||||
|
||||
Tcl_ListObjAppendElement(0, pRet, pVal);
|
||||
|
||||
if( mem.zMalloc ){
|
||||
sqlite3DbFree(db, mem.zMalloc);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
|
||||
Tcl_DecrRefCount(pRet);
|
||||
}
|
||||
|
||||
|
||||
static int registerTestFunctions(sqlite3 *db){
|
||||
static const struct {
|
||||
@ -482,6 +626,8 @@ static int registerTestFunctions(sqlite3 *db){
|
||||
{ "test_isolation", 2, SQLITE_UTF8, test_isolation},
|
||||
{ "test_counter", 1, SQLITE_UTF8, counterFunc},
|
||||
{ "real2hex", 1, SQLITE_UTF8, real2hex},
|
||||
{ "test_decode", 1, SQLITE_UTF8, test_decode},
|
||||
{ "test_extract", 2, SQLITE_UTF8, test_extract},
|
||||
};
|
||||
int i;
|
||||
|
||||
|
@ -61,7 +61,7 @@ static void updateVirtualTable(
|
||||
void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
assert( pTab!=0 );
|
||||
if( !pTab->pSelect ){
|
||||
sqlite3_value *pValue;
|
||||
sqlite3_value *pValue = 0;
|
||||
u8 enc = ENC(sqlite3VdbeDb(v));
|
||||
Column *pCol = &pTab->aCol[i];
|
||||
VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
|
||||
|
26
src/utf.c
26
src/utf.c
@ -450,32 +450,6 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
|
||||
return m.z;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
|
||||
** enc. A pointer to the new string is returned, and the value of *pnOut
|
||||
** is set to the length of the returned string in bytes. The call should
|
||||
** arrange to call sqlite3DbFree() on the returned pointer when it is
|
||||
** no longer required.
|
||||
**
|
||||
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
|
||||
** flag set.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
|
||||
Mem m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.db = db;
|
||||
sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
|
||||
if( sqlite3VdbeMemTranslate(&m, enc) ){
|
||||
assert( db->mallocFailed );
|
||||
return 0;
|
||||
}
|
||||
assert( m.z==m.zMalloc );
|
||||
*pnOut = m.n;
|
||||
return m.z;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
|
||||
** Return the number of bytes in the first nChar unicode characters
|
||||
|
59
src/vdbe.c
59
src/vdbe.c
@ -638,7 +638,7 @@ int sqlite3VdbeExec(
|
||||
assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] );
|
||||
if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
assert( pOp->p2<=(p->nMem-p->nCursor) );
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
VdbeMemRelease(pOut);
|
||||
@ -649,30 +649,30 @@ int sqlite3VdbeExec(
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( (pOp->opflags & OPFLG_IN1)!=0 ){
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1<=p->nMem );
|
||||
assert( pOp->p1<=(p->nMem-p->nCursor) );
|
||||
assert( memIsValid(&aMem[pOp->p1]) );
|
||||
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_IN2)!=0 ){
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
assert( pOp->p2<=(p->nMem-p->nCursor) );
|
||||
assert( memIsValid(&aMem[pOp->p2]) );
|
||||
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_IN3)!=0 ){
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p3<=p->nMem );
|
||||
assert( pOp->p3<=(p->nMem-p->nCursor) );
|
||||
assert( memIsValid(&aMem[pOp->p3]) );
|
||||
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
|
||||
assert( pOp->p2>0 );
|
||||
assert( pOp->p2<=p->nMem );
|
||||
assert( pOp->p2<=(p->nMem-p->nCursor) );
|
||||
memAboutToChange(p, &aMem[pOp->p2]);
|
||||
}
|
||||
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p3<=p->nMem );
|
||||
assert( pOp->p3<=(p->nMem-p->nCursor) );
|
||||
memAboutToChange(p, &aMem[pOp->p3]);
|
||||
}
|
||||
#endif
|
||||
@ -765,7 +765,7 @@ check_for_interrupt:
|
||||
** and then jump to address P2.
|
||||
*/
|
||||
case OP_Gosub: { /* jump */
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nMem );
|
||||
assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( (pIn1->flags & MEM_Dyn)==0 );
|
||||
memAboutToChange(p, pIn1);
|
||||
@ -977,7 +977,7 @@ case OP_Null: { /* out2-prerelease */
|
||||
int cnt;
|
||||
u16 nullFlag;
|
||||
cnt = pOp->p3-pOp->p2;
|
||||
assert( pOp->p3<=p->nMem );
|
||||
assert( pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
|
||||
while( cnt>0 ){
|
||||
pOut++;
|
||||
@ -1046,8 +1046,8 @@ case OP_Move: {
|
||||
pIn1 = &aMem[p1];
|
||||
pOut = &aMem[p2];
|
||||
while( n-- ){
|
||||
assert( pOut<=&aMem[p->nMem] );
|
||||
assert( pIn1<=&aMem[p->nMem] );
|
||||
assert( pOut<=&aMem[(p->nMem-p->nCursor)] );
|
||||
assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
|
||||
assert( memIsValid(pIn1) );
|
||||
memAboutToChange(p, pOut);
|
||||
zMalloc = pOut->zMalloc;
|
||||
@ -1131,7 +1131,7 @@ case OP_ResultRow: {
|
||||
int i;
|
||||
assert( p->nResColumn==pOp->p2 );
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1+pOp->p2<=p->nMem+1 );
|
||||
assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 );
|
||||
|
||||
/* If this statement has violated immediate foreign key constraints, do
|
||||
** not return the number of rows modified. And do not RELEASE the statement
|
||||
@ -1405,11 +1405,11 @@ case OP_Function: {
|
||||
n = pOp->p5;
|
||||
apVal = p->apArg;
|
||||
assert( apVal || n==0 );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pOut = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pOut);
|
||||
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
|
||||
assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
|
||||
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
|
||||
pArg = &aMem[pOp->p2];
|
||||
for(i=0; i<n; i++, pArg++){
|
||||
@ -1939,11 +1939,11 @@ case OP_Compare: {
|
||||
if( aPermute ){
|
||||
int k, mx = 0;
|
||||
for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
|
||||
assert( p1>0 && p1+mx<=p->nMem+1 );
|
||||
assert( p2>0 && p2+mx<=p->nMem+1 );
|
||||
assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 );
|
||||
assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 );
|
||||
}else{
|
||||
assert( p1>0 && p1+n<=p->nMem+1 );
|
||||
assert( p2>0 && p2+n<=p->nMem+1 );
|
||||
assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 );
|
||||
assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 );
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
for(i=0; i<n; i++){
|
||||
@ -2194,7 +2194,7 @@ case OP_Column: {
|
||||
pC = 0;
|
||||
memset(&sMem, 0, sizeof(sMem));
|
||||
assert( p1<p->nCursor );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
zRec = 0;
|
||||
@ -2492,7 +2492,7 @@ case OP_Affinity: {
|
||||
assert( zAffinity[pOp->p2]==0 );
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
while( (cAff = *(zAffinity++))!=0 ){
|
||||
assert( pIn1 <= &p->aMem[p->nMem] );
|
||||
assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] );
|
||||
assert( memIsValid(pIn1) );
|
||||
ExpandBlob(pIn1);
|
||||
applyAffinity(pIn1, cAff, encoding);
|
||||
@ -2553,7 +2553,7 @@ case OP_MakeRecord: {
|
||||
nZero = 0; /* Number of zero bytes at the end of the record */
|
||||
nField = pOp->p1;
|
||||
zAffinity = pOp->p4.z;
|
||||
assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 );
|
||||
assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 );
|
||||
pData0 = &aMem[nField];
|
||||
nField = pOp->p2;
|
||||
pLast = &pData0[nField-1];
|
||||
@ -2619,7 +2619,7 @@ case OP_MakeRecord: {
|
||||
}
|
||||
assert( i==nByte );
|
||||
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pOut->n = (int)nByte;
|
||||
pOut->flags = MEM_Blob | MEM_Dyn;
|
||||
pOut->xDel = 0;
|
||||
@ -3199,7 +3199,7 @@ case OP_OpenWrite: {
|
||||
}
|
||||
if( pOp->p5 & OPFLAG_P2ISREG ){
|
||||
assert( p2>0 );
|
||||
assert( p2<=p->nMem );
|
||||
assert( p2<=(p->nMem-p->nCursor) );
|
||||
pIn2 = &aMem[p2];
|
||||
assert( memIsValid(pIn2) );
|
||||
assert( (pIn2->flags & MEM_Int)!=0 );
|
||||
@ -3736,7 +3736,7 @@ case OP_IsUnique: { /* jump, in3 */
|
||||
aMx = &aMem[pOp->p4.i];
|
||||
/* Assert that the values of parameters P1 and P4 are in range. */
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
|
||||
assert( pOp->p4.i>0 && pOp->p4.i<=(p->nMem-p->nCursor) );
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
|
||||
/* Find the index cursor. */
|
||||
@ -3939,7 +3939,7 @@ case OP_NewRowid: { /* out2-prerelease */
|
||||
pMem = &pFrame->aMem[pOp->p3];
|
||||
}else{
|
||||
/* Assert that P3 is a valid memory cell. */
|
||||
assert( pOp->p3<=p->nMem );
|
||||
assert( pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pMem = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pMem);
|
||||
}
|
||||
@ -4593,7 +4593,7 @@ case OP_IdxDelete: {
|
||||
UnpackedRecord r;
|
||||
|
||||
assert( pOp->p3>0 );
|
||||
assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
|
||||
assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 );
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
@ -4801,6 +4801,7 @@ case OP_Clear: {
|
||||
|
||||
nChange = 0;
|
||||
assert( p->readOnly==0 );
|
||||
assert( pOp->p1!=1 );
|
||||
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
|
||||
rc = sqlite3BtreeClearTable(
|
||||
db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
|
||||
@ -5001,7 +5002,7 @@ case OP_IntegrityCk: {
|
||||
assert( nRoot>0 );
|
||||
aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
|
||||
if( aRoot==0 ) goto no_mem;
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pnErr = &aMem[pOp->p3];
|
||||
assert( (pnErr->flags & MEM_Int)!=0 );
|
||||
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
|
||||
@ -5425,7 +5426,7 @@ case OP_AggStep: {
|
||||
sqlite3VdbeMemStoreType(pRec);
|
||||
}
|
||||
ctx.pFunc = pOp->p4.pFunc;
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
ctx.pMem = pMem = &aMem[pOp->p3];
|
||||
pMem->n++;
|
||||
ctx.s.flags = MEM_Null;
|
||||
@ -5472,7 +5473,7 @@ case OP_AggStep: {
|
||||
*/
|
||||
case OP_AggFinal: {
|
||||
Mem *pMem;
|
||||
assert( pOp->p1>0 && pOp->p1<=p->nMem );
|
||||
assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
|
||||
pMem = &aMem[pOp->p1];
|
||||
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
|
||||
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
|
||||
@ -5889,7 +5890,7 @@ case OP_VColumn: {
|
||||
|
||||
VdbeCursor *pCur = p->apCsr[pOp->p1];
|
||||
assert( pCur->pVtabCursor );
|
||||
assert( pOp->p3>0 && pOp->p3<=p->nMem );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
if( pCur->nullRow ){
|
||||
|
296
src/vdbemem.c
296
src/vdbemem.c
@ -1001,27 +1001,92 @@ sqlite3_value *sqlite3ValueNew(sqlite3 *db){
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new sqlite3_value object, containing the value of pExpr.
|
||||
**
|
||||
** This only works for very simple expressions that consist of one constant
|
||||
** token (i.e. "5", "5.1", "'a string'"). If the expression can
|
||||
** be converted directly into a value, then the value is allocated and
|
||||
** a pointer written to *ppVal. The caller is responsible for deallocating
|
||||
** the value by passing it to sqlite3ValueFree() later on. If the expression
|
||||
** cannot be converted to a value, then *ppVal is set to NULL.
|
||||
** Context object passed by sqlite3Stat4ProbeSetValue() through to
|
||||
** valueNew(). See comments above valueNew() for details.
|
||||
*/
|
||||
int sqlite3ValueFromExpr(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Expr *pExpr, /* The expression to evaluate */
|
||||
u8 enc, /* Encoding to use */
|
||||
u8 affinity, /* Affinity to use */
|
||||
sqlite3_value **ppVal /* Write the new value here */
|
||||
struct ValueNewStat4Ctx {
|
||||
Parse *pParse;
|
||||
Index *pIdx;
|
||||
UnpackedRecord **ppRec;
|
||||
int iVal;
|
||||
};
|
||||
|
||||
/*
|
||||
** Allocate and return a pointer to a new sqlite3_value object. If
|
||||
** the second argument to this function is NULL, the object is allocated
|
||||
** by calling sqlite3ValueNew().
|
||||
**
|
||||
** Otherwise, if the second argument is non-zero, then this function is
|
||||
** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
|
||||
** already been allocated, allocate the UnpackedRecord structure that
|
||||
** that function will return to its caller here. Then return a pointer
|
||||
** an sqlite3_value within the UnpackedRecord.a[] array.
|
||||
*/
|
||||
static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
if( p ){
|
||||
UnpackedRecord *pRec = p->ppRec[0];
|
||||
|
||||
if( pRec==0 ){
|
||||
Index *pIdx = p->pIdx; /* Index being probed */
|
||||
int nByte; /* Bytes of space to allocate */
|
||||
int i; /* Counter variable */
|
||||
int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */
|
||||
|
||||
nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord);
|
||||
pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
|
||||
if( pRec ){
|
||||
pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx);
|
||||
if( pRec->pKeyInfo ){
|
||||
assert( pRec->pKeyInfo->nField+1==nCol );
|
||||
pRec->pKeyInfo->enc = ENC(db);
|
||||
pRec->flags = UNPACKED_PREFIX_MATCH;
|
||||
pRec->aMem = (Mem *)&pRec[1];
|
||||
for(i=0; i<nCol; i++){
|
||||
pRec->aMem[i].flags = MEM_Null;
|
||||
pRec->aMem[i].type = SQLITE_NULL;
|
||||
pRec->aMem[i].db = db;
|
||||
}
|
||||
}else{
|
||||
sqlite3DbFree(db, pRec);
|
||||
pRec = 0;
|
||||
}
|
||||
}
|
||||
if( pRec==0 ) return 0;
|
||||
p->ppRec[0] = pRec;
|
||||
}
|
||||
|
||||
pRec->nField = p->iVal+1;
|
||||
return &pRec->aMem[p->iVal];
|
||||
}
|
||||
#endif
|
||||
return sqlite3ValueNew(db);
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract a value from the supplied expression in the manner described
|
||||
** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
|
||||
** using valueNew().
|
||||
**
|
||||
** If pCtx is NULL and an error occurs after the sqlite3_value object
|
||||
** has been allocated, it is freed before returning. Or, if pCtx is not
|
||||
** NULL, it is assumed that the caller will free any allocated object
|
||||
** in all cases.
|
||||
*/
|
||||
int valueFromExpr(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Expr *pExpr, /* The expression to evaluate */
|
||||
u8 enc, /* Encoding to use */
|
||||
u8 affinity, /* Affinity to use */
|
||||
sqlite3_value **ppVal, /* Write the new value here */
|
||||
struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
|
||||
){
|
||||
int op;
|
||||
char *zVal = 0;
|
||||
sqlite3_value *pVal = 0;
|
||||
int negInt = 1;
|
||||
const char *zNeg = "";
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( !pExpr ){
|
||||
*ppVal = 0;
|
||||
@ -1029,11 +1094,11 @@ int sqlite3ValueFromExpr(
|
||||
}
|
||||
op = pExpr->op;
|
||||
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
|
||||
/* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT4.
|
||||
** The ifdef here is to enable us to achieve 100% branch test coverage even
|
||||
** when SQLITE_ENABLE_STAT3 is omitted.
|
||||
** when SQLITE_ENABLE_STAT4 is omitted.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
if( op==TK_REGISTER ) op = pExpr->op2;
|
||||
#else
|
||||
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
|
||||
@ -1051,7 +1116,7 @@ int sqlite3ValueFromExpr(
|
||||
}
|
||||
|
||||
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
|
||||
pVal = sqlite3ValueNew(db);
|
||||
pVal = valueNew(db, pCtx);
|
||||
if( pVal==0 ) goto no_mem;
|
||||
if( ExprHasProperty(pExpr, EP_IntValue) ){
|
||||
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
|
||||
@ -1068,11 +1133,13 @@ int sqlite3ValueFromExpr(
|
||||
}
|
||||
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
|
||||
if( enc!=SQLITE_UTF8 ){
|
||||
sqlite3VdbeChangeEncoding(pVal, enc);
|
||||
rc = sqlite3VdbeChangeEncoding(pVal, enc);
|
||||
}
|
||||
}else if( op==TK_UMINUS ) {
|
||||
/* This branch happens for multiple negative signs. Ex: -(-5) */
|
||||
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
|
||||
if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal)
|
||||
&& pVal!=0
|
||||
){
|
||||
sqlite3VdbeMemNumerify(pVal);
|
||||
if( pVal->u.i==SMALLEST_INT64 ){
|
||||
pVal->flags &= MEM_Int;
|
||||
@ -1085,7 +1152,7 @@ int sqlite3ValueFromExpr(
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, enc);
|
||||
}
|
||||
}else if( op==TK_NULL ){
|
||||
pVal = sqlite3ValueNew(db);
|
||||
pVal = valueNew(db, pCtx);
|
||||
if( pVal==0 ) goto no_mem;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_BLOB_LITERAL
|
||||
@ -1093,7 +1160,7 @@ int sqlite3ValueFromExpr(
|
||||
int nVal;
|
||||
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
|
||||
assert( pExpr->u.zToken[1]=='\'' );
|
||||
pVal = sqlite3ValueNew(db);
|
||||
pVal = valueNew(db, pCtx);
|
||||
if( !pVal ) goto no_mem;
|
||||
zVal = &pExpr->u.zToken[2];
|
||||
nVal = sqlite3Strlen30(zVal)-1;
|
||||
@ -1107,16 +1174,195 @@ int sqlite3ValueFromExpr(
|
||||
sqlite3VdbeMemStoreType(pVal);
|
||||
}
|
||||
*ppVal = pVal;
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
|
||||
no_mem:
|
||||
db->mallocFailed = 1;
|
||||
sqlite3DbFree(db, zVal);
|
||||
sqlite3ValueFree(pVal);
|
||||
*ppVal = 0;
|
||||
assert( *ppVal==0 );
|
||||
if( pCtx==0 ) sqlite3ValueFree(pVal);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new sqlite3_value object, containing the value of pExpr.
|
||||
**
|
||||
** This only works for very simple expressions that consist of one constant
|
||||
** token (i.e. "5", "5.1", "'a string'"). If the expression can
|
||||
** be converted directly into a value, then the value is allocated and
|
||||
** a pointer written to *ppVal. The caller is responsible for deallocating
|
||||
** the value by passing it to sqlite3ValueFree() later on. If the expression
|
||||
** cannot be converted to a value, then *ppVal is set to NULL.
|
||||
*/
|
||||
int sqlite3ValueFromExpr(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Expr *pExpr, /* The expression to evaluate */
|
||||
u8 enc, /* Encoding to use */
|
||||
u8 affinity, /* Affinity to use */
|
||||
sqlite3_value **ppVal /* Write the new value here */
|
||||
){
|
||||
return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
/*
|
||||
** The implementation of the sqlite_record() function. This function accepts
|
||||
** a single argument of any type. The return value is a formatted database
|
||||
** record (a blob) containing the argument value.
|
||||
**
|
||||
** This is used to convert the value stored in the 'sample' column of the
|
||||
** sqlite_stat3 table to the record format SQLite uses internally.
|
||||
*/
|
||||
static void recordFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
const int file_format = 1;
|
||||
int iSerial; /* Serial type */
|
||||
int nSerial; /* Bytes of space for iSerial as varint */
|
||||
int nVal; /* Bytes of space required for argv[0] */
|
||||
int nRet;
|
||||
sqlite3 *db;
|
||||
u8 *aRet;
|
||||
|
||||
iSerial = sqlite3VdbeSerialType(argv[0], file_format);
|
||||
nSerial = sqlite3VarintLen(iSerial);
|
||||
nVal = sqlite3VdbeSerialTypeLen(iSerial);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
|
||||
nRet = 1 + nSerial + nVal;
|
||||
aRet = sqlite3DbMallocRaw(db, nRet);
|
||||
if( aRet==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
}else{
|
||||
aRet[0] = nSerial+1;
|
||||
sqlite3PutVarint(&aRet[1], iSerial);
|
||||
sqlite3VdbeSerialPut(&aRet[1+nSerial], nVal, argv[0], file_format);
|
||||
sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
|
||||
sqlite3DbFree(db, aRet);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help read ANALYZE data.
|
||||
*/
|
||||
void sqlite3AnalyzeFunctions(void){
|
||||
static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
|
||||
FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
|
||||
};
|
||||
int i;
|
||||
FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
|
||||
FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
|
||||
for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
|
||||
sqlite3FuncDefInsert(pHash, &aFunc[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to allocate and populate UnpackedRecord
|
||||
** structures intended to be compared against sample index keys stored
|
||||
** in the sqlite_stat4 table.
|
||||
**
|
||||
** A single call to this function attempts to populates field iVal (leftmost
|
||||
** is 0 etc.) of the unpacked record with a value extracted from expression
|
||||
** pExpr. Extraction of values is possible if:
|
||||
**
|
||||
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
|
||||
**
|
||||
** * The expression is a bound variable, and this is a reprepare, or
|
||||
**
|
||||
** * The sqlite3ValueFromExpr() function is able to extract a value
|
||||
** from the expression (i.e. the expression is a literal value).
|
||||
**
|
||||
** If a value can be extracted, the affinity passed as the 5th argument
|
||||
** is applied to it before it is copied into the UnpackedRecord. Output
|
||||
** parameter *pbOk is set to true if a value is extracted, or false
|
||||
** otherwise.
|
||||
**
|
||||
** When this function is called, *ppRec must either point to an object
|
||||
** allocated by an earlier call to this function, or must be NULL. If it
|
||||
** is NULL and a value can be successfully extracted, a new UnpackedRecord
|
||||
** is allocated (and *ppRec set to point to it) before returning.
|
||||
**
|
||||
** Unless an error is encountered, SQLITE_OK is returned. It is not an
|
||||
** error if a value cannot be extracted from pExpr. If an error does
|
||||
** occur, an SQLite error code is returned.
|
||||
*/
|
||||
int sqlite3Stat4ProbeSetValue(
|
||||
Parse *pParse, /* Parse context */
|
||||
Index *pIdx, /* Index being probed */
|
||||
UnpackedRecord **ppRec, /* IN/OUT: Probe record */
|
||||
Expr *pExpr, /* The expression to extract a value from */
|
||||
u8 affinity, /* Affinity to use */
|
||||
int iVal, /* Array element to populate */
|
||||
int *pbOk /* OUT: True if value was extracted */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_value *pVal = 0;
|
||||
|
||||
struct ValueNewStat4Ctx alloc;
|
||||
alloc.pParse = pParse;
|
||||
alloc.pIdx = pIdx;
|
||||
alloc.ppRec = ppRec;
|
||||
alloc.iVal = iVal;
|
||||
|
||||
if( !pExpr ){
|
||||
pVal = valueNew(pParse->db, &alloc);
|
||||
if( pVal ){
|
||||
sqlite3VdbeMemSetNull((Mem*)pVal);
|
||||
*pbOk = 1;
|
||||
}
|
||||
}else if( pExpr->op==TK_VARIABLE
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
|
||||
){
|
||||
Vdbe *v;
|
||||
int iBindVar = pExpr->iColumn;
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
|
||||
if( (v = pParse->pReprepare)!=0 ){
|
||||
pVal = valueNew(pParse->db, &alloc);
|
||||
if( pVal ){
|
||||
rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
|
||||
}
|
||||
pVal->db = pParse->db;
|
||||
*pbOk = 1;
|
||||
sqlite3VdbeMemStoreType((Mem*)pVal);
|
||||
}
|
||||
}else{
|
||||
*pbOk = 0;
|
||||
}
|
||||
}else{
|
||||
sqlite3 *db = pParse->db;
|
||||
rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc);
|
||||
*pbOk = (pVal!=0);
|
||||
}
|
||||
|
||||
assert( pVal==0 || pVal->db==pParse->db );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unless it is NULL, the argument must be an UnpackedRecord object returned
|
||||
** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
|
||||
** the object.
|
||||
*/
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
|
||||
if( pRec ){
|
||||
int i;
|
||||
int nCol = pRec->pKeyInfo->nField+1;
|
||||
Mem *aMem = pRec->aMem;
|
||||
sqlite3 *db = aMem[0].db;
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3DbFree(db, aMem[i].zMalloc);
|
||||
}
|
||||
sqlite3DbFree(db, pRec->pKeyInfo);
|
||||
sqlite3DbFree(db, pRec);
|
||||
}
|
||||
}
|
||||
#endif /* ifdef SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/*
|
||||
** Change the string value of an sqlite3_value object
|
||||
*/
|
||||
|
467
src/where.c
467
src/where.c
@ -286,7 +286,7 @@ struct WhereTerm {
|
||||
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
|
||||
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
|
||||
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
|
||||
#else
|
||||
# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
|
||||
@ -392,6 +392,10 @@ struct WhereLoopBuilder {
|
||||
ExprList *pOrderBy; /* ORDER BY clause */
|
||||
WhereLoop *pNew; /* Template WhereLoop */
|
||||
WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
UnpackedRecord *pRec; /* Probe for stat4 (if required) */
|
||||
int nRecValid; /* Number of valid fields currently in pRec */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1786,7 +1790,7 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
/* When sqlite_stat3 histogram data is available an operator of the
|
||||
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
|
||||
@ -2394,7 +2398,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
/*
|
||||
** Estimate the location of a particular key among all keys in an
|
||||
** index. Store the results in aStat as follows:
|
||||
@ -2404,141 +2408,70 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
|
||||
**
|
||||
** Return SQLITE_OK on success.
|
||||
*/
|
||||
static int whereKeyStats(
|
||||
static void whereKeyStats(
|
||||
Parse *pParse, /* Database connection */
|
||||
Index *pIdx, /* Index to consider domain of */
|
||||
sqlite3_value *pVal, /* Value to consider */
|
||||
UnpackedRecord *pRec, /* Vector of values to consider */
|
||||
int roundUp, /* Round up if true. Round down if false */
|
||||
tRowcnt *aStat /* OUT: stats written here */
|
||||
){
|
||||
tRowcnt n;
|
||||
IndexSample *aSample;
|
||||
int i, eType;
|
||||
int isEq = 0;
|
||||
i64 v;
|
||||
double r, rS;
|
||||
IndexSample *aSample = pIdx->aSample;
|
||||
int iCol = pRec->nField-1; /* Index of required stats in anEq[] etc. */
|
||||
int iMin = 0; /* Smallest sample not yet tested */
|
||||
int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */
|
||||
int iTest; /* Next sample to test */
|
||||
int res; /* Result of comparison operation */
|
||||
|
||||
assert( roundUp==0 || roundUp==1 );
|
||||
assert( pIdx->nSample>0 );
|
||||
if( pVal==0 ) return SQLITE_ERROR;
|
||||
n = pIdx->aiRowEst[0];
|
||||
aSample = pIdx->aSample;
|
||||
eType = sqlite3_value_type(pVal);
|
||||
assert( pRec->nField>0 && iCol<pIdx->nSampleCol );
|
||||
do{
|
||||
iTest = (iMin+i)/2;
|
||||
res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec);
|
||||
if( res<0 ){
|
||||
iMin = iTest+1;
|
||||
}else{
|
||||
i = iTest;
|
||||
}
|
||||
}while( res && iMin<i );
|
||||
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
v = sqlite3_value_int64(pVal);
|
||||
r = (i64)v;
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_NULL ) continue;
|
||||
if( aSample[i].eType>=SQLITE_TEXT ) break;
|
||||
if( aSample[i].eType==SQLITE_INTEGER ){
|
||||
if( aSample[i].u.i>=v ){
|
||||
isEq = aSample[i].u.i==v;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
assert( aSample[i].eType==SQLITE_FLOAT );
|
||||
if( aSample[i].u.r>=r ){
|
||||
isEq = aSample[i].u.r==r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if( eType==SQLITE_FLOAT ){
|
||||
r = sqlite3_value_double(pVal);
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_NULL ) continue;
|
||||
if( aSample[i].eType>=SQLITE_TEXT ) break;
|
||||
if( aSample[i].eType==SQLITE_FLOAT ){
|
||||
rS = aSample[i].u.r;
|
||||
}else{
|
||||
rS = aSample[i].u.i;
|
||||
}
|
||||
if( rS>=r ){
|
||||
isEq = rS==r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if( eType==SQLITE_NULL ){
|
||||
i = 0;
|
||||
if( aSample[0].eType==SQLITE_NULL ) isEq = 1;
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* The following assert statements check that the binary search code
|
||||
** above found the right answer. This block serves no purpose other
|
||||
** than to invoke the asserts. */
|
||||
if( res==0 ){
|
||||
/* If (res==0) is true, then sample $i must be equal to pRec */
|
||||
assert( i<pIdx->nSample );
|
||||
assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)
|
||||
|| pParse->db->mallocFailed );
|
||||
}else{
|
||||
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
|
||||
for(i=0; i<pIdx->nSample; i++){
|
||||
if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( i<pIdx->nSample ){
|
||||
sqlite3 *db = pParse->db;
|
||||
CollSeq *pColl;
|
||||
const u8 *z;
|
||||
if( eType==SQLITE_BLOB ){
|
||||
z = (const u8 *)sqlite3_value_blob(pVal);
|
||||
pColl = db->pDfltColl;
|
||||
assert( pColl->enc==SQLITE_UTF8 );
|
||||
}else{
|
||||
pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl);
|
||||
/* If the collating sequence was unavailable, we should have failed
|
||||
** long ago and never reached this point. But we'll check just to
|
||||
** be doubly sure. */
|
||||
if( NEVER(pColl==0) ) return SQLITE_ERROR;
|
||||
z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
|
||||
if( !z ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( z && pColl && pColl->xCmp );
|
||||
}
|
||||
n = sqlite3ValueBytes(pVal, pColl->enc);
|
||||
|
||||
for(; i<pIdx->nSample; i++){
|
||||
int c;
|
||||
int eSampletype = aSample[i].eType;
|
||||
if( eSampletype<eType ) continue;
|
||||
if( eSampletype!=eType ) break;
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
if( pColl->enc!=SQLITE_UTF8 ){
|
||||
int nSample;
|
||||
char *zSample = sqlite3Utf8to16(
|
||||
db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample
|
||||
);
|
||||
if( !zSample ){
|
||||
assert( db->mallocFailed );
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
|
||||
sqlite3DbFree(db, zSample);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
|
||||
}
|
||||
if( c>=0 ){
|
||||
if( c==0 ) isEq = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Otherwise, pRec must be smaller than sample $i and larger than
|
||||
** sample ($i-1). */
|
||||
assert( i==pIdx->nSample
|
||||
|| sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0
|
||||
|| pParse->db->mallocFailed );
|
||||
assert( i==0
|
||||
|| sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
|
||||
|| pParse->db->mallocFailed );
|
||||
}
|
||||
#endif /* ifdef SQLITE_DEBUG */
|
||||
|
||||
/* At this point, aSample[i] is the first sample that is greater than
|
||||
** or equal to pVal. Or if i==pIdx->nSample, then all samples are less
|
||||
** than pVal. If aSample[i]==pVal, then isEq==1.
|
||||
** than pVal. If aSample[i]==pVal, then res==0.
|
||||
*/
|
||||
if( isEq ){
|
||||
assert( i<pIdx->nSample );
|
||||
aStat[0] = aSample[i].nLt;
|
||||
aStat[1] = aSample[i].nEq;
|
||||
if( res==0 ){
|
||||
aStat[0] = aSample[i].anLt[iCol];
|
||||
aStat[1] = aSample[i].anEq[iCol];
|
||||
}else{
|
||||
tRowcnt iLower, iUpper, iGap;
|
||||
if( i==0 ){
|
||||
iLower = 0;
|
||||
iUpper = aSample[0].nLt;
|
||||
iUpper = aSample[0].anLt[iCol];
|
||||
}else{
|
||||
iUpper = i>=pIdx->nSample ? n : aSample[i].nLt;
|
||||
iLower = aSample[i-1].nEq + aSample[i-1].nLt;
|
||||
iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol];
|
||||
iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol];
|
||||
}
|
||||
aStat[1] = pIdx->avgEq;
|
||||
aStat[1] = (pIdx->nColumn>iCol ? pIdx->aAvgEq[iCol] : 1);
|
||||
if( iLower>=iUpper ){
|
||||
iGap = 0;
|
||||
}else{
|
||||
@ -2551,44 +2484,8 @@ static int whereKeyStats(
|
||||
}
|
||||
aStat[0] = iLower + iGap;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT3 */
|
||||
|
||||
/*
|
||||
** If expression pExpr represents a literal value, set *pp to point to
|
||||
** an sqlite3_value structure containing the same value, with affinity
|
||||
** aff applied to it, before returning. It is the responsibility of the
|
||||
** caller to eventually release this structure by passing it to
|
||||
** sqlite3ValueFree().
|
||||
**
|
||||
** If the current parse is a recompile (sqlite3Reprepare()) and pExpr
|
||||
** is an SQL variable that currently has a non-NULL value bound to it,
|
||||
** create an sqlite3_value structure containing this value, again with
|
||||
** affinity aff applied to it, instead.
|
||||
**
|
||||
** If neither of the above apply, set *pp to NULL.
|
||||
**
|
||||
** If an error occurs, return an error code. Otherwise, SQLITE_OK.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
static int valueFromExpr(
|
||||
Parse *pParse,
|
||||
Expr *pExpr,
|
||||
u8 aff,
|
||||
sqlite3_value **pp
|
||||
){
|
||||
if( pExpr->op==TK_VARIABLE
|
||||
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
|
||||
){
|
||||
int iVar = pExpr->iColumn;
|
||||
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
|
||||
*pp = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, aff);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
|
||||
}
|
||||
#endif
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/*
|
||||
** This function is used to estimate the number of rows that will be visited
|
||||
@ -2605,103 +2502,150 @@ static int valueFromExpr(
|
||||
** If either of the upper or lower bound is not present, then NULL is passed in
|
||||
** place of the corresponding WhereTerm.
|
||||
**
|
||||
** The nEq parameter is passed the index of the index column subject to the
|
||||
** range constraint. Or, equivalently, the number of equality constraints
|
||||
** optimized by the proposed index scan. For example, assuming index p is
|
||||
** on t1(a, b), and the SQL query is:
|
||||
** The value in (pBuilder->pNew->u.btree.nEq) is the index of the index
|
||||
** column subject to the range constraint. Or, equivalently, the number of
|
||||
** equality constraints optimized by the proposed index scan. For example,
|
||||
** assuming index p is on t1(a, b), and the SQL query is:
|
||||
**
|
||||
** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ...
|
||||
**
|
||||
** then nEq should be passed the value 1 (as the range restricted column,
|
||||
** b, is the second left-most column of the index). Or, if the query is:
|
||||
** then nEq is set to 1 (as the range restricted column, b, is the second
|
||||
** left-most column of the index). Or, if the query is:
|
||||
**
|
||||
** ... FROM t1 WHERE a > ? AND a < ? ...
|
||||
**
|
||||
** then nEq should be passed 0.
|
||||
** then nEq is set to 0.
|
||||
**
|
||||
** The returned value is an integer divisor to reduce the estimated
|
||||
** search space. A return value of 1 means that range constraints are
|
||||
** no help at all. A return value of 2 means range constraints are
|
||||
** expected to reduce the search space by half. And so forth...
|
||||
**
|
||||
** In the absence of sqlite_stat3 ANALYZE data, each range inequality
|
||||
** reduces the search space by a factor of 4. Hence a single constraint (x>?)
|
||||
** results in a return of 4 and a range constraint (x>? AND x<?) results
|
||||
** in a return of 16.
|
||||
** When this function is called, *pnOut is set to the whereCost() of the
|
||||
** number of rows that the index scan is expected to visit without
|
||||
** considering the range constraints. If nEq is 0, this is the number of
|
||||
** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
|
||||
** to account for the range contraints pLower and pUpper.
|
||||
**
|
||||
** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be
|
||||
** used, each range inequality reduces the search space by a factor of 4.
|
||||
** Hence a pair of constraints (x>? AND x<?) reduces the expected number of
|
||||
** rows visited by a factor of 16.
|
||||
*/
|
||||
static int whereRangeScanEst(
|
||||
Parse *pParse, /* Parsing & code generating context */
|
||||
Index *p, /* The index containing the range-compared column; "x" */
|
||||
int nEq, /* index into p->aCol[] of the range-compared column */
|
||||
WhereLoopBuilder *pBuilder,
|
||||
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
|
||||
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
|
||||
WhereCost *pRangeDiv /* OUT: Reduce search space by this divisor */
|
||||
WhereCost *pnOut /* IN/OUT: Number of rows visited */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int nOut = (int)*pnOut;
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
int nEq = pBuilder->pNew->u.btree.nEq;
|
||||
|
||||
if( nEq==0 && p->nSample && OptimizationEnabled(pParse->db, SQLITE_Stat3) ){
|
||||
sqlite3_value *pRangeVal;
|
||||
tRowcnt iLower = 0;
|
||||
tRowcnt iUpper = p->aiRowEst[0];
|
||||
if( nEq==pBuilder->nRecValid
|
||||
&& nEq<p->nSampleCol
|
||||
&& p->nSample
|
||||
&& OptimizationEnabled(pParse->db, SQLITE_Stat3)
|
||||
){
|
||||
UnpackedRecord *pRec = pBuilder->pRec;
|
||||
tRowcnt a[2];
|
||||
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
|
||||
|
||||
/* Variable iLower will be set to the estimate of the number of rows in
|
||||
** the index that are less than the lower bound of the range query. The
|
||||
** lower bound being the concatenation of $P and $L, where $P is the
|
||||
** key-prefix formed by the nEq values matched against the nEq left-most
|
||||
** columns of the index, and $L is the value in pLower.
|
||||
**
|
||||
** Or, if pLower is NULL or $L cannot be extracted from it (because it
|
||||
** is not a simple variable or literal value), the lower bound of the
|
||||
** range is $P. Due to a quirk in the way whereKeyStats() works, even
|
||||
** if $L is available, whereKeyStats() is called for both ($P) and
|
||||
** ($P:$L) and the larger of the two returned values used.
|
||||
**
|
||||
** Similarly, iUpper is to be set to the estimate of the number of rows
|
||||
** less than the upper bound of the range query. Where the upper bound
|
||||
** is either ($P) or ($P:$U). Again, even if $U is available, both values
|
||||
** of iUpper are requested of whereKeyStats() and the smaller used.
|
||||
*/
|
||||
tRowcnt iLower;
|
||||
tRowcnt iUpper;
|
||||
|
||||
/* Determine iLower and iUpper using ($P) only. */
|
||||
if( nEq==0 ){
|
||||
iLower = 0;
|
||||
iUpper = p->aiRowEst[0];
|
||||
}else{
|
||||
/* Note: this call could be optimized away - since the same values must
|
||||
** have been requested when testing key $P in whereEqualScanEst(). */
|
||||
whereKeyStats(pParse, p, pRec, 0, a);
|
||||
iLower = a[0];
|
||||
iUpper = a[0] + a[1];
|
||||
}
|
||||
|
||||
/* If possible, improve on the iLower estimate using ($P:$L). */
|
||||
if( pLower ){
|
||||
int bOk; /* True if value is extracted from pExpr */
|
||||
Expr *pExpr = pLower->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
|
||||
){
|
||||
iLower = a[0];
|
||||
if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
tRowcnt iNew;
|
||||
whereKeyStats(pParse, p, pRec, 0, a);
|
||||
iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0);
|
||||
if( iNew>iLower ) iLower = iNew;
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
if( rc==SQLITE_OK && pUpper ){
|
||||
|
||||
/* If possible, improve on the iUpper estimate using ($P:$U). */
|
||||
if( pUpper ){
|
||||
int bOk; /* True if value is extracted from pExpr */
|
||||
Expr *pExpr = pUpper->pExpr->pRight;
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
|
||||
assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
|
||||
if( rc==SQLITE_OK
|
||||
&& whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
|
||||
){
|
||||
iUpper = a[0];
|
||||
if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
|
||||
if( rc==SQLITE_OK && bOk ){
|
||||
tRowcnt iNew;
|
||||
whereKeyStats(pParse, p, pRec, 1, a);
|
||||
iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0);
|
||||
if( iNew<iUpper ) iUpper = iNew;
|
||||
}
|
||||
sqlite3ValueFree(pRangeVal);
|
||||
}
|
||||
|
||||
pBuilder->pRec = pRec;
|
||||
if( rc==SQLITE_OK ){
|
||||
WhereCost iBase = whereCost(p->aiRowEst[0]);
|
||||
WhereCost nNew;
|
||||
if( iUpper>iLower ){
|
||||
iBase -= whereCost(iUpper - iLower);
|
||||
nNew = whereCost(iUpper - iLower);
|
||||
}else{
|
||||
nNew = 10; assert( 10==whereCost(2) );
|
||||
}
|
||||
*pRangeDiv = iBase;
|
||||
WHERETRACE(0x100, ("range scan regions: %u..%u div=%d\n",
|
||||
(u32)iLower, (u32)iUpper, *pRangeDiv));
|
||||
if( nNew<nOut ){
|
||||
nOut = nNew;
|
||||
}
|
||||
*pnOut = (WhereCost)nOut;
|
||||
WHERETRACE(0x100, ("range scan regions: %u..%u est=%d\n",
|
||||
(u32)iLower, (u32)iUpper, nOut));
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pParse);
|
||||
UNUSED_PARAMETER(p);
|
||||
UNUSED_PARAMETER(nEq);
|
||||
UNUSED_PARAMETER(pBuilder);
|
||||
#endif
|
||||
assert( pLower || pUpper );
|
||||
*pRangeDiv = 0;
|
||||
/* TUNING: Each inequality constraint reduces the search space 4-fold.
|
||||
** A BETWEEN operator, therefore, reduces the search space 16-fold */
|
||||
if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ){
|
||||
*pRangeDiv += 20; assert( 20==whereCost(4) );
|
||||
nOut -= 20; assert( 20==whereCost(4) );
|
||||
}
|
||||
if( pUpper ){
|
||||
*pRangeDiv += 20; assert( 20==whereCost(4) );
|
||||
nOut -= 20; assert( 20==whereCost(4) );
|
||||
}
|
||||
if( nOut<10 ) nOut = 10;
|
||||
*pnOut = (WhereCost)nOut;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
/*
|
||||
** Estimate the number of rows that will be returned based on
|
||||
** an equality constraint x=VALUE and where that VALUE occurs in
|
||||
@ -2721,37 +2665,53 @@ static int whereRangeScanEst(
|
||||
*/
|
||||
static int whereEqualScanEst(
|
||||
Parse *pParse, /* Parsing & code generating context */
|
||||
Index *p, /* The index whose left-most column is pTerm */
|
||||
WhereLoopBuilder *pBuilder,
|
||||
Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */
|
||||
tRowcnt *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
int nEq = pBuilder->pNew->u.btree.nEq;
|
||||
UnpackedRecord *pRec = pBuilder->pRec;
|
||||
u8 aff; /* Column affinity */
|
||||
int rc; /* Subfunction return code */
|
||||
tRowcnt a[2]; /* Statistics */
|
||||
int bOk;
|
||||
|
||||
assert( nEq>=1 );
|
||||
assert( nEq<=(p->nColumn+1) );
|
||||
assert( p->aSample!=0 );
|
||||
assert( p->nSample>0 );
|
||||
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
|
||||
if( pExpr ){
|
||||
rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
|
||||
if( rc ) goto whereEqualScanEst_cancel;
|
||||
}else{
|
||||
pRhs = sqlite3ValueNew(pParse->db);
|
||||
assert( pBuilder->nRecValid<nEq );
|
||||
|
||||
/* If values are not available for all fields of the index to the left
|
||||
** of this one, no estimate can be made. Return SQLITE_NOTFOUND. */
|
||||
if( pBuilder->nRecValid<(nEq-1) ){
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
if( pRhs==0 ) return SQLITE_NOTFOUND;
|
||||
rc = whereKeyStats(pParse, p, pRhs, 0, a);
|
||||
if( rc==SQLITE_OK ){
|
||||
WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1]));
|
||||
*pnRow = a[1];
|
||||
|
||||
/* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
|
||||
** below would return the same value. */
|
||||
if( nEq>p->nColumn ){
|
||||
*pnRow = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
whereEqualScanEst_cancel:
|
||||
sqlite3ValueFree(pRhs);
|
||||
|
||||
aff = p->pTable->aCol[p->aiColumn[nEq-1]].affinity;
|
||||
rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
|
||||
pBuilder->pRec = pRec;
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
if( bOk==0 ) return SQLITE_NOTFOUND;
|
||||
pBuilder->nRecValid = nEq;
|
||||
|
||||
whereKeyStats(pParse, p, pRec, 0, a);
|
||||
WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1]));
|
||||
*pnRow = a[1];
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_STAT3) */
|
||||
#endif /* defined(SQLITE_ENABLE_STAT4) */
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
/*
|
||||
** Estimate the number of rows that will be returned based on
|
||||
** an IN constraint where the right-hand side of the IN operator
|
||||
@ -2770,10 +2730,12 @@ whereEqualScanEst_cancel:
|
||||
*/
|
||||
static int whereInScanEst(
|
||||
Parse *pParse, /* Parsing & code generating context */
|
||||
Index *p, /* The index whose left-most column is pTerm */
|
||||
WhereLoopBuilder *pBuilder,
|
||||
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
|
||||
tRowcnt *pnRow /* Write the revised row estimate here */
|
||||
){
|
||||
Index *p = pBuilder->pNew->u.btree.pIndex;
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
int rc = SQLITE_OK; /* Subfunction return code */
|
||||
tRowcnt nEst; /* Number of rows for a single term */
|
||||
tRowcnt nRowEst = 0; /* New estimate of the number of rows */
|
||||
@ -2782,17 +2744,20 @@ static int whereInScanEst(
|
||||
assert( p->aSample!=0 );
|
||||
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
|
||||
nEst = p->aiRowEst[0];
|
||||
rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
|
||||
nRowEst += nEst;
|
||||
pBuilder->nRecValid = nRecValid;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
|
||||
*pnRow = nRowEst;
|
||||
WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst));
|
||||
}
|
||||
assert( pBuilder->nRecValid==nRecValid );
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_STAT3) */
|
||||
#endif /* defined(SQLITE_ENABLE_STAT4) */
|
||||
|
||||
/*
|
||||
** Disable a term in the WHERE clause. Except, do not disable the term
|
||||
@ -4337,12 +4302,17 @@ static int whereLoopAddBtreeIndex(
|
||||
rLogSize = estLog(whereCost(pProbe->aiRowEst[0]));
|
||||
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
|
||||
int nIn = 0;
|
||||
if( pTerm->prereqRight & pNew->maskSelf ) continue;
|
||||
if( (pTerm->eOperator==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
|
||||
&& (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
|
||||
){
|
||||
continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
int nRecValid = pBuilder->nRecValid;
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
if( (pTerm->wtFlags & TERM_VNULL)!=0 && pSrc->pTab->aCol[iCol].notNull ){
|
||||
continue; /* skip IS NOT NULL constraints on a NOT NULL column */
|
||||
}
|
||||
#endif
|
||||
if( pTerm->prereqRight & pNew->maskSelf ) continue;
|
||||
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
|
||||
pNew->wsFlags = saved_wsFlags;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
pNew->nLTerm = saved_nLTerm;
|
||||
@ -4399,25 +4369,30 @@ static int whereLoopAddBtreeIndex(
|
||||
}
|
||||
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
|
||||
/* Adjust nOut and rRun for STAT3 range values */
|
||||
WhereCost rDiv;
|
||||
whereRangeScanEst(pParse, pProbe, pNew->u.btree.nEq,
|
||||
pBtm, pTop, &rDiv);
|
||||
pNew->nOut = saved_nOut>rDiv+10 ? saved_nOut - rDiv : 10;
|
||||
assert( pNew->nOut==saved_nOut );
|
||||
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, &pNew->nOut);
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( pNew->u.btree.nEq==1 && pProbe->nSample
|
||||
&& OptimizationEnabled(db, SQLITE_Stat3) ){
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
if( nInMul==0
|
||||
&& pProbe->nSample
|
||||
&& pNew->u.btree.nEq<=pProbe->nSampleCol
|
||||
&& OptimizationEnabled(db, SQLITE_Stat3)
|
||||
){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
tRowcnt nOut = 0;
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
|
||||
testcase( pTerm->eOperator & WO_EQ );
|
||||
testcase( pTerm->eOperator & WO_ISNULL );
|
||||
rc = whereEqualScanEst(pParse, pProbe, pTerm->pExpr->pRight, &nOut);
|
||||
rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
|
||||
}else if( (pTerm->eOperator & WO_IN)
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_xIsSelect) ){
|
||||
rc = whereInScanEst(pParse, pProbe, pTerm->pExpr->x.pList, &nOut);
|
||||
&& !ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
|
||||
}
|
||||
assert( nOut==0 || rc==SQLITE_OK );
|
||||
if( nOut ) pNew->nOut = whereCost(nOut);
|
||||
if( nOut ){
|
||||
nOut = whereCost(nOut);
|
||||
pNew->nOut = MIN(nOut, saved_nOut);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
|
||||
@ -4434,6 +4409,10 @@ static int whereLoopAddBtreeIndex(
|
||||
){
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
|
||||
}
|
||||
pNew->nOut = saved_nOut;
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
pBuilder->nRecValid = nRecValid;
|
||||
#endif
|
||||
}
|
||||
pNew->prereq = saved_prereq;
|
||||
pNew->u.btree.nEq = saved_nEq;
|
||||
@ -4663,7 +4642,13 @@ static int whereLoopAddBtree(
|
||||
if( rc ) break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0);
|
||||
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_ENABLE_STAT3)
|
||||
sqlite3Stat4ProbeFree(pBuilder->pRec);
|
||||
pBuilder->nRecValid = 0;
|
||||
pBuilder->pRec = 0;
|
||||
#endif
|
||||
|
||||
/* If there was an INDEXED BY clause, then only that one index is
|
||||
** considered. */
|
||||
|
@ -847,7 +847,7 @@ do_test alter-14.2 {
|
||||
set system_table_list {1 sqlite_master}
|
||||
catchsql ANALYZE
|
||||
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
|
||||
ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 }
|
||||
ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
|
||||
|
||||
foreach {tn tbl} $system_table_list {
|
||||
do_test alter-15.$tn.1 {
|
||||
|
@ -143,6 +143,11 @@ do_test alter4-2.6 {
|
||||
alter table t1 add column d DEFAULT CURRENT_TIME;
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.7 {
|
||||
catchsql {
|
||||
alter table t1 add column d default (-+1);
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
|
@ -288,7 +288,7 @@ do_test analyze-4.3 {
|
||||
} {}
|
||||
|
||||
# Verify that DROP TABLE and DROP INDEX remove entries from the
|
||||
# sqlite_stat1 and sqlite_stat3 tables.
|
||||
# sqlite_stat1, sqlite_stat3 and sqlite_stat4 tables.
|
||||
#
|
||||
do_test analyze-5.0 {
|
||||
execsql {
|
||||
@ -306,12 +306,13 @@ do_test analyze-5.0 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
ifcapable stat4 {set stat sqlite_stat4} else {set stat sqlite_stat3}
|
||||
do_test analyze-5.1 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.2 {
|
||||
@ -321,12 +322,12 @@ do_test analyze-5.2 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test analyze-5.3 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.4 {
|
||||
@ -336,12 +337,12 @@ do_test analyze-5.4 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test analyze-5.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t4i1 t4i2 t4}
|
||||
}
|
||||
|
||||
@ -360,5 +361,4 @@ do_test analyze-99.1 {
|
||||
}
|
||||
} {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@ -95,7 +95,13 @@ do_test analyze3-1.1.1 {
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
|
||||
ifcapable stat4 {
|
||||
execsql { SELECT count(*)>0 FROM sqlite_stat4; }
|
||||
} else {
|
||||
execsql { SELECT count(*)>0 FROM sqlite_stat3; }
|
||||
}
|
||||
} {1}
|
||||
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
@ -312,7 +318,6 @@ do_test analyze3-3.1 {
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
|
||||
do_test analyze3-3.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
|
@ -10,14 +10,14 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests for SQLite library. The focus of the tests
|
||||
# in this file is the use of the sqlite_stat3 histogram data on tables
|
||||
# in this file is the use of the sqlite_stat4 histogram data on tables
|
||||
# with many repeated values and only a few distinct values.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@ -28,6 +28,17 @@ proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
proc alpha {blob} {
|
||||
set ret ""
|
||||
foreach c [split $blob {}] {
|
||||
if {[string is alpha $c]} {append ret $c}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db func alpha alpha
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
unset -nocomplain i t u v w x y z
|
||||
do_test analyze5-1.0 {
|
||||
db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)}
|
||||
@ -55,17 +66,40 @@ do_test analyze5-1.0 {
|
||||
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
|
||||
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
|
||||
ANALYZE;
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
SELECT DISTINCT lindex(test_decode(sample),0)
|
||||
FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
|
||||
do_test analyze5-1.1 {
|
||||
db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v'
|
||||
ORDER BY 1}
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
SELECT DISTINCT lower(lindex(test_decode(sample), 0))
|
||||
FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
SELECT lower(sample) FROM sqlite_stat3 WHERE idx='t1v' ORDER BY 1
|
||||
}
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
ifcapable stat4 {
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8}
|
||||
} else {
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
|
@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@ -82,14 +82,14 @@ do_test analyze7-3.1 {
|
||||
do_test analyze7-3.2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
|
||||
ifcapable stat3 {
|
||||
# If ENABLE_STAT3 is defined, SQLite comes up with a different estimated
|
||||
ifcapable stat4||stat3 {
|
||||
# If ENABLE_STAT4 is defined, SQLite comes up with a different estimated
|
||||
# row count for (c=2) than it does for (c=?).
|
||||
do_test analyze7-3.2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
|
||||
} else {
|
||||
# If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the
|
||||
# If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the
|
||||
# same as that for (c=?).
|
||||
do_test analyze7-3.2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
@ -98,7 +98,8 @@ ifcapable stat3 {
|
||||
do_test analyze7-3.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
|
||||
ifcapable {!stat3} {
|
||||
|
||||
ifcapable {!stat4 && !stat3} {
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
|
||||
|
@ -16,7 +16,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@ -84,7 +84,19 @@ do_test 2.1 {
|
||||
# There are many more values of c between 0 and 100000 than there are
|
||||
# between 800000 and 900000. So t1c is more selective for the latter
|
||||
# range.
|
||||
#
|
||||
# Test 3.2 is a little unstable. It depends on the planner estimating
|
||||
# that (b BETWEEN 50 AND 54) will match more rows than (c BETWEEN
|
||||
# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
|
||||
# the planner could get it wrong with an unlucky set of samples. This
|
||||
# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
|
||||
# will fail.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 50 AND 54;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
|
||||
} {50 376 32}
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
|
||||
|
426
test/analyze9.test
Normal file
426
test/analyze9.test
Normal file
@ -0,0 +1,426 @@
|
||||
# 2013 August 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains automated tests used to verify that the sqlite_stat4
|
||||
# functionality is working.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix analyze9
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc s {blob} {
|
||||
set ret ""
|
||||
binary scan $blob c* bytes
|
||||
foreach b $bytes {
|
||||
set t [binary format c $b]
|
||||
if {[string is print $t]} {
|
||||
append ret $t
|
||||
} else {
|
||||
append ret .
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db function s s
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a TEXT, b TEXT);
|
||||
INSERT INTO t1 VALUES('(0)', '(0)');
|
||||
INSERT INTO t1 VALUES('(1)', '(1)');
|
||||
INSERT INTO t1 VALUES('(2)', '(2)');
|
||||
INSERT INTO t1 VALUES('(3)', '(3)');
|
||||
INSERT INTO t1 VALUES('(4)', '(4)');
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ANALYZE;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
t1 i1 {1 1 1} {0 0 0} {0 0 0} {(0) (0) 1}
|
||||
t1 i1 {1 1 1} {1 1 1} {1 1 1} {(1) (1) 2}
|
||||
t1 i1 {1 1 1} {2 2 2} {2 2 2} {(2) (2) 3}
|
||||
t1 i1 {1 1 1} {3 3 3} {3 3 3} {(3) (3) 4}
|
||||
t1 i1 {1 1 1} {4 4 4} {4 4 4} {(4) (4) 5}
|
||||
}
|
||||
|
||||
if {[permutation] != "utf16"} {
|
||||
do_execsql_test 1.3 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
t1 i1 {1 1 1} {0 0 0} {0 0 0} ....(0)(0)
|
||||
t1 i1 {1 1 1} {1 1 1} {1 1 1} ....(1)(1).
|
||||
t1 i1 {1 1 1} {2 2 2} {2 2 2} ....(2)(2).
|
||||
t1 i1 {1 1 1} {3 3 3} {3 3 3} ....(3)(3).
|
||||
t1 i1 {1 1 1} {4 4 4} {4 4 4} ....(4)(4).
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This is really just to test SQL user function "test_decode".
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES('some text', 14, NULL);
|
||||
INSERT INTO t1 VALUES(22.0, NULL, x'656667');
|
||||
CREATE INDEX i1 ON t1(a, b, c);
|
||||
ANALYZE;
|
||||
SELECT test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
{22.0 NULL x'656667' 2}
|
||||
{{some text} 14 NULL 1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX i2 ON t2(a, b);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
do_test 3.2 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set a [expr $i / 10]
|
||||
set b [expr int(rand() * 15.0)]
|
||||
execsql { INSERT INTO t2 VALUES($a, $b) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
# Each value of "a" occurs exactly 10 times in the table.
|
||||
#
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT count(*) FROM t2 GROUP BY a;
|
||||
} [lrange [string repeat "10 " 100] 0 99]
|
||||
|
||||
# The first element in the "nEq" list of all samples should therefore be 10.
|
||||
#
|
||||
do_execsql_test 3.3.2 {
|
||||
ANALYZE;
|
||||
SELECT lindex(nEq, 0) FROM sqlite_stat4;
|
||||
} [lrange [string repeat "10 " 100] 0 23]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.4 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 1, 'one-a');
|
||||
INSERT INTO t1 VALUES(11, 1, 'one-b');
|
||||
INSERT INTO t1 VALUES(21, 1, 'one-c');
|
||||
INSERT INTO t1 VALUES(31, 1, 'one-d');
|
||||
INSERT INTO t1 VALUES(41, 1, 'one-e');
|
||||
INSERT INTO t1 VALUES(51, 1, 'one-f');
|
||||
INSERT INTO t1 VALUES(61, 1, 'one-g');
|
||||
INSERT INTO t1 VALUES(71, 1, 'one-h');
|
||||
INSERT INTO t1 VALUES(81, 1, 'one-i');
|
||||
INSERT INTO t1 VALUES(91, 1, 'one-j');
|
||||
INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1;
|
||||
INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
ANALYZE;
|
||||
SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60;
|
||||
} {three-d three-e three-f}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# These tests verify that the sample selection for stat4 appears to be
|
||||
# working as designed.
|
||||
#
|
||||
|
||||
reset_db
|
||||
db func lindex lindex
|
||||
db func lrange lrange
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(c, b, a);
|
||||
}
|
||||
|
||||
|
||||
proc insert_filler_rows_n {iStart args} {
|
||||
set A(-ncopy) 1
|
||||
set A(-nval) 1
|
||||
|
||||
foreach {k v} $args {
|
||||
if {[info exists A($k)]==0} { error "no such option: $k" }
|
||||
set A($k) $v
|
||||
}
|
||||
if {[llength $args] % 2} {
|
||||
error "option requires an argument: [lindex $args end]"
|
||||
}
|
||||
|
||||
for {set i 0} {$i < $A(-nval)} {incr i} {
|
||||
set iVal [expr $iStart+$i]
|
||||
for {set j 0} {$j < $A(-ncopy)} {incr j} {
|
||||
execsql { INSERT INTO t1 VALUES($iVal, $iVal, $iVal) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_test 4.1 {
|
||||
execsql { BEGIN }
|
||||
insert_filler_rows_n 0 -ncopy 10 -nval 19
|
||||
insert_filler_rows_n 20 -ncopy 1 -nval 100
|
||||
|
||||
execsql {
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'a');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'b');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'c');
|
||||
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 2, 'e');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 2, 'f');
|
||||
|
||||
INSERT INTO t1(c, b, a) VALUES(201, 3, 'g');
|
||||
INSERT INTO t1(c, b, a) VALUES(201, 4, 'h');
|
||||
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM sqlite_stat4;
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {24 297}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT
|
||||
neq,
|
||||
lrange(nlt, 0, 2),
|
||||
lrange(ndlt, 0, 2),
|
||||
lrange(test_decode(sample), 0, 2)
|
||||
FROM sqlite_stat4
|
||||
ORDER BY rowid LIMIT 16;
|
||||
} {
|
||||
{10 10 10 1} {0 0 0} {0 0 0} {0 0 0}
|
||||
{10 10 10 1} {10 10 10} {1 1 1} {1 1 1}
|
||||
{10 10 10 1} {20 20 20} {2 2 2} {2 2 2}
|
||||
{10 10 10 1} {30 30 30} {3 3 3} {3 3 3}
|
||||
{10 10 10 1} {40 40 40} {4 4 4} {4 4 4}
|
||||
{10 10 10 1} {50 50 50} {5 5 5} {5 5 5}
|
||||
{10 10 10 1} {60 60 60} {6 6 6} {6 6 6}
|
||||
{10 10 10 1} {70 70 70} {7 7 7} {7 7 7}
|
||||
{10 10 10 1} {80 80 80} {8 8 8} {8 8 8}
|
||||
{10 10 10 1} {90 90 90} {9 9 9} {9 9 9}
|
||||
{10 10 10 1} {100 100 100} {10 10 10} {10 10 10}
|
||||
{10 10 10 1} {110 110 110} {11 11 11} {11 11 11}
|
||||
{10 10 10 1} {120 120 120} {12 12 12} {12 12 12}
|
||||
{10 10 10 1} {130 130 130} {13 13 13} {13 13 13}
|
||||
{10 10 10 1} {140 140 140} {14 14 14} {14 14 14}
|
||||
{10 10 10 1} {150 150 150} {15 15 15} {15 15 15}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
SELECT
|
||||
neq,
|
||||
lrange(nlt, 0, 2),
|
||||
lrange(ndlt, 0, 2),
|
||||
lrange(test_decode(sample), 0, 1)
|
||||
FROM sqlite_stat4
|
||||
ORDER BY rowid DESC LIMIT 2;
|
||||
} {
|
||||
{2 1 1 1} {295 296 296} {120 122 125} {201 4}
|
||||
{5 3 1 1} {290 290 292} {119 119 121} {200 1}
|
||||
}
|
||||
|
||||
do_execsql_test 4.4 { SELECT count(DISTINCT c) FROM t1 WHERE c<201 } 120
|
||||
do_execsql_test 4.5 { SELECT count(DISTINCT c) FROM t1 WHERE c<200 } 119
|
||||
|
||||
# Check that the perioidic samples are present.
|
||||
do_execsql_test 4.6 {
|
||||
SELECT count(*) FROM sqlite_stat4
|
||||
WHERE lindex(test_decode(sample), 3) IN
|
||||
('34', '68', '102', '136', '170', '204', '238', '272')
|
||||
} {8}
|
||||
|
||||
reset_db
|
||||
do_test 4.7 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(o,t INTEGER PRIMARY KEY);
|
||||
CREATE INDEX i1 ON t1(o);
|
||||
}
|
||||
for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} {
|
||||
execsql { INSERT INTO t1 VALUES('x', $i) }
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM sqlite_stat4;
|
||||
}
|
||||
} {8}
|
||||
do_execsql_test 4.8 {
|
||||
SELECT test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
{x 211} {x 423} {x 635} {x 847}
|
||||
{x 1590} {x 3710} {x 5830} {x 7950}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following would cause a crash at one point.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
PRAGMA encoding = 'utf-16';
|
||||
CREATE TABLE t0(v);
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This was also crashing (corrupt sqlite_stat4 table).
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
ANALYZE;
|
||||
PRAGMA writable_schema = 1;
|
||||
CREATE TEMP TABLE x1 AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat4
|
||||
ORDER BY (rowid%5), rowid;
|
||||
DELETE FROM sqlite_stat4;
|
||||
INSERT INTO sqlite_stat4 SELECT * FROM x1;
|
||||
PRAGMA writable_schema = 0;
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
do_execsql_test 6.2 {
|
||||
SELECT * FROM t1 WHERE a = 'abc';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests experiment with adding corrupted records to the
|
||||
# 'sample' column of the sqlite_stat4 table.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET sample = X'' WHERE rowid = 1;
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
UPDATE sqlite_stat4 SET sample = X'FFFF';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
} {1 1}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET neq = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
} {1 1}
|
||||
|
||||
do_execsql_test 7.4 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET ndlt = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 3;
|
||||
} {3 3}
|
||||
|
||||
do_execsql_test 7.5 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET nlt = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 5;
|
||||
} {5 5}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.1 {
|
||||
CREATE TABLE t1(x TEXT);
|
||||
CREATE INDEX i1 ON t1(x);
|
||||
INSERT INTO t1 VALUES('1');
|
||||
INSERT INTO t1 VALUES('2');
|
||||
INSERT INTO t1 VALUES('3');
|
||||
INSERT INTO t1 VALUES('4');
|
||||
ANALYZE;
|
||||
}
|
||||
do_execsql_test 8.2 {
|
||||
SELECT * FROM t1 WHERE x = 3;
|
||||
} {3}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the bug fixed by [91733bc485] really is fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t1(a, b, c, d, e);
|
||||
CREATE INDEX i1 ON t1(a, b, c, d);
|
||||
CREATE INDEX i2 ON t1(e);
|
||||
}
|
||||
do_test 9.2 {
|
||||
execsql BEGIN;
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
|
||||
}
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', 101, $i)"
|
||||
}
|
||||
for {set i 102} {$i < 200} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
|
||||
do_eqp_test 9.3.1 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=101 AND e=5;
|
||||
} {/t1 USING INDEX i2/}
|
||||
do_eqp_test 9.3.2 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=99 AND e=5;
|
||||
} {/t1 USING INDEX i1/}
|
||||
|
||||
set value_d [expr 101]
|
||||
do_eqp_test 9.4.1 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
|
||||
} {/t1 USING INDEX i2/}
|
||||
set value_d [expr 99]
|
||||
do_eqp_test 9.4.2 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
|
||||
} {/t1 USING INDEX i1/}
|
||||
|
||||
finish_test
|
||||
|
167
test/analyzeA.test
Normal file
167
test/analyzeA.test
Normal file
@ -0,0 +1,167 @@
|
||||
# 2013 August 3
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains automated tests used to verify that the current build
|
||||
# (which must be either ENABLE_STAT3 or ENABLE_STAT4) works with both stat3
|
||||
# and stat4 data.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix analyzeA
|
||||
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Populate the stat3 table according to the current contents of the db
|
||||
#
|
||||
proc populate_stat3 {{bDropTable 1}} {
|
||||
# Open a second connection on database "test.db" and run ANALYZE. If this
|
||||
# is an ENABLE_STAT3 build, this is all that is required to create and
|
||||
# populate the sqlite_stat3 table.
|
||||
#
|
||||
sqlite3 db2 test.db
|
||||
execsql { ANALYZE }
|
||||
|
||||
# Now, if this is an ENABLE_STAT4 build, create and populate the
|
||||
# sqlite_stat3 table based on the stat4 data gathered by the ANALYZE
|
||||
# above. Then drop the sqlite_stat4 table.
|
||||
#
|
||||
ifcapable stat4 {
|
||||
db2 func lindex lindex
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
CREATE TABLE sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);
|
||||
INSERT INTO sqlite_stat3
|
||||
SELECT DISTINCT tbl, idx,
|
||||
lindex(neq,0), lindex(nlt,0), lindex(ndlt,0), test_extract(sample, 0)
|
||||
FROM sqlite_stat4;
|
||||
} db2
|
||||
if {$bDropTable} { execsql {DROP TABLE sqlite_stat4} db2 }
|
||||
execsql { PRAGMA writable_schema = off }
|
||||
}
|
||||
|
||||
# Modify the database schema cookie to ensure that the other connection
|
||||
# reloads the schema.
|
||||
#
|
||||
execsql {
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Populate the stat4 table according to the current contents of the db
|
||||
#
|
||||
proc populate_stat4 {{bDropTable 1}} {
|
||||
sqlite3 db2 test.db
|
||||
execsql { ANALYZE }
|
||||
|
||||
ifcapable stat3 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample);
|
||||
INSERT INTO sqlite_stat4
|
||||
SELECT tbl, idx, neq, nlt, ndlt, sqlite_record(sample)
|
||||
FROM sqlite_stat3;
|
||||
} db2
|
||||
if {$bDropTable} { execsql {DROP TABLE sqlite_stat3} db2 }
|
||||
execsql { PRAGMA writable_schema = off }
|
||||
}
|
||||
|
||||
# Modify the database schema cookie to ensure that the other connection
|
||||
# reloads the schema.
|
||||
#
|
||||
execsql {
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Populate the stat4 table according to the current contents of the db.
|
||||
# Leave deceptive data in the stat3 table. This data should be ignored
|
||||
# in favour of that from the stat4 table.
|
||||
#
|
||||
proc populate_both {} {
|
||||
ifcapable stat4 { populate_stat3 0 }
|
||||
ifcapable stat3 { populate_stat4 0 }
|
||||
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
UPDATE sqlite_stat3 SET idx =
|
||||
CASE idx WHEN 't1b' THEN 't1c' ELSE 't1b'
|
||||
END;
|
||||
PRAGMA writable_schema = off;
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
foreach {tn analyze_cmd} {
|
||||
1 populate_stat4
|
||||
2 populate_stat3
|
||||
3 populate_both
|
||||
} {
|
||||
reset_db
|
||||
do_test 1.$tn.1 {
|
||||
execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set c [expr int(pow(1.1,$i)/100)]
|
||||
set b [expr 125 - int(pow(1.1,99-$i))/100]
|
||||
execsql {INSERT INTO t1 VALUES($i, $b, $c)}
|
||||
}
|
||||
} {}
|
||||
|
||||
execsql { CREATE INDEX t1b ON t1(b) }
|
||||
execsql { CREATE INDEX t1c ON t1(c) }
|
||||
$analyze_cmd
|
||||
|
||||
do_execsql_test 1.$tn.2.1 { SELECT count(*) FROM t1 WHERE b=31 } 1
|
||||
do_execsql_test 1.$tn.2.2 { SELECT count(*) FROM t1 WHERE c=0 } 49
|
||||
do_execsql_test 1.$tn.2.3 { SELECT count(*) FROM t1 WHERE b=125 } 49
|
||||
do_execsql_test 1.$tn.2.4 { SELECT count(*) FROM t1 WHERE c=16 } 1
|
||||
|
||||
do_eqp_test 1.$tn.2.5 {
|
||||
SELECT * FROM t1 WHERE b = 31 AND c = 0;
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
|
||||
do_eqp_test 1.$tn.2.6 {
|
||||
SELECT * FROM t1 WHERE b = 125 AND c = 16;
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)}}
|
||||
|
||||
do_execsql_test 1.$tn.3.1 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 0 AND 50
|
||||
} {6}
|
||||
do_execsql_test 1.$tn.3.2 {
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 50
|
||||
} {90}
|
||||
do_execsql_test 1.$tn.3.3 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 75 AND 125
|
||||
} {90}
|
||||
do_execsql_test 1.$tn.3.4 {
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 75 AND 125
|
||||
} {6}
|
||||
|
||||
do_eqp_test 1.$tn.3.5 {
|
||||
SELECT * FROM t1 WHERE b BETWEEN 0 AND 50 AND c BETWEEN 0 AND 50
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
|
||||
|
||||
do_eqp_test 1.$tn.3.6 {
|
||||
SELECT * FROM t1 WHERE b BETWEEN 75 AND 125 AND c BETWEEN 75 AND 125
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -2325,10 +2325,14 @@ ifcapable compound&&subquery {
|
||||
}
|
||||
}
|
||||
}
|
||||
ifcapable stat3 {
|
||||
set stat3 "sqlite_stat3 "
|
||||
ifcapable stat4 {
|
||||
set stat4 "sqlite_stat4 "
|
||||
} else {
|
||||
set stat3 ""
|
||||
ifcapable stat3 {
|
||||
set stat4 "sqlite_stat3 "
|
||||
} else {
|
||||
set stat4 ""
|
||||
}
|
||||
}
|
||||
do_test auth-5.2 {
|
||||
execsql {
|
||||
@ -2337,7 +2341,7 @@ ifcapable compound&&subquery {
|
||||
WHERE type='table'
|
||||
ORDER BY name
|
||||
}
|
||||
} "sqlite_stat1 ${stat3}t1 t2 t3 t4"
|
||||
} "sqlite_stat1 ${stat4}t1 t2 t3 t4"
|
||||
}
|
||||
|
||||
# Ticket #3944
|
||||
|
@ -61,7 +61,7 @@ proc lookaside {db} {
|
||||
}
|
||||
}
|
||||
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
set STAT3 1
|
||||
} else {
|
||||
set STAT3 0
|
||||
@ -214,7 +214,7 @@ foreach ::lookaside_buffer_size {0 64 120} {
|
||||
# much greater than just that reported by DBSTATUS_SCHEMA_USED in this
|
||||
# case.
|
||||
#
|
||||
# Some of the memory used for sqlite_stat3 is unaccounted for by
|
||||
# Some of the memory used for sqlite_stat4 is unaccounted for by
|
||||
# dbstatus.
|
||||
#
|
||||
# Finally, on osx the estimate of memory used by the schema may be
|
||||
|
@ -149,15 +149,15 @@ do_test index6-2.2 {
|
||||
SELECT * FROM t2 WHERE a=5;
|
||||
}
|
||||
} {/.* TABLE t2 USING INDEX t2a1 .*/}
|
||||
ifcapable stat3 {
|
||||
do_test index6-2.3stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test index6-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {/.* TABLE t2 USING INDEX t2a1 .*/}
|
||||
} else {
|
||||
do_test index6-2.3stat3 {
|
||||
do_test index6-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL AND a>0;
|
||||
|
@ -15,6 +15,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix mallocA
|
||||
|
||||
# Only run these tests if memory debugging is turned on.
|
||||
#
|
||||
@ -40,7 +41,6 @@ db eval {
|
||||
db close
|
||||
copy_file test.db test.db.bu
|
||||
|
||||
|
||||
do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody {
|
||||
ANALYZE
|
||||
}
|
||||
@ -53,6 +53,7 @@ do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody {
|
||||
do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody {
|
||||
ANALYZE main.t1
|
||||
}
|
||||
|
||||
ifcapable reindex {
|
||||
do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody {
|
||||
REINDEX;
|
||||
@ -68,6 +69,35 @@ ifcapable reindex {
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
do_execsql_test 6-prep {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1
|
||||
INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2
|
||||
INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3
|
||||
INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4
|
||||
|
||||
INSERT INTO t1 VALUES('def', 'w'); -- rowid=5
|
||||
INSERT INTO t1 VALUES('def', 'x'); -- rowid=6
|
||||
INSERT INTO t1 VALUES('def', 'y'); -- rowid=7
|
||||
INSERT INTO t1 VALUES('def', 'z'); -- rowid=8
|
||||
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_faultsim_test 6.1 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 2]
|
||||
}
|
||||
do_faultsim_test 6.2 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 {1 2}]
|
||||
}
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
@ -500,6 +500,8 @@ test_suite "utf16" -description {
|
||||
pragma encoding = 'UTF-16'
|
||||
} -files {
|
||||
alter.test alter3.test
|
||||
analyze.test analyze3.test analyze4.test analyze5.test analyze6.test
|
||||
analyze7.test analyze8.test analyze9.test analyzeA.test
|
||||
auth.test bind.test blob.test capi2.test capi3.test collate1.test
|
||||
collate2.test collate3.test collate4.test collate5.test collate6.test
|
||||
conflict.test date.test delete.test expr.test fkey1.test func.test
|
||||
|
@ -268,6 +268,7 @@ do_test table-5.2.1 {
|
||||
DROP TABLE IF EXISTS sqlite_stat1;
|
||||
DROP TABLE IF EXISTS sqlite_stat2;
|
||||
DROP TABLE IF EXISTS sqlite_stat3;
|
||||
DROP TABLE IF EXISTS sqlite_stat4;
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_stat*';
|
||||
}
|
||||
} {}
|
||||
|
@ -16,11 +16,26 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc s {blob} {
|
||||
set ret ""
|
||||
binary scan $blob c* bytes
|
||||
foreach b $bytes {
|
||||
set t [binary format c $b]
|
||||
if {[string is print $t]} {
|
||||
append ret $t
|
||||
} else {
|
||||
append ret .
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db function s s
|
||||
|
||||
do_test tkt-cbd05-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT UNIQUE NOT NULL);
|
||||
@ -39,18 +54,30 @@ do_test tkt-cbd05-1.1 {
|
||||
}
|
||||
} {10}
|
||||
do_test tkt-cbd05-1.2 {
|
||||
db eval {
|
||||
ANALYZE;
|
||||
db eval { ANALYZE; }
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
PRAGMA writable_schema = 1;
|
||||
CREATE VIEW vvv AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,test_extract(sample,0) AS sample
|
||||
FROM sqlite_stat4;
|
||||
PRAGMA writable_schema = 0;
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
CREATE VIEW vvv AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat3;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_test tkt-cbd05-1.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat3
|
||||
SELECT tbl,idx,group_concat(s(sample),' ')
|
||||
FROM vvv
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {/t1 t1_x .[ ABCDEFGHI]{10}./}
|
||||
} {t1 t1_x { A B C D E F G H I}}
|
||||
|
||||
do_test tkt-cbd05-2.1 {
|
||||
db eval {
|
||||
@ -77,11 +104,11 @@ do_test tkt-cbd05-2.2 {
|
||||
} {}
|
||||
do_test tkt-cbd05-2.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat3
|
||||
SELECT tbl,idx,group_concat(s(sample),' ')
|
||||
FROM vvv
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {/t1 t1_x .[ ABCDEFGHI]{10}./}
|
||||
} {t1 t1_x { A B C D E F G H I}}
|
||||
|
||||
finish_test
|
||||
|
@ -781,11 +781,11 @@ do_test where9-6.8.2 {
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
}
|
||||
} {1 {no query solution}}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
# When STAT3 is enabled, the "b NOT NULL" terms get translated
|
||||
# into b>NULL, which can be satified by the index t1b. It is a very
|
||||
# expensive way to do the query, but it works, and so a solution is possible.
|
||||
do_test where9-6.8.3-stat3 {
|
||||
do_test where9-6.8.3-stat4 {
|
||||
catchsql {
|
||||
UPDATE t1 INDEXED BY t1b SET a=a+100
|
||||
WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
@ -793,7 +793,7 @@ ifcapable stat3 {
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
}
|
||||
} {0 {}}
|
||||
do_test where9-6.8.4-stat3 {
|
||||
do_test where9-6.8.4-stat4 {
|
||||
catchsql {
|
||||
DELETE FROM t1 INDEXED BY t1b
|
||||
WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
@ -851,6 +851,11 @@ do_test where9-7.0 {
|
||||
INSERT INTO t6 SELECT * FROM t5;
|
||||
ANALYZE t5;
|
||||
}
|
||||
ifcapable stat3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { DROP TABLE IF EXISTS sqlite_stat3 }
|
||||
db2 close
|
||||
}
|
||||
} {}
|
||||
do_test where9-7.1.1 {
|
||||
count_steps {
|
||||
|
@ -42,6 +42,10 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# TODO: Reenable this test.
|
||||
finish_test
|
||||
return
|
||||
|
||||
ifcapable !stat3 {
|
||||
finish_test
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user