Add support for WITHOUT ROWID tables. This change also includes
(1) standardization of the error message returned from run-time constraint errors, (2) improved EXPLAIN comments, (3) the SQLITE_ENABLE_EXPLAIN_COMMENTS option, (4) the SQLITE_ENABLE_MODULE_COMMENTS option, and (5) a bug fix (see [573cc27427]) in the handling of REPLACE on the rowid when secondary indices use FAIL or IGNORE. FossilOrigin-Name: c80e229dd9c1230abefbc707d4bf0b24315c6bb5
This commit is contained in:
commit
b0e503409d
173
manifest
173
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\sbug\spreventing\sFTS\sfrom\scorrectly\sprocessing\sbracket\stokens\sthat\sare\simmediately\spreceded\sby\scharacters\sthat\sare\sneither\swhitespace\sor\stoken\scharacters.
|
||||
D 2013-11-04T08:56:22.163
|
||||
C Add\ssupport\sfor\sWITHOUT\sROWID\stables.\s\sThis\schange\salso\sincludes\n(1)\sstandardization\sof\sthe\serror\smessage\sreturned\sfrom\srun-time\sconstraint\nerrors,\s(2)\simproved\sEXPLAIN\scomments,\s(3)\sthe\sSQLITE_ENABLE_EXPLAIN_COMMENTS\noption,\s(4)\sthe\sSQLITE_ENABLE_MODULE_COMMENTS\soption,\sand\s(5)\sa\sbug\sfix\n(see\s[573cc27427])\sin\sthe\shandling\sof\sREPLACE\son\sthe\srowid\swhen\ssecondary\nindices\suse\sFAIL\sor\sIGNORE.
|
||||
D 2013-11-07T16:08:10.334
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -159,35 +159,35 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083
|
||||
F src/analyze.c f1c5ed1fe128c3f106dcd95e97ee9ef94db7a3fa
|
||||
F src/analyze.c 27f0c132aa0679189837e0addf8762e7fd6831b6
|
||||
F src/attach.c 0a17c9364895316ca4f52d06a97a72c0af1ae8b3
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 509722ce305471b626d3401c0631a808fd33237b
|
||||
F src/btree.c 260dedc13119e6fb7930380bd3d294b98362bf5a
|
||||
F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf
|
||||
F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
|
||||
F src/build.c 6d9a545d726956fdc0c63d7076291fc9e7207484
|
||||
F src/build.c d4d5ceedf196a7da36d6dd15f4d073cb68c8f079
|
||||
F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
|
||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||
F src/delete.c 45788c5e48734f2af4acd75a876466e5b9838e34
|
||||
F src/expr.c e7338ccffdc391c53ba2d51c5eb6a2f5299e040e
|
||||
F src/delete.c fb896d68bae9a5ee3459c60d8ed929a2b41f7afb
|
||||
F src/expr.c e7bbe3c6916e141f27a28655d3cf325b817695e4
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 5dc10cbaa355753903cd2a64da040f948997ebf8
|
||||
F src/fkey.c 5370840745f01f11fb219922d8e99a48ff92fbeb
|
||||
F src/func.c 2c47b65e6e00e3e9374942f28254faf8adafe398
|
||||
F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
|
||||
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
|
||||
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c 9a3f578ffe37f18ef695a06764320db5dc1f0011
|
||||
F src/insert.c 325ef76ae5c7a4a9da1beade47e1bd314f22d6d5
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
|
||||
F src/main.c 35931467ec026b05babb279cb8a573e62f6fe1a3
|
||||
F src/main.c 32bf1e6e164a6fa0ddf1bf2616c6eafbefd6e9b0
|
||||
F src/malloc.c 543a8eb5508eaf4cadf55a9b503379eba2088128
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa
|
||||
@ -208,22 +208,22 @@ F src/os_unix.c 143624d9eabb3b997c59cf594e0d06c56edd43e9
|
||||
F src/os_win.c b159b5249d9f70607d961bbdd1dbba789c75812c
|
||||
F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8
|
||||
F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c
|
||||
F src/parse.y a97566d6da75075589a7c716d1bda14b586cf8da
|
||||
F src/parse.y 073a8294e1826f1b1656e84806b77e4199f4bb57
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
|
||||
F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63
|
||||
F src/pragma.c 6fb3125fff078cd81db0039ac778948df4e8cb6f
|
||||
F src/pragma.c 3b7b766382ac679d3c1a7ba368aa008f9a756d59
|
||||
F src/prepare.c fa6988589f39af8504a61731614cd4f6ae71554f
|
||||
F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b
|
||||
F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68
|
||||
F src/resolve.c 572585a96bf282bb9c3d9e08785ec3cae21dc488
|
||||
F src/resolve.c e729889b2c7a680ba4aa7296efa72c09369956d8
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c 15127b54cc11defb2cddef6914e1f384501a61c4
|
||||
F src/select.c 60af46f5f6bc3803c27af3edd8881ed2d0c3f19f
|
||||
F src/shell.c 03d8d9b4052430343ff30d646334621f980f1202
|
||||
F src/sqlite.h.in 547a44dd4ff4d975e92a645ea2d609e543a83d0f
|
||||
F src/sqlite.h.in a8cad179541b8d171fed425a737084702ef462ef
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 6eff1bbd7e687510c4d5a2aea915b50a51001eb2
|
||||
F src/sqliteInt.h 360c8a484065f6b52ecdd5ef6766429e7aa552dd
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@ -273,32 +273,32 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
|
||||
F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9
|
||||
F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2
|
||||
F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4
|
||||
F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716
|
||||
F src/update.c f5182157f5d0d0a97bc5f5e3c9bdba0dfbe08f08
|
||||
F src/update.c 516e0ea0f853bfb852e098f3b643a3f2bfd423ef
|
||||
F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
|
||||
F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918
|
||||
F src/vacuum.c f313bc97123a4dd4bfd3f50a00c4d44c08a5b1b7
|
||||
F src/vdbe.c de4018a314991d41f9b8fb68a17a04471076675d
|
||||
F src/vdbe.h 6bdee35c54d57fd52733d4c542781820009311dc
|
||||
F src/vdbeInt.h ff57f67aee1ba26a3a47e786533dab155ab6dad6
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c ff0170cd3c5fea68386818cbd19a23bbfc711364
|
||||
F src/vdbe.h 8d5a7351024d80374fc0acdbbe3cfe65c51ba8b6
|
||||
F src/vdbeInt.h f2fa3ceccceeb757773921fb08af7c6e9f3caa1c
|
||||
F src/vdbeapi.c 93a22a9ba2abe292d5c2cf304d7eb2e894dde0ed
|
||||
F src/vdbeaux.c 1cbff05cb4c6618d805b4d4efc15d31af0f47f0d
|
||||
F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69
|
||||
F src/vdbemem.c 649933bad3e922465b726eaf85c72a75acba2ab7
|
||||
F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017
|
||||
F src/vdbeaux.c faf5c3c9b1b0d2573d652856dc6e70f8adc86f59
|
||||
F src/vdbeblob.c ff60adf2953e0ffc1d59541b3101c9886b03a3de
|
||||
F src/vdbemem.c cc529bbf4f13e4e181bdb446bf6e6962ab030b4b
|
||||
F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147
|
||||
F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc
|
||||
F src/vtab.c 5a423b042eb1402ef77697d03d6a67378d97bc8d
|
||||
F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74
|
||||
F src/where.c f18400f121fd27385f41cdff7aa8a811feca842e
|
||||
F src/where.c 1a99f1275fb5db2d76b0093ae1c1936c3b036aa0
|
||||
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 775a1dded3f8247983c9a3e767b011d9a5114442
|
||||
F test/alter.test e88dfa77e020c2b48e52a8020c70171ab828e079
|
||||
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
|
||||
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
|
||||
F test/alter4.test 8e93bf7a7e6919b14b0c9a6c1e4908bcf21b0165
|
||||
@ -330,10 +330,10 @@ F test/auth2.test c3b415b76c033bedb81292118fb7c01f5f10cbcd
|
||||
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
|
||||
F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
|
||||
F test/autoindex1.test d4dfe14001dfcb74cfbd7107f45a79fc1ab6183e
|
||||
F test/autovacuum.test 9f22a7733f39c56ef6a5665d10145ac25d8cb574
|
||||
F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
F test/backcompat.test ecd841f3a3bfb81518721879cc56a760670e3198
|
||||
F test/backcompat.test 5f8ad58b3eaebc78cd2c66c65476a42e6f32b2ad
|
||||
F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62
|
||||
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup4.test 2a2e4a64388090b152de753fd9e123f28f6a3bd4
|
||||
@ -361,21 +361,21 @@ F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
|
||||
F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
|
||||
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
||||
F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
|
||||
F test/capi2.test e8b18cc61090b6e5e388f54d6b125d711d1b265a
|
||||
F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738
|
||||
F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa
|
||||
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0
|
||||
F test/capi3d.test 6d0fc0a86d73f42dd19a7d8b7761ab9bc02277d0
|
||||
F test/capi3e.test ad90088b18b0367125ff2d4b5400153fd2f99aab
|
||||
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
|
||||
F test/check.test 1e9be446eb0bbd47a5f65955802e9632425096ab
|
||||
F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
|
||||
F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815
|
||||
F test/closure01.test dbb28f1ea9eeaf0a53ec5bc0fed352e479def8c7
|
||||
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
|
||||
F test/collate1.test b709989e6e6ff6e1d2bd64231c2c1d8146846c9e
|
||||
F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
|
||||
F test/collate3.test 79558a286362cb9ed603c6fa543f1cda7f563f0f
|
||||
F test/collate4.test 031f7265c13308b724ba3c49f41cc04612bd92b1
|
||||
F test/collate4.test f04d5168685f2eef637ecfa2d4ddf8ec0d600177
|
||||
F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6
|
||||
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
|
||||
F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868
|
||||
@ -384,7 +384,9 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
|
||||
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
|
||||
F test/colmeta.test 087c42997754b8c648819832241daf724f813322
|
||||
F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
|
||||
F test/conflict.test 0b3922d2304a14a47e3ccd61bbd6824327af659b
|
||||
F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8
|
||||
F test/conflict2.test 1892e796cb631d9b5661ab9916de3542520a1725
|
||||
F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b
|
||||
F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
|
||||
F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e
|
||||
F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0
|
||||
@ -429,19 +431,19 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 44028aaf161a5e80a2f229622b3a174d3b352810
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test ee9b70a38369ae44360febb411976aa3c8cf2689
|
||||
F test/e_createtable.test f1d21fa142391917b1fa84cf985ac6c63dfcbe4d
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
|
||||
F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
|
||||
F test/e_expr.test d5cdda0e4ffb17760858ed4c7c4ece07efc40f71
|
||||
F test/e_fkey.test 17cfb40002d165299681f39aac0cb5890c359935
|
||||
F test/e_fkey.test d83a04478bb9c02d2c513518548a69f818869f41
|
||||
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
|
||||
F test/e_insert.test 291e056e1a442a5e5166a989a8a03a46e38225ca
|
||||
F test/e_reindex.test e175794fc41f8e8aef34772e87a7d7b7a9251dd3
|
||||
F test/e_insert.test 1e44f84d2abe44d66e4fbf198be4b20e3cc724a0
|
||||
F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589
|
||||
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test d3226cb94fae4af3f198e68e71f655e106d0be47
|
||||
F test/e_select2.test 22c660a7becf0712c95e1ca1b2d9e716a1261460
|
||||
F test/e_update.test bea00499e43ee1da77b03cdb0b20c7c864c1ec5a
|
||||
F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
|
||||
F test/e_uri.test a2c92d80093a7efdcfbb11093651cbea87097b6b
|
||||
F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
@ -449,7 +451,7 @@ F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
|
||||
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
|
||||
F test/eqp.test 57c6c604c2807fb5531731c5323133453c24afac
|
||||
F test/errmsg.test 050717f1c6a5685de9c79f5f9f6b83d7c592f73a
|
||||
F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
|
||||
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
|
||||
F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75
|
||||
F test/exclusive2.test 881193eccec225cfed9d7f744b65e57f26adee25
|
||||
@ -460,11 +462,11 @@ F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
|
||||
F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a
|
||||
F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146
|
||||
F test/fkey1.test e1d1fa84cde579185ea01358436839703e415a5b
|
||||
F test/fkey2.test 06e0b4cc9e1b3271ae2ae6feeb19755468432111
|
||||
F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
|
||||
F test/fkey2.test 32ca728bcb854feed72d1406ea375fe423eebff2
|
||||
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
|
||||
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
|
||||
F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
|
||||
F test/fkey6.test 18e8c024d39f68f8d4bb1177c992bcffdc273464
|
||||
F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
|
||||
F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
|
||||
F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51
|
||||
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
|
||||
@ -571,7 +573,7 @@ F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
||||
F test/func.test c7e80a44eebac8604397eb2ad83d0d5d9d541237
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
F test/func3.test dbccee9133cfef1473c59ec07b5f0262b9d72f9a
|
||||
F test/func4.test cb3f888a1cafad195a1f8d396875bdb11dc4faf9
|
||||
F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f
|
||||
F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74
|
||||
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
|
||||
F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
|
||||
@ -583,13 +585,13 @@ F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
F test/hook.test 45cb22b940c3cc0af616ba7430f666e245711a48
|
||||
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3
|
||||
F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||
F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3
|
||||
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
|
||||
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
|
||||
F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
|
||||
F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
|
||||
F test/incrblob4.test f26502a5697893e5acea268c910f16478c2f0fab
|
||||
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
|
||||
@ -598,24 +600,25 @@ F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
|
||||
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
|
||||
F test/incrvacuum3.test 75256fb1377e7c39ef2de62bfc42bbff67be295a
|
||||
F test/incrvacuum_ioerr.test 6ae2f783424e47a0033304808fe27789cf93e635
|
||||
F test/index.test f2abacfb83d384ae36b8a919fbd94b1319333e55
|
||||
F test/index.test 4d990005a67a36984e4f1a5f1bdccea8d08da4ee
|
||||
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
|
||||
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
|
||||
F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
|
||||
F test/index3.test 55a90cff99834305e8141df7afaef39674b57062
|
||||
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
||||
F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33
|
||||
F test/index6.test 9996f064672c03e768e256e4bf7cff4b63e8b109
|
||||
F test/index6.test 936979c3a1e87b81feaed2d00505665bf142d764
|
||||
F test/index7.test a3baf9a625bda7fd49471e99aeae04095fbfeecf
|
||||
F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2
|
||||
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
|
||||
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
|
||||
F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
|
||||
F test/insert4.test b00ddf82d6d4f0a6f3999f42bb6a3c813ea70707
|
||||
F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
|
||||
F test/instr.test 737bbf80685232033f3abedc6ae92f75860b5dd2
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc
|
||||
F test/intpkey.test a9674fc6195e0952e4e6105a9981ce1e48e7f215
|
||||
F test/intpkey.test 7506090fc08e028712a8bf47e5f54111947e3844
|
||||
F test/io.test 3a7abcef18727cc0f2399e04b0e8903eccae50f8
|
||||
F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8
|
||||
F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
|
||||
@ -677,7 +680,7 @@ F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
|
||||
F test/malloc_common.tcl 58e54229c4132ef882a11fab6419ec4cd3073589
|
||||
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
|
||||
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
|
||||
F test/memdb.test db5260330676de007be8530d6ecc7c9ab2b06ad3
|
||||
F test/memdb.test fcb5297b321b562084fc79d64d5a12a1cd2b639b
|
||||
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
|
||||
F test/memsubsys1.test f97cfd0b30e85c2f1ed16d642e7ac58006be84b2
|
||||
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
@ -685,9 +688,9 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
|
||||
F test/misc1.test 889b40722442380a2f6575f30831b32b2372d70e
|
||||
F test/misc1.test 9bed1bd334065a57dc841cff969d4fc1eeb6d49b
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
|
||||
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
@ -706,7 +709,7 @@ F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
|
||||
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
|
||||
F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c
|
||||
F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
|
||||
F test/notnull.test 2afad748d18fd66d01f66463de73b3e2501fb226
|
||||
F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
@ -714,9 +717,9 @@ F test/orderby1.test 9b524aff9147288da43a6d7ddfdcff47fa2303c6
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
|
||||
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
|
||||
F test/orderby5.test 02eca502a0f97c77ce383b0dfa17e99c6a107b8d
|
||||
F test/orderby5.test 0eb82d5890c3f3d0563966560cfdc984ea69e30c
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 16b649c8f0b38d446acbcff8a7bf055a9be43276
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 7285379906ab2f1108b8e82bbdf2d386cc8ff3ff
|
||||
@ -728,7 +731,7 @@ F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
|
||||
F test/permutations.test e154f5ed66d4d4913a99a110e870c9407f75b055
|
||||
F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
|
||||
F test/pragma.test e882183ecd21d064cec5c7aaea174fbd36293429
|
||||
F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
|
||||
@ -747,11 +750,11 @@ F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce
|
||||
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
|
||||
F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07
|
||||
F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
F test/savepoint.test f5acd87d0c7a5f4ad6c547b47fd18c0e1aeaf048
|
||||
F test/savepoint.test 6c53f76dffe5df0dd87646efe3e7aa159c36e07b
|
||||
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
|
||||
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
|
||||
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
|
||||
@ -762,7 +765,7 @@ F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
|
||||
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
||||
F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5
|
||||
F test/schema5.test 0103e4c0313b3725b5ae5600bdca53006ab53db3
|
||||
F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e
|
||||
F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c
|
||||
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
|
||||
F test/select1.test fc2a61f226a649393664ad54bc5376631801517c
|
||||
@ -822,13 +825,14 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806
|
||||
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
|
||||
F test/table.test 30423211108121884588d24d6776c7f38702ad7b
|
||||
F test/table.test 580d23530187026d4502fae74a490f0408cf2cc7
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
F test/tableopts.test 2950f094a984c8aa53493598bf0a07ac7549eb02
|
||||
F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptrigger.test 0a48d94222d50e6e50d72ac103606c4f8e7cbb81
|
||||
F test/tester.tcl 5e97d1fe08f45fa3cc2320cee437e315c75ce995
|
||||
F test/tester.tcl 7eac97d18c7836d91c078e1d5fa3f7eb5d9d6b4e
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@ -852,7 +856,7 @@ F test/tkt-38cb5df375.test f3cc8671f1eb604d4ae9cf886ed4366bec656678
|
||||
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
|
||||
F test/tkt-3a77c9714e.test b08bca26de1140bdf004a37716582a43d7bd8be8
|
||||
F test/tkt-3fe897352e.test 27e26eb0f1811aeba4d65aba43a4c52e99da5e70
|
||||
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
|
||||
F test/tkt-4a03edc4c8.test 91c0e135888cdc3d4eea82406a44b05c8c1648d0
|
||||
F test/tkt-4dd95f6943.test 3d0ce415d2ee15d3d564121960016b9c7be79407
|
||||
F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894
|
||||
F test/tkt-5d863f876e.test c9f36ca503fa154a3655f92a69d2c30da1747bfa
|
||||
@ -860,7 +864,7 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
|
||||
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
|
||||
F test/tkt-6bfb98dfc0.test 24780633627b5cfc0635a5500c2389ebfb563336
|
||||
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
|
||||
F test/tkt-78e04e52ea.test b0190d3375cf88b97d32188149cc99ccf22f556b
|
||||
F test/tkt-78e04e52ea.test 813779f8888f3ca226df656c4eef078f9635f3c9
|
||||
F test/tkt-7a31705a7e6.test e75a2bba4eec801b92c8040eb22096ac6d35e844
|
||||
F test/tkt-7bbfb7d442.test 7b2cd79c7a17ae6750e75ec1a7846712a69c9d18
|
||||
F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8
|
||||
@ -872,7 +876,7 @@ F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71
|
||||
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
|
||||
F test/tkt-9f2eb3abac.test 85bc63e749f050e6a61c8f9207f1eee65c9d3395
|
||||
F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4
|
||||
F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
|
||||
F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550
|
||||
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
|
||||
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
|
||||
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
|
||||
@ -899,7 +903,7 @@ F test/tkt1512.test a1df1f66caf0b9122d6220c15dcee230298c2c2f
|
||||
F test/tkt1514.test ddef38e34fea72eb1ab935ded9f17a3fb71dd9df
|
||||
F test/tkt1536.test 83ff7a7b6e248016f8d682d4f7a4ae114070d466
|
||||
F test/tkt1537.test e3a14332de9770be8ff14bd15c19a49cbec10808
|
||||
F test/tkt1567.test 18023cc3626a365f0118e17b66decedec93b1a6f
|
||||
F test/tkt1567.test 52f329386ac77e59260d4af1c58490d61377db20
|
||||
F test/tkt1644.test 80b6a2bb17885f3cf1cb886d97cdad13232bb869
|
||||
F test/tkt1667.test 4700d931ed19ea3983e8e703becb28079250b460
|
||||
F test/tkt1873.test 0e1b8c023050a430c2525179ed4022ddc7c31264
|
||||
@ -947,7 +951,7 @@ F test/tkt3527.test 1a6a48441b560bdc53aec581a868eb576234874d
|
||||
F test/tkt3541.test 5dc257bde9bc833ab9cc6844bf170b998dbb950a
|
||||
F test/tkt3554.test f599967f279077bace39220cbe76085c7b423725
|
||||
F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3
|
||||
F test/tkt35xx.test 69d038ce5898f1b64f2084b780bbab1cf9be0a25
|
||||
F test/tkt35xx.test f38c1b03713179d414969187c941466e44945b35
|
||||
F test/tkt3630.test 929f64852103054125200bc825c316d5f75d42f7
|
||||
F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
|
||||
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
|
||||
@ -981,7 +985,7 @@ F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
|
||||
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
|
||||
F test/transitive1.test 03f532954f46cdf5608f7766bff0b0c52bf2a7cd
|
||||
F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03
|
||||
F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
|
||||
F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
|
||||
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
|
||||
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
|
||||
F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
|
||||
@ -991,17 +995,17 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
|
||||
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
|
||||
F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
|
||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test a7b4367392c755bc5fd5fff88011753e6b6afe90
|
||||
F test/triggerC.test a68980c5955d62ee24be6f97129d824f199f9a4c
|
||||
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
|
||||
F test/triggerE.test 355e9c5cbaed5cd039a60baad1fb2197caeb8e52
|
||||
F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unique.test cadb172bbd5a2e83cd644d186ccd602085e54edc
|
||||
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
|
||||
F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46
|
||||
F test/unordered.test ef85ac8f2f3c93ed2b9e811b684de73175fc464c
|
||||
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
|
||||
F test/update.test 1b6c488a8f993d090b7ee9ad0e234faa161b3aeb
|
||||
F test/uri.test 63e03df051620a18f794b4f4adcdefb3c23b6751
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
@ -1011,7 +1015,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
|
||||
F test/view.test 4057630287bfa5955628fe90a13d4c225d1c7352
|
||||
F test/vtab1.test 1550abf90bc2b996f8c34e0be3fdb251af54fa41
|
||||
F test/vtab1.test 45ddde57764659c0ec90874bcb6c4831f1004c06
|
||||
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
|
||||
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
|
||||
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
|
||||
@ -1029,7 +1033,7 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
|
||||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test 82f463886e18d7f8395a4b6167c91815efe54839
|
||||
F test/wal.test 3a6ebdf0287b38b5537c07c517b30dda9aaac317
|
||||
F test/wal.test a59d00eaa0901e42b8a2ef21f2a4c972ee1f1bd4
|
||||
F test/wal2.test d4b470f13c87f6d8268b004380afa04c3c67cb90
|
||||
F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
@ -1061,7 +1065,7 @@ F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8
|
||||
F test/where8.test 6f95896633cf2d307b5263145b942b7d33e837c6
|
||||
F test/where8.test d2b4fd6d7b7c5d44f590182a05033d78a14c00a1
|
||||
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
||||
F test/where9.test 4f3eab951353a3ae164befc521c777dfa903e46c
|
||||
F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
|
||||
@ -1075,6 +1079,10 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
||||
F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b
|
||||
F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
|
||||
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
|
||||
F test/without_rowid3.test eac3d5c8a1924725b58503a368f2cbd24fd6c8a0
|
||||
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
|
||||
F tool/build-all-msvc.bat 1bac6adc3fdb4d9204f21d17b14be25778370e48 x
|
||||
@ -1091,7 +1099,7 @@ F tool/lemon.c 796930d5fc2036c7636f3f1ee12f9ae03719a2eb
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/logest.c 7ad625cac3d54012b27d468b7af6612f78b9ba75
|
||||
F tool/mkautoconfamal.sh f8d8dbf7d62f409ebed5134998bf5b51d7266383
|
||||
F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79
|
||||
F tool/mkkeywordhash.c 189d76644e373c7d0864c628deb8ce7b4f403591
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 17d40faae6c4b865633bfc5763821402a1cbefc3
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
@ -1111,7 +1119,7 @@ F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
|
||||
F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
|
||||
F tool/spaceanal.tcl 81fad69052145daa3a0a89d47ba69adf52b7aa01
|
||||
F tool/spaceanal.tcl 8e50b217c56a6a086a1b47eac9d09c5cd65b996f
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@ -1126,7 +1134,8 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P 42a11e7464ab1d97d603c7409f10710ad4f1f542
|
||||
R 2d5fb8fa6614cd47b99fe6098fa2430f
|
||||
U dan
|
||||
Z a252af69123903f914c4a37a9c4019f1
|
||||
P 49be646cd981f8ff0434cf90d2748afa30260017 55eea1782aead6a6aaf93f14d85486f8fd2209ad
|
||||
R 1e773dfb806625ad59b6a083881b6fca
|
||||
T +closed 55eea1782aead6a6aaf93f14d85486f8fd2209ad
|
||||
U drh
|
||||
Z 3c1e4b61d489a4d1c755b624a57fdf8f
|
||||
|
@ -1 +1 @@
|
||||
49be646cd981f8ff0434cf90d2748afa30260017
|
||||
c80e229dd9c1230abefbc707d4bf0b24315c6bb5
|
196
src/analyze.c
196
src/analyze.c
@ -244,8 +244,7 @@ static void openStatTable(
|
||||
/* Open the sqlite_stat[134] tables for writing. */
|
||||
for(i=0; aTable[i].zCols; i++){
|
||||
assert( i<ArraySize(aTable) );
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
|
||||
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
|
||||
}
|
||||
}
|
||||
@ -269,7 +268,11 @@ struct Stat4Sample {
|
||||
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
tRowcnt *anLt; /* sqlite_stat4.nLt */
|
||||
i64 iRowid; /* Rowid in main table of the key */
|
||||
union {
|
||||
i64 iRowid; /* Rowid in main table of the key */
|
||||
u8 *aRowid; /* Key for WITHOUT ROWID tables */
|
||||
} u;
|
||||
u32 nRowid; /* Sizeof aRowid[] */
|
||||
u8 isPSample; /* True if a periodic sample */
|
||||
int iCol; /* If !isPSample, the reason for inclusion */
|
||||
u32 iHash; /* Tiebreaker hash */
|
||||
@ -282,13 +285,87 @@ struct Stat4Accum {
|
||||
int mxSample; /* Maximum number of samples to accumulate */
|
||||
Stat4Sample current; /* Current row as a Stat4Sample */
|
||||
u32 iPrn; /* Pseudo-random number used for sampling */
|
||||
Stat4Sample *aBest; /* Array of (nCol-1) best samples */
|
||||
Stat4Sample *aBest; /* Array of nCol best samples */
|
||||
int iMin; /* Index in a[] of entry with minimum score */
|
||||
int nSample; /* Current number of samples */
|
||||
int iGet; /* Index of current sample accessed by stat_get() */
|
||||
Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
|
||||
sqlite3 *db; /* Database connection, for malloc() */
|
||||
};
|
||||
|
||||
/* Reclaim memory used by a Stat4Sample
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
static void sampleClear(sqlite3 *db, Stat4Sample *p){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ){
|
||||
sqlite3DbFree(db, p->u.aRowid);
|
||||
p->nRowid = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize the BLOB value of a ROWID
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
p->u.aRowid = sqlite3DbMallocRaw(db, n);
|
||||
if( p->u.aRowid ){
|
||||
p->nRowid = n;
|
||||
memcpy(p->u.aRowid, pData, n);
|
||||
}else{
|
||||
p->nRowid = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize the INTEGER value of a ROWID.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
|
||||
assert( db!=0 );
|
||||
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
|
||||
p->nRowid = 0;
|
||||
p->u.iRowid = iRowid;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Copy the contents of object (*pFrom) into (*pTo).
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
pTo->isPSample = pFrom->isPSample;
|
||||
pTo->iCol = pFrom->iCol;
|
||||
pTo->iHash = pFrom->iHash;
|
||||
memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol);
|
||||
memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol);
|
||||
memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol);
|
||||
if( pFrom->nRowid ){
|
||||
sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid);
|
||||
}else{
|
||||
sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Reclaim all memory of a Stat4Accum structure.
|
||||
*/
|
||||
static void stat4Destructor(void *pOld){
|
||||
Stat4Accum *p = (Stat4Accum*)pOld;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int i;
|
||||
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
|
||||
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
|
||||
sampleClear(p->db, &p->current);
|
||||
#endif
|
||||
sqlite3DbFree(p->db, p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the stat_init(N,C) SQL function. The two parameters
|
||||
** are the number of rows in the table or index (C) and the number of columns
|
||||
@ -307,6 +384,7 @@ static void statInit(
|
||||
int nCol; /* Number of columns in index being sampled */
|
||||
int nColUp; /* nCol rounded up for alignment */
|
||||
int n; /* Bytes of space to allocate */
|
||||
sqlite3 *db; /* Database connection */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int mxSample = SQLITE_STAT4_SAMPLES;
|
||||
#endif
|
||||
@ -323,16 +401,18 @@ static void statInit(
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
|
||||
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
|
||||
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
|
||||
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
|
||||
#endif
|
||||
;
|
||||
p = sqlite3MallocZero(n);
|
||||
db = sqlite3_context_db_handle(context);
|
||||
p = sqlite3DbMallocZero(db, n);
|
||||
if( p==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
|
||||
p->db = db;
|
||||
p->nRow = 0;
|
||||
p->nCol = nCol;
|
||||
p->current.anDLt = (tRowcnt*)&p[1];
|
||||
@ -367,7 +447,7 @@ static void statInit(
|
||||
#endif
|
||||
|
||||
/* Return a pointer to the allocated object to the caller */
|
||||
sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
|
||||
sqlite3_result_blob(context, p, sizeof(p), stat4Destructor);
|
||||
}
|
||||
static const FuncDef statInitFuncdef = {
|
||||
1+IsStat34, /* nArg */
|
||||
@ -441,25 +521,12 @@ static int sampleIsBetter(
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the contents of object (*pFrom) into (*pTo).
|
||||
*/
|
||||
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
|
||||
pTo->iRowid = pFrom->iRowid;
|
||||
pTo->isPSample = pFrom->isPSample;
|
||||
pTo->iCol = pFrom->iCol;
|
||||
pTo->iHash = pFrom->iHash;
|
||||
memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol);
|
||||
memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol);
|
||||
memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol);
|
||||
}
|
||||
|
||||
/*
|
||||
** Copy the contents of sample *pNew into the p->a[] array. If necessary,
|
||||
** remove the least desirable sample from p->a[] to make room.
|
||||
*/
|
||||
static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
Stat4Sample *pSample;
|
||||
Stat4Sample *pSample = 0;
|
||||
int i;
|
||||
|
||||
assert( IsStat4 || nEqZero==0 );
|
||||
@ -499,8 +566,10 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
|
||||
tRowcnt *anEq = pMin->anEq;
|
||||
tRowcnt *anLt = pMin->anLt;
|
||||
tRowcnt *anDLt = pMin->anDLt;
|
||||
sampleClear(p->db, pMin);
|
||||
memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1));
|
||||
pSample = &p->a[p->nSample-1];
|
||||
pSample->nRowid = 0;
|
||||
pSample->anEq = anEq;
|
||||
pSample->anDLt = anDLt;
|
||||
pSample->anLt = anLt;
|
||||
@ -597,16 +666,17 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the stat_push SQL function: stat_push(P,R,C)
|
||||
** Implementation of the stat_push SQL function: stat_push(P,C,R)
|
||||
** Arguments:
|
||||
**
|
||||
** P Pointer to the Stat4Accum object created by stat_init()
|
||||
** C Index of left-most column to differ from previous row
|
||||
** R Rowid for the current row
|
||||
** R Rowid for the current row. Might be a key record for
|
||||
** WITHOUT ROWID tables.
|
||||
**
|
||||
** The SQL function always returns NULL.
|
||||
**
|
||||
** The R parameter is only used for STAT3 and STAT4.
|
||||
** The R parameter is only used for STAT3 and STAT4
|
||||
*/
|
||||
static void statPush(
|
||||
sqlite3_context *context,
|
||||
@ -646,7 +716,12 @@ static void statPush(
|
||||
}
|
||||
p->nRow++;
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
p->current.iRowid = sqlite3_value_int64(argv[2]);
|
||||
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
|
||||
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
|
||||
}else{
|
||||
sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
|
||||
sqlite3_value_blob(argv[2]));
|
||||
}
|
||||
p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
|
||||
#endif
|
||||
|
||||
@ -770,7 +845,13 @@ static void statGet(
|
||||
p->iGet = 0;
|
||||
}
|
||||
if( p->iGet<p->nSample ){
|
||||
sqlite3_result_int64(context, p->a[p->iGet].iRowid);
|
||||
Stat4Sample *pS = p->a + p->iGet;
|
||||
if( pS->nRowid==0 ){
|
||||
sqlite3_result_int64(context, pS->u.iRowid);
|
||||
}else{
|
||||
sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid,
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
tRowcnt *aCnt = 0;
|
||||
@ -907,22 +988,26 @@ static void analyzeOneTable(
|
||||
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nCol; /* Number of columns indexed by pIdx */
|
||||
KeyInfo *pKey; /* KeyInfo structure for pIdx */
|
||||
int *aGotoChng; /* Array of jump instruction addresses */
|
||||
int addrRewind; /* Address of "OP_Rewind iIdxCur" */
|
||||
int addrGotoChng0; /* Address of "Goto addr_chng_0" */
|
||||
int addrNextRow; /* Address of "next_row:" */
|
||||
const char *zIdxName; /* Name of the index */
|
||||
|
||||
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
|
||||
if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
|
||||
VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
|
||||
nCol = pIdx->nColumn;
|
||||
nCol = pIdx->nKeyCol;
|
||||
aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1));
|
||||
if( aGotoChng==0 ) continue;
|
||||
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
|
||||
/* Populate the register containing the index name. */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
|
||||
if( pIdx->autoIndex==2 && !HasRowid(pTab) ){
|
||||
zIdxName = pTab->zName;
|
||||
}else{
|
||||
zIdxName = pIdx->zName;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, zIdxName, 0);
|
||||
|
||||
/*
|
||||
** Pseudo-code for loop that calls stat_push():
|
||||
@ -965,7 +1050,7 @@ static void analyzeOneTable(
|
||||
/* Open a read-only cursor on the index being analyzed. */
|
||||
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
|
||||
/* Invoke the stat_init() function. The arguments are:
|
||||
@ -1039,8 +1124,21 @@ static void analyzeOneTable(
|
||||
*/
|
||||
sqlite3VdbeJumpHere(v, aGotoChng[nCol]);
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
assert( regRowid==(regStat4+2) );
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
int j, k, regKey;
|
||||
regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
|
||||
VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
|
||||
sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
|
||||
}
|
||||
#endif
|
||||
assert( regChng==(regStat4+1) );
|
||||
sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp);
|
||||
@ -1066,6 +1164,7 @@ static void analyzeOneTable(
|
||||
int regSampleRowid = regCol + nCol;
|
||||
int addrNext;
|
||||
int addrIsNull;
|
||||
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
|
||||
pParse->nMem = MAX(pParse->nMem, regCol+nCol+1);
|
||||
|
||||
@ -1075,13 +1174,13 @@ static void analyzeOneTable(
|
||||
callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
|
||||
callStatGet(v, regStat4, STAT_GET_NLT, regLt);
|
||||
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, addrNext, regSampleRowid);
|
||||
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
|
||||
pIdx->aiColumn[0], regSample);
|
||||
#else
|
||||
for(i=0; i<nCol; i++){
|
||||
int iCol = pIdx->aiColumn[i];
|
||||
i16 iCol = pIdx->aiColumn[i];
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample);
|
||||
@ -1337,7 +1436,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
z = argv[2];
|
||||
|
||||
if( pIndex ){
|
||||
decodeIntArray((char*)z, pIndex->nColumn+1, pIndex->aiRowEst, pIndex);
|
||||
decodeIntArray((char*)z, pIndex->nKeyCol+1, pIndex->aiRowEst, pIndex);
|
||||
if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0];
|
||||
}else{
|
||||
Index fakeIdx;
|
||||
@ -1383,7 +1482,7 @@ static void initAvgEq(Index *pIdx){
|
||||
IndexSample *aSample = pIdx->aSample;
|
||||
IndexSample *pFinal = &aSample[pIdx->nSample-1];
|
||||
int iCol;
|
||||
for(iCol=0; iCol<pIdx->nColumn; iCol++){
|
||||
for(iCol=0; iCol<pIdx->nKeyCol; iCol++){
|
||||
int i; /* Used to iterate through samples */
|
||||
tRowcnt sumEq = 0; /* Sum of the nEq values */
|
||||
tRowcnt nSum = 0; /* Number of terms contributing to sumEq */
|
||||
@ -1411,6 +1510,23 @@ static void initAvgEq(Index *pIdx){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Look up an index by name. Or, if the name of a WITHOUT ROWID table
|
||||
** is supplied instead, find the PRIMARY KEY index for that table.
|
||||
*/
|
||||
static Index *findIndexOrPrimaryKey(
|
||||
sqlite3 *db,
|
||||
const char *zName,
|
||||
const char *zDb
|
||||
){
|
||||
Index *pIdx = sqlite3FindIndex(db, zName, zDb);
|
||||
if( pIdx==0 ){
|
||||
Table *pTab = sqlite3FindTable(db, zName, zDb);
|
||||
if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab);
|
||||
}
|
||||
return pIdx;
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content from either the sqlite_stat4 or sqlite_stat3 table
|
||||
** into the relevant Index.aSample[] arrays.
|
||||
@ -1460,14 +1576,14 @@ static int loadStatTbl(
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
nSample = sqlite3_column_int(pStmt, 1);
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
|
||||
/* Index.nSample is non-zero at this point if data has already been
|
||||
** loaded from the stat4 table. In this case ignore stat3 data. */
|
||||
if( pIdx==0 || pIdx->nSample ) continue;
|
||||
if( bStat3==0 ){
|
||||
nIdxCol = pIdx->nColumn+1;
|
||||
nAvgCol = pIdx->nColumn;
|
||||
nIdxCol = pIdx->nKeyCol+1;
|
||||
nAvgCol = pIdx->nKeyCol;
|
||||
}
|
||||
pIdx->nSampleCol = nIdxCol;
|
||||
nByte = sizeof(IndexSample) * nSample;
|
||||
@ -1506,7 +1622,7 @@ static int loadStatTbl(
|
||||
|
||||
zIndex = (char *)sqlite3_column_text(pStmt, 0);
|
||||
if( zIndex==0 ) continue;
|
||||
pIdx = sqlite3FindIndex(db, zIndex, zDb);
|
||||
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
|
||||
if( pIdx==0 ) continue;
|
||||
/* This next condition is true if data has already been loaded from
|
||||
** the sqlite_stat4 table. In this case ignore stat3 data. */
|
||||
|
@ -684,7 +684,7 @@ static int btreeMoveto(
|
||||
){
|
||||
int rc; /* Status code */
|
||||
UnpackedRecord *pIdxKey; /* Unpacked index key */
|
||||
char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
|
||||
char *pFree = 0;
|
||||
|
||||
if( pKey ){
|
||||
|
534
src/build.c
534
src/build.c
@ -382,8 +382,10 @@ static void freeIndex(sqlite3 *db, Index *p){
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
sqlite3DeleteIndexSamples(db, p);
|
||||
#endif
|
||||
if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo);
|
||||
sqlite3ExprDelete(db, p->pPartIdxWhere);
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
if( p->isResized ) sqlite3DbFree(db, p->azColl);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
@ -641,8 +643,7 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
|
||||
void sqlite3OpenMasterTable(Parse *p, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(p);
|
||||
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
|
||||
if( p->nTab==0 ){
|
||||
p->nTab = 1;
|
||||
}
|
||||
@ -747,6 +748,27 @@ int sqlite3CheckObjectName(Parse *pParse, const char *zName){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the PRIMARY KEY index of a table
|
||||
*/
|
||||
Index *sqlite3PrimaryKeyIndex(Table *pTab){
|
||||
Index *p;
|
||||
for(p=pTab->pIndex; p && p->autoIndex!=2; p=p->pNext){}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the column of index pIdx that corresponds to table
|
||||
** column iCol. Return -1 if not found.
|
||||
*/
|
||||
i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
|
||||
int i;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
if( iCol==pIdx->aiColumn[i] ) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin constructing a new table representation in memory. This is
|
||||
** the first of several action routines that get called in response
|
||||
@ -946,7 +968,7 @@ void sqlite3StartTable(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
|
||||
pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
|
||||
}
|
||||
sqlite3OpenMasterTable(pParse, iDb);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
|
||||
@ -1214,6 +1236,7 @@ void sqlite3AddPrimaryKey(
|
||||
Table *pTab = pParse->pNewTable;
|
||||
char *zType = 0;
|
||||
int iCol = -1, i;
|
||||
int nTerm;
|
||||
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
|
||||
if( pTab->tabFlags & TF_HasPrimaryKey ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
@ -1224,39 +1247,43 @@ void sqlite3AddPrimaryKey(
|
||||
if( pList==0 ){
|
||||
iCol = pTab->nCol - 1;
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
nTerm = 1;
|
||||
}else{
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
nTerm = pList->nExpr;
|
||||
for(i=0; i<nTerm; i++){
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iCol<pTab->nCol ){
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
|
||||
}
|
||||
}
|
||||
if( pList->nExpr>1 ) iCol = -1;
|
||||
}
|
||||
if( iCol>=0 && iCol<pTab->nCol ){
|
||||
zType = pTab->aCol[iCol].zType;
|
||||
}
|
||||
if( zType && sqlite3StrICmp(zType, "INTEGER")==0
|
||||
&& sortOrder==SQLITE_SO_ASC ){
|
||||
if( nTerm==1
|
||||
&& zType && sqlite3StrICmp(zType, "INTEGER")==0
|
||||
&& sortOrder==SQLITE_SO_ASC
|
||||
){
|
||||
pTab->iPKey = iCol;
|
||||
pTab->keyConf = (u8)onError;
|
||||
assert( autoInc==0 || autoInc==1 );
|
||||
pTab->tabFlags |= autoInc*TF_Autoincrement;
|
||||
if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
|
||||
}else if( autoInc ){
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
|
||||
"INTEGER PRIMARY KEY");
|
||||
#endif
|
||||
}else{
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
Index *p;
|
||||
if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
|
||||
0, sortOrder, 0);
|
||||
if( p ){
|
||||
p->autoIndex = 2;
|
||||
if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK);
|
||||
}
|
||||
pList = 0;
|
||||
}
|
||||
@ -1313,7 +1340,7 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
|
||||
** collation type was added. Correct this if it is the case.
|
||||
*/
|
||||
for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->nColumn==1 );
|
||||
assert( pIdx->nKeyCol==1 );
|
||||
if( pIdx->aiColumn[0]==i ){
|
||||
pIdx->azColl[0] = p->aCol[i].zColl;
|
||||
}
|
||||
@ -1505,6 +1532,31 @@ static char *createTableStmt(sqlite3 *db, Table *p){
|
||||
return zStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Resize an Index object to hold N columns total. Return SQLITE_OK
|
||||
** on success and SQLITE_NOMEM on an OOM error.
|
||||
*/
|
||||
static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
|
||||
char *zExtra;
|
||||
int nByte;
|
||||
if( pIdx->nColumn>=N ) return SQLITE_OK;
|
||||
assert( pIdx->isResized==0 );
|
||||
nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
|
||||
zExtra = sqlite3DbMallocZero(db, nByte);
|
||||
if( zExtra==0 ) return SQLITE_NOMEM;
|
||||
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
|
||||
pIdx->azColl = (char**)zExtra;
|
||||
zExtra += sizeof(char*)*N;
|
||||
memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
|
||||
pIdx->aiColumn = (i16*)zExtra;
|
||||
zExtra += sizeof(i16)*N;
|
||||
memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
|
||||
pIdx->aSortOrder = (u8*)zExtra;
|
||||
pIdx->nColumn = N;
|
||||
pIdx->isResized = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Estimate the total row width for a table.
|
||||
*/
|
||||
@ -1523,16 +1575,148 @@ static void estimateTableWidth(Table *pTab){
|
||||
** Estimate the average size of a row for an index.
|
||||
*/
|
||||
static void estimateIndexWidth(Index *pIdx){
|
||||
unsigned wIndex = 1;
|
||||
unsigned wIndex = 0;
|
||||
int i;
|
||||
const Column *aCol = pIdx->pTable->aCol;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
assert( pIdx->aiColumn[i]>=0 && pIdx->aiColumn[i]<pIdx->pTable->nCol );
|
||||
wIndex += aCol[pIdx->aiColumn[i]].szEst;
|
||||
i16 x = pIdx->aiColumn[i];
|
||||
assert( x<pIdx->pTable->nCol );
|
||||
wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst;
|
||||
}
|
||||
pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
|
||||
}
|
||||
|
||||
/* Return true if value x is found any of the first nCol entries of aiCol[]
|
||||
*/
|
||||
static int hasColumn(const i16 *aiCol, int nCol, int x){
|
||||
while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine runs at the end of parsing a CREATE TABLE statement that
|
||||
** has a WITHOUT ROWID clause. The job of this routine is to convert both
|
||||
** internal schema data structures and the generated VDBE code so that they
|
||||
** are appropriate for a WITHOUT ROWID table instead of a rowid table.
|
||||
** Changes include:
|
||||
**
|
||||
** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is
|
||||
** no rowid btree for a WITHOUT ROWID. Instead, the canonical
|
||||
** data storage is a covering index btree.
|
||||
** (2) Bypass the creation of the sqlite_master table entry
|
||||
** for the PRIMARY KEY as the the primary key index is now
|
||||
** identified by the sqlite_master table entry of the table itself.
|
||||
** (3) Set the Index.tnum of the PRIMARY KEY Index object in the
|
||||
** schema to the rootpage from the main table.
|
||||
** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
|
||||
** (5) Add all table columns to the PRIMARY KEY Index object
|
||||
** so that the PRIMARY KEY is a covering index. The surplus
|
||||
** columns are part of KeyInfo.nXField and are not used for
|
||||
** sorting or lookup or uniqueness checks.
|
||||
** (6) Replace the rowid tail on all automatically generated UNIQUE
|
||||
** indices with the PRIMARY KEY columns.
|
||||
*/
|
||||
static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
Index *pIdx;
|
||||
Index *pPk;
|
||||
int nPk;
|
||||
int i, j;
|
||||
sqlite3 *db = pParse->db;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
|
||||
/* Convert the OP_CreateTable opcode that would normally create the
|
||||
** root-page for the table into a OP_CreateIndex opcode. The index
|
||||
** created will become the PRIMARY KEY index.
|
||||
*/
|
||||
if( pParse->addrCrTab ){
|
||||
assert( v );
|
||||
sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex;
|
||||
}
|
||||
|
||||
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
|
||||
** table entry.
|
||||
*/
|
||||
if( pParse->addrSkipPK ){
|
||||
assert( v );
|
||||
sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto;
|
||||
}
|
||||
|
||||
/* Locate the PRIMARY KEY index. Or, if this table was originally
|
||||
** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
|
||||
*/
|
||||
if( pTab->iPKey>=0 ){
|
||||
ExprList *pList;
|
||||
pList = sqlite3ExprListAppend(pParse, 0, 0);
|
||||
if( pList==0 ) return;
|
||||
pList->a[0].zName = sqlite3DbStrDup(pParse->db,
|
||||
pTab->aCol[pTab->iPKey].zName);
|
||||
pList->a[0].sortOrder = pParse->iPkSortOrder;
|
||||
assert( pParse->pNewTable==pTab );
|
||||
pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0);
|
||||
if( pPk==0 ) return;
|
||||
pPk->autoIndex = 2;
|
||||
pTab->iPKey = -1;
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
}
|
||||
pPk->isCovering = 1;
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
|
||||
/* Make sure every column of the PRIMARY KEY is NOT NULL */
|
||||
for(i=0; i<nPk; i++){
|
||||
pTab->aCol[pPk->aiColumn[i]].notNull = 1;
|
||||
}
|
||||
pPk->uniqNotNull = 1;
|
||||
|
||||
/* The root page of the PRIMARY KEY is the table root page */
|
||||
pPk->tnum = pTab->tnum;
|
||||
|
||||
/* Update the in-memory representation of all UNIQUE indices by converting
|
||||
** the final rowid column into one or more columns of the PRIMARY KEY.
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int n;
|
||||
if( pIdx->autoIndex==2 ) continue;
|
||||
for(i=n=0; i<nPk; i++){
|
||||
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
|
||||
}
|
||||
if( n==0 ){
|
||||
/* This index is a superset of the primary key */
|
||||
pIdx->nColumn = pIdx->nKeyCol;
|
||||
continue;
|
||||
}
|
||||
if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
|
||||
for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
|
||||
if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
|
||||
pIdx->aiColumn[j] = pPk->aiColumn[i];
|
||||
pIdx->azColl[j] = pPk->azColl[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
assert( pIdx->nColumn>=pIdx->nKeyCol+n );
|
||||
assert( pIdx->nColumn>=j );
|
||||
}
|
||||
|
||||
/* Add all table columns to the PRIMARY KEY index
|
||||
*/
|
||||
if( nPk<pTab->nCol ){
|
||||
if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
|
||||
for(i=0, j=nPk; i<pTab->nCol; i++){
|
||||
if( !hasColumn(pPk->aiColumn, j, i) ){
|
||||
assert( j<pPk->nColumn );
|
||||
pPk->aiColumn[j] = i;
|
||||
pPk->azColl[j] = "BINARY";
|
||||
j++;
|
||||
}
|
||||
}
|
||||
assert( pPk->nColumn==j );
|
||||
assert( pTab->nCol==j );
|
||||
}else{
|
||||
pPk->nColumn = pTab->nCol;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called to report the final ")" that terminates
|
||||
** a CREATE TABLE statement.
|
||||
@ -1556,7 +1740,8 @@ static void estimateIndexWidth(Index *pIdx){
|
||||
void sqlite3EndTable(
|
||||
Parse *pParse, /* Parse context */
|
||||
Token *pCons, /* The ',' token after the last column defn. */
|
||||
Token *pEnd, /* The final ')' token in the CREATE TABLE */
|
||||
Token *pEnd, /* The ')' before options in the CREATE TABLE */
|
||||
u8 tabOpts, /* Extra table options. Usually 0. */
|
||||
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
|
||||
){
|
||||
Table *p; /* The new table */
|
||||
@ -1572,6 +1757,26 @@ void sqlite3EndTable(
|
||||
|
||||
assert( !db->init.busy || !pSelect );
|
||||
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
** for the table from the db->init.newTnum field. (The page number
|
||||
** should have been put there by the sqliteOpenCb routine.)
|
||||
*/
|
||||
if( db->init.busy ){
|
||||
p->tnum = db->init.newTnum;
|
||||
}
|
||||
|
||||
/* Special processing for WITHOUT ROWID Tables */
|
||||
if( tabOpts & TF_WithoutRowid ){
|
||||
if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
|
||||
sqlite3ErrorMsg(pParse, "no PRIMARY KEY for table %s", p->zName);
|
||||
}else{
|
||||
p->tabFlags |= TF_WithoutRowid;
|
||||
convertToWithoutRowidTable(pParse, p);
|
||||
}
|
||||
}
|
||||
|
||||
iDb = sqlite3SchemaToIndex(db, p->pSchema);
|
||||
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
@ -1588,16 +1793,6 @@ void sqlite3EndTable(
|
||||
estimateIndexWidth(pIdx);
|
||||
}
|
||||
|
||||
/* If the db->init.busy is 1 it means we are reading the SQL off the
|
||||
** "sqlite_master" or "sqlite_temp_master" table on the disk.
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
** for the table from the db->init.newTnum field. (The page number
|
||||
** should have been put there by the sqliteOpenCb routine.)
|
||||
*/
|
||||
if( db->init.busy ){
|
||||
p->tnum = db->init.newTnum;
|
||||
}
|
||||
|
||||
/* If not initializing, then create a record for the new table
|
||||
** in the SQLITE_MASTER table of the database.
|
||||
**
|
||||
@ -1671,7 +1866,9 @@ void sqlite3EndTable(
|
||||
if( pSelect ){
|
||||
zStmt = createTableStmt(db, p);
|
||||
}else{
|
||||
n = (int)(pEnd->z - pParse->sNameToken.z) + 1;
|
||||
Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd;
|
||||
n = (int)(pEnd2->z - pParse->sNameToken.z);
|
||||
if( pEnd2->z[0]!=';' ) n += pEnd2->n;
|
||||
zStmt = sqlite3MPrintf(db,
|
||||
"CREATE %s %.*s", zType2, n, pParse->sNameToken.z
|
||||
);
|
||||
@ -1819,7 +2016,7 @@ void sqlite3CreateView(
|
||||
sEnd.n = 1;
|
||||
|
||||
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
|
||||
sqlite3EndTable(pParse, 0, &sEnd, 0);
|
||||
sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
|
||||
return;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIEW */
|
||||
@ -2274,8 +2471,8 @@ exit_drop_table:
|
||||
** currently under construction. pFromCol determines which columns
|
||||
** in the current table point to the foreign key. If pFromCol==0 then
|
||||
** connect the key to the last column inserted. pTo is the name of
|
||||
** the table referred to. pToCol is a list of tables in the other
|
||||
** pTo table that the foreign key points to. flags contains all
|
||||
** the table referred to (a.k.a the "parent" table). pToCol is a list
|
||||
** of tables in the parent pTo table. flags contains all
|
||||
** information about the conflict resolution algorithms specified
|
||||
** in the ON DELETE, ON UPDATE and ON INSERT clauses.
|
||||
**
|
||||
@ -2458,16 +2655,13 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
tnum = memRootPage;
|
||||
}else{
|
||||
tnum = pIndex->tnum;
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
|
||||
}
|
||||
pKey = sqlite3IndexKeyinfo(pParse, pIndex);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
|
||||
pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
|
||||
|
||||
/* Open the sorter cursor if we are to use one. */
|
||||
iSorter = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
|
||||
sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)
|
||||
sqlite3KeyInfoRef(pKey), P4_KEYINFO);
|
||||
|
||||
/* Open the table. Loop through all rows of the table, inserting index
|
||||
** records into the sorter. */
|
||||
@ -2475,20 +2669,25 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
|
||||
sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1, &iPartIdxLabel);
|
||||
sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 0, &iPartIdxLabel);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
|
||||
sqlite3VdbeResolveLabel(v, iPartIdxLabel);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
|
||||
(char *)pKey, P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
|
||||
|
||||
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
|
||||
if( pIndex->onError!=OE_None ){
|
||||
assert( pKey!=0 || db->mallocFailed || pParse->nErr );
|
||||
if( pIndex->onError!=OE_None && pKey!=0 ){
|
||||
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE,
|
||||
OE_Abort, "indexed columns are not unique", P4_STATIC
|
||||
);
|
||||
sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
|
||||
pKey->nField - pIndex->nKeyCol);
|
||||
sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
|
||||
}else{
|
||||
addr2 = sqlite3VdbeCurrentAddr(v);
|
||||
}
|
||||
@ -2504,6 +2703,41 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iSorter);
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate heap space to hold an Index object with nCol columns.
|
||||
**
|
||||
** Increase the allocation size to provide an extra nExtra bytes
|
||||
** of 8-byte aligned space after the Index object and return a
|
||||
** pointer to this extra space in *ppExtra.
|
||||
*/
|
||||
Index *sqlite3AllocateIndexObject(
|
||||
sqlite3 *db, /* Database connection */
|
||||
i16 nCol, /* Total number of columns in the index */
|
||||
int nExtra, /* Number of bytes of extra space to alloc */
|
||||
char **ppExtra /* Pointer to the "extra" space */
|
||||
){
|
||||
Index *p; /* Allocated index object */
|
||||
int nByte; /* Bytes of space for Index object + arrays */
|
||||
|
||||
nByte = ROUND8(sizeof(Index)) + /* Index structure */
|
||||
ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
|
||||
ROUND8(sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
|
||||
sizeof(i16)*nCol + /* Index.aiColumn */
|
||||
sizeof(u8)*nCol); /* Index.aSortOrder */
|
||||
p = sqlite3DbMallocZero(db, nByte + nExtra);
|
||||
if( p ){
|
||||
char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
|
||||
p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
|
||||
p->aiRowEst = (tRowcnt*)pExtra; pExtra += sizeof(tRowcnt)*(nCol+1);
|
||||
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
|
||||
p->aSortOrder = (u8*)pExtra;
|
||||
p->nColumn = nCol;
|
||||
p->nKeyCol = nCol - 1;
|
||||
*ppExtra = ((char*)p) + nByte;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new index for an SQL table. pName1.pName2 is the name of the index
|
||||
** and pTblList is the name of the table that is to be indexed. Both will
|
||||
@ -2538,7 +2772,6 @@ Index *sqlite3CreateIndex(
|
||||
char *zName = 0; /* Name of the index */
|
||||
int nName; /* Number of characters in zName */
|
||||
int i, j;
|
||||
Token nullId; /* Fake token for an empty ID list */
|
||||
DbFixer sFix; /* For assigning database names to pTable */
|
||||
int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */
|
||||
sqlite3 *db = pParse->db;
|
||||
@ -2547,9 +2780,10 @@ Index *sqlite3CreateIndex(
|
||||
Token *pName = 0; /* Unqualified name of the index to create */
|
||||
struct ExprList_item *pListItem; /* For looping over pList */
|
||||
const Column *pTabCol; /* A column in the table */
|
||||
int nCol; /* Number of columns */
|
||||
int nExtra = 0; /* Space allocated for zExtra[] */
|
||||
int nExtraCol; /* Number of extra columns needed */
|
||||
char *zExtra; /* Extra space after the Index object */
|
||||
Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */
|
||||
|
||||
assert( pParse->nErr==0 ); /* Never called with prior errors */
|
||||
if( db->mallocFailed || IN_DECLARE_VTAB ){
|
||||
@ -2601,6 +2835,7 @@ Index *sqlite3CreateIndex(
|
||||
pTab->zName);
|
||||
goto exit_create_index;
|
||||
}
|
||||
if( !HasRowid(pTab) ) pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
}else{
|
||||
assert( pName==0 );
|
||||
assert( pStart==0 );
|
||||
@ -2696,11 +2931,10 @@ Index *sqlite3CreateIndex(
|
||||
** So create a fake list to simulate this.
|
||||
*/
|
||||
if( pList==0 ){
|
||||
nullId.z = pTab->aCol[pTab->nCol-1].zName;
|
||||
nullId.n = sqlite3Strlen30((char*)nullId.z);
|
||||
pList = sqlite3ExprListAppend(pParse, 0, 0);
|
||||
if( pList==0 ) goto exit_create_index;
|
||||
sqlite3ExprListSetName(pParse, pList, &nullId, 0);
|
||||
pList->a[0].zName = sqlite3DbStrDup(pParse->db,
|
||||
pTab->aCol[pTab->nCol-1].zName);
|
||||
pList->a[0].sortOrder = (u8)sortOrder;
|
||||
}
|
||||
|
||||
@ -2719,36 +2953,23 @@ Index *sqlite3CreateIndex(
|
||||
** Allocate the index structure.
|
||||
*/
|
||||
nName = sqlite3Strlen30(zName);
|
||||
nCol = pList->nExpr;
|
||||
pIndex = sqlite3DbMallocZero(db,
|
||||
ROUND8(sizeof(Index)) + /* Index structure */
|
||||
ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */
|
||||
sizeof(char *)*nCol + /* Index.azColl */
|
||||
sizeof(int)*nCol + /* Index.aiColumn */
|
||||
sizeof(u8)*nCol + /* Index.aSortOrder */
|
||||
nName + 1 + /* Index.zName */
|
||||
nExtra /* Collation sequence names */
|
||||
);
|
||||
nExtraCol = pPk ? pPk->nKeyCol : 1;
|
||||
pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol,
|
||||
nName + nExtra + 1, &zExtra);
|
||||
if( db->mallocFailed ){
|
||||
goto exit_create_index;
|
||||
}
|
||||
zExtra = (char*)pIndex;
|
||||
pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))];
|
||||
pIndex->azColl = (char**)
|
||||
((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1));
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) );
|
||||
assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
|
||||
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
|
||||
pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
|
||||
pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
|
||||
zExtra = (char *)(&pIndex->zName[nName+1]);
|
||||
pIndex->zName = zExtra;
|
||||
zExtra += nName + 1;
|
||||
memcpy(pIndex->zName, zName, nName+1);
|
||||
pIndex->pTable = pTab;
|
||||
pIndex->nColumn = pList->nExpr;
|
||||
pIndex->onError = (u8)onError;
|
||||
pIndex->uniqNotNull = onError==OE_Abort;
|
||||
pIndex->uniqNotNull = onError!=OE_None;
|
||||
pIndex->autoIndex = (u8)(pName==0);
|
||||
pIndex->pSchema = db->aDb[iDb].pSchema;
|
||||
pIndex->nKeyCol = pList->nExpr;
|
||||
if( pPIWhere ){
|
||||
sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere, 0);
|
||||
pIndex->pPartIdxWhere = pPIWhere;
|
||||
@ -2788,7 +3009,8 @@ Index *sqlite3CreateIndex(
|
||||
pParse->checkSchema = 1;
|
||||
goto exit_create_index;
|
||||
}
|
||||
pIndex->aiColumn[i] = j;
|
||||
assert( pTab->nCol<=0x7fff && j<=0x7fff );
|
||||
pIndex->aiColumn[i] = (i16)j;
|
||||
if( pListItem->pExpr ){
|
||||
int nColl;
|
||||
assert( pListItem->pExpr->op==TK_COLLATE );
|
||||
@ -2811,6 +3033,23 @@ Index *sqlite3CreateIndex(
|
||||
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
|
||||
if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0;
|
||||
}
|
||||
if( pPk ){
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
int x = pPk->aiColumn[j];
|
||||
if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
|
||||
pIndex->nColumn--;
|
||||
}else{
|
||||
pIndex->aiColumn[i] = x;
|
||||
pIndex->azColl[i] = pPk->azColl[j];
|
||||
pIndex->aSortOrder[i] = pPk->aSortOrder[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
assert( i==pIndex->nColumn );
|
||||
}else{
|
||||
pIndex->aiColumn[i] = -1;
|
||||
pIndex->azColl[i] = "BINARY";
|
||||
}
|
||||
sqlite3DefaultRowEst(pIndex);
|
||||
if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
|
||||
|
||||
@ -2843,8 +3082,8 @@ Index *sqlite3CreateIndex(
|
||||
assert( pIdx->autoIndex );
|
||||
assert( pIndex->onError!=OE_None );
|
||||
|
||||
if( pIdx->nColumn!=pIndex->nColumn ) continue;
|
||||
for(k=0; k<pIdx->nColumn; k++){
|
||||
if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue;
|
||||
for(k=0; k<pIdx->nKeyCol; k++){
|
||||
const char *z1;
|
||||
const char *z2;
|
||||
if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
|
||||
@ -2852,7 +3091,7 @@ Index *sqlite3CreateIndex(
|
||||
z2 = pIndex->azColl[k];
|
||||
if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
|
||||
}
|
||||
if( k==pIdx->nColumn ){
|
||||
if( k==pIdx->nKeyCol ){
|
||||
if( pIdx->onError!=pIndex->onError ){
|
||||
/* This constraint creates the same index as a previous
|
||||
** constraint specified somewhere in the CREATE TABLE statement.
|
||||
@ -2894,22 +3133,20 @@ Index *sqlite3CreateIndex(
|
||||
}
|
||||
}
|
||||
|
||||
/* If the db->init.busy is 0 then create the index on disk. This
|
||||
** involves writing the index into the master table and filling in the
|
||||
** index with the current table contents.
|
||||
/* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
|
||||
** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
|
||||
** emit code to allocate the index rootpage on disk and make an entry for
|
||||
** the index in the sqlite_master table and populate the index with
|
||||
** content. But, do not do this if we are simply reading the sqlite_master
|
||||
** table to parse the schema, or if this index is the PRIMARY KEY index
|
||||
** of a WITHOUT ROWID table.
|
||||
**
|
||||
** The db->init.busy is 0 when the user first enters a CREATE INDEX
|
||||
** command. db->init.busy is 1 when a database is opened and
|
||||
** CREATE INDEX statements are read out of the master table. In
|
||||
** the latter case the index already exists on disk, which is why
|
||||
** we don't want to recreate it.
|
||||
**
|
||||
** If pTblName==0 it means this index is generated as a primary key
|
||||
** or UNIQUE constraint of a CREATE TABLE statement. Since the table
|
||||
** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
|
||||
** or UNIQUE index in a CREATE TABLE statement. Since the table
|
||||
** has just been created, it contains no data and the index initialization
|
||||
** step can be skipped.
|
||||
*/
|
||||
else if( pParse->nErr==0 ){
|
||||
else if( pParse->nErr==0 && (HasRowid(pTab) || pTblName!=0) ){
|
||||
Vdbe *v;
|
||||
char *zStmt;
|
||||
int iMem = ++pParse->nMem;
|
||||
@ -3021,12 +3258,12 @@ void sqlite3DefaultRowEst(Index *pIdx){
|
||||
a[0] = pIdx->pTable->nRowEst;
|
||||
if( a[0]<10 ) a[0] = 10;
|
||||
n = 10;
|
||||
for(i=1; i<=pIdx->nColumn; i++){
|
||||
for(i=1; i<=pIdx->nKeyCol; i++){
|
||||
a[i] = n;
|
||||
if( n>5 ) n--;
|
||||
}
|
||||
if( pIdx->onError!=OE_None ){
|
||||
a[pIdx->nColumn] = 1;
|
||||
a[pIdx->nKeyCol] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3714,7 +3951,8 @@ void sqlite3HaltConstraint(
|
||||
int errCode, /* extended error code */
|
||||
int onError, /* Constraint type */
|
||||
char *p4, /* Error message */
|
||||
int p4type /* P4_STATIC or P4_TRANSIENT */
|
||||
i8 p4type, /* P4_STATIC or P4_TRANSIENT */
|
||||
u8 p5Errmsg /* P5_ErrMsg type */
|
||||
){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
assert( (errCode&0xff)==SQLITE_CONSTRAINT );
|
||||
@ -3722,6 +3960,58 @@ void sqlite3HaltConstraint(
|
||||
sqlite3MayAbort(pParse);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
|
||||
if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
** Code an OP_Halt due to UNIQUE or PRIMARY KEY constraint violation.
|
||||
*/
|
||||
void sqlite3UniqueConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int onError, /* Constraint type */
|
||||
Index *pIdx /* The index that triggers the constraint */
|
||||
){
|
||||
char *zErr;
|
||||
int j;
|
||||
StrAccum errMsg;
|
||||
Table *pTab = pIdx->pTable;
|
||||
|
||||
sqlite3StrAccumInit(&errMsg, 0, 0, 200);
|
||||
errMsg.db = pParse->db;
|
||||
for(j=0; j<pIdx->nKeyCol; j++){
|
||||
char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
||||
if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
|
||||
sqlite3StrAccumAppend(&errMsg, pTab->zName, -1);
|
||||
sqlite3StrAccumAppend(&errMsg, ".", 1);
|
||||
sqlite3StrAccumAppend(&errMsg, zCol, -1);
|
||||
}
|
||||
zErr = sqlite3StrAccumFinish(&errMsg);
|
||||
sqlite3HaltConstraint(pParse,
|
||||
(pIdx->autoIndex==2)?SQLITE_CONSTRAINT_PRIMARYKEY:SQLITE_CONSTRAINT_UNIQUE,
|
||||
onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code an OP_Halt due to non-unique rowid.
|
||||
*/
|
||||
void sqlite3RowidConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
int onError, /* Conflict resolution algorithm */
|
||||
Table *pTab /* The table with the non-unique rowid */
|
||||
){
|
||||
char *zMsg;
|
||||
int rc;
|
||||
if( pTab->iPKey>=0 ){
|
||||
zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName,
|
||||
pTab->aCol[pTab->iPKey].zName);
|
||||
rc = SQLITE_CONSTRAINT_PRIMARYKEY;
|
||||
}else{
|
||||
zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName);
|
||||
rc = SQLITE_CONSTRAINT_ROWID;
|
||||
}
|
||||
sqlite3HaltConstraint(pParse, rc, onError, zMsg, P4_DYNAMIC,
|
||||
P5_ConstraintUnique);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3734,8 +4024,8 @@ static int collationMatch(const char *zColl, Index *pIndex){
|
||||
assert( zColl!=0 );
|
||||
for(i=0; i<pIndex->nColumn; i++){
|
||||
const char *z = pIndex->azColl[i];
|
||||
assert( z!=0 );
|
||||
if( 0==sqlite3StrICmp(z, zColl) ){
|
||||
assert( z!=0 || pIndex->aiColumn[i]<0 );
|
||||
if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -3854,33 +4144,47 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return a dynamicly allocated KeyInfo structure that can be used
|
||||
** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
|
||||
** Return a KeyInfo structure that is appropriate for the given Index.
|
||||
**
|
||||
** If successful, a pointer to the new structure is returned. In this case
|
||||
** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
|
||||
** pointer. If an error occurs (out of memory or missing collation
|
||||
** sequence), NULL is returned and the state of pParse updated to reflect
|
||||
** the error.
|
||||
** The KeyInfo structure for an index is cached in the Index object.
|
||||
** So there might be multiple references to the returned pointer. The
|
||||
** caller should not try to modify the KeyInfo object.
|
||||
**
|
||||
** The caller should invoke sqlite3KeyInfoUnref() on the returned object
|
||||
** when it has finished using it.
|
||||
*/
|
||||
KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
|
||||
int i;
|
||||
int nCol = pIdx->nColumn;
|
||||
KeyInfo *pKey;
|
||||
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nCol);
|
||||
if( pKey ){
|
||||
for(i=0; i<nCol; i++){
|
||||
char *zColl = pIdx->azColl[i];
|
||||
assert( zColl );
|
||||
pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
|
||||
if( pParse->nErr ) return 0;
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
#endif
|
||||
if( pIdx->pKeyInfo==0 ){
|
||||
int i;
|
||||
int nCol = pIdx->nColumn;
|
||||
int nKey = pIdx->nKeyCol;
|
||||
KeyInfo *pKey;
|
||||
if( pIdx->uniqNotNull ){
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey);
|
||||
}else{
|
||||
pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0);
|
||||
}
|
||||
if( pKey ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKey) );
|
||||
for(i=0; i<nCol; i++){
|
||||
char *zColl = pIdx->azColl[i];
|
||||
if( zColl==0 ) zColl = "BINARY";
|
||||
pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
|
||||
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
|
||||
}
|
||||
if( pParse->nErr ){
|
||||
sqlite3KeyInfoUnref(pKey);
|
||||
}else{
|
||||
pIdx->pKeyInfo = pKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pParse->nErr ){
|
||||
sqlite3DbFree(pParse->db, pKey);
|
||||
pKey = 0;
|
||||
}
|
||||
return pKey;
|
||||
return sqlite3KeyInfoRef(pIdx->pKeyInfo);
|
||||
}
|
||||
|
232
src/delete.c
232
src/delete.c
@ -134,7 +134,7 @@ Expr *sqlite3LimitWhere(
|
||||
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
|
||||
Expr *pLimit, /* The LIMIT clause. May be null */
|
||||
Expr *pOffset, /* The OFFSET clause. May be null */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For error messages. */
|
||||
char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
|
||||
){
|
||||
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
|
||||
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
|
||||
@ -209,7 +209,8 @@ limit_where_cleanup_2:
|
||||
sqlite3ExprDelete(pParse->db, pOffset);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
|
||||
/* && !defined(SQLITE_OMIT_SUBQUERY) */
|
||||
|
||||
/*
|
||||
** Generate code for a DELETE FROM statement.
|
||||
@ -230,7 +231,9 @@ void sqlite3DeleteFrom(
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Index *pIdx; /* For looping over indices of the table */
|
||||
int iCur; /* VDBE Cursor number for pTab */
|
||||
int iTabCur; /* Cursor number for the table */
|
||||
int iDataCur; /* VDBE cursor for the canonical data source */
|
||||
int iIdxCur; /* Cursor number of the first index */
|
||||
sqlite3 *db; /* Main database structure */
|
||||
AuthContext sContext; /* Authorization context */
|
||||
NameContext sNC; /* Name context to resolve expressions in */
|
||||
@ -295,7 +298,7 @@ void sqlite3DeleteFrom(
|
||||
/* Assign cursor number to the table and all its indices.
|
||||
*/
|
||||
assert( pTabList->nSrc==1 );
|
||||
iCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
iTabCur = pTabList->a[0].iCursor = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
pParse->nTab++;
|
||||
}
|
||||
@ -320,7 +323,8 @@ void sqlite3DeleteFrom(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
|
||||
iDataCur = iIdxCur = iTabCur;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -351,18 +355,74 @@ void sqlite3DeleteFrom(
|
||||
){
|
||||
assert( !isView );
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
pTab->zName, P4_STATIC);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
pTab->zName, P4_STATIC);
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
assert( pIdx->pSchema==pTab->pSchema );
|
||||
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
|
||||
/* The usual case: There is a WHERE clause so we have to scan through
|
||||
** the table and pick which records to delete.
|
||||
*/
|
||||
{
|
||||
if( !HasRowid(pTab) ){
|
||||
/* There is a WHERE clause on a WITHOUT ROWID table.
|
||||
*/
|
||||
Index *pPk; /* The PRIMARY KEY index on the table */
|
||||
int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
|
||||
int iEph; /* Ephemeral table holding all primary key values */
|
||||
int iKey; /* Key value inserting into iEph */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
iKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
for(i=0; i<nPk; i++){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pPk->aiColumn[i],iPk+i);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
|
||||
sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, iKey);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Open cursors for all indices of the table.
|
||||
*/
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite,
|
||||
iTabCur, &iDataCur, &iIdxCur);
|
||||
|
||||
/* Loop over the primary keys to be deleted. */
|
||||
addr = sqlite3VdbeAddOp1(v, OP_Rewind, iEph);
|
||||
sqlite3VdbeAddOp2(v, OP_RowKey, iEph, iPk);
|
||||
|
||||
/* Delete the row */
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
iPk, 0, 1, OE_Default);
|
||||
|
||||
/* End of the delete loop */
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addr+1);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
/* Close the cursors open on the table and its indexes. */
|
||||
assert( iDataCur>=iIdxCur );
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur+i);
|
||||
}
|
||||
}else{
|
||||
/* There is a WHERE clause on a rowid table. Run a loop that extracts
|
||||
** all rowids to be deleted into a RowSet.
|
||||
*/
|
||||
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
|
||||
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
|
||||
int regRowid; /* Actual register containing rowids */
|
||||
@ -374,7 +434,7 @@ void sqlite3DeleteFrom(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
|
||||
);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iRowid, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
@ -391,7 +451,10 @@ void sqlite3DeleteFrom(
|
||||
** only effect this statement has is to fire the INSTEAD OF
|
||||
** triggers. */
|
||||
if( !isView ){
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur,
|
||||
&iDataCur, &iIdxCur);
|
||||
assert( iDataCur==iTabCur );
|
||||
assert( iIdxCur==iDataCur+1 );
|
||||
}
|
||||
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
@ -408,7 +471,8 @@ void sqlite3DeleteFrom(
|
||||
#endif
|
||||
{
|
||||
int count = (pParse->nested==0); /* True to count changes */
|
||||
sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
|
||||
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
|
||||
iRowid, 1, count, OE_Default);
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
@ -417,10 +481,10 @@ void sqlite3DeleteFrom(
|
||||
|
||||
/* Close the cursors open on the table and its indexes. */
|
||||
if( !isView && !IsVirtual(pTab) ){
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i);
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCur);
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,44 +524,51 @@ delete_from_cleanup:
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes a single row of a
|
||||
** single table to be deleted.
|
||||
** single table to be deleted. Both the original table entry and
|
||||
** all indices are removed.
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
** Preconditions:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number $iCur.
|
||||
** 1. iDataCur is an open cursor on the btree that is the canonical data
|
||||
** store for the table. (This will be either the table itself,
|
||||
** in the case of a rowid table, or the PRIMARY KEY index in the case
|
||||
** of a WITHOUT ROWID table.)
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number base+i for the i-th index.
|
||||
** cursor number iIdxCur+i for the i-th index.
|
||||
**
|
||||
** 3. The record number of the row to be deleted must be stored in
|
||||
** memory cell iRowid.
|
||||
**
|
||||
** This routine generates code to remove both the table record and all
|
||||
** index entries that point to that record.
|
||||
** 3. The primary key for the row to be deleted must be stored in a
|
||||
** sequence of nPk memory cells starting at iPk. If nPk==0 that means
|
||||
** that a search record formed from OP_MakeRecord is contained in the
|
||||
** single memory location iPk.
|
||||
*/
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int iRowid, /* Memory cell that contains the rowid to delete */
|
||||
int count, /* If non-zero, increment the row change counter */
|
||||
Trigger *pTrigger, /* List of triggers to (potentially) fire */
|
||||
int onconf /* Default ON CONFLICT policy for triggers */
|
||||
int iDataCur, /* Cursor from which column data is extracted */
|
||||
int iIdxCur, /* First index cursor */
|
||||
int iPk, /* First memory cell containing the PRIMARY KEY */
|
||||
i16 nPk, /* Number of PRIMARY KEY memory cells */
|
||||
u8 count, /* If non-zero, increment the row change counter */
|
||||
u8 onconf /* Default ON CONFLICT policy for triggers */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
||||
int iOld = 0; /* First register in OLD.* array */
|
||||
int iLabel; /* Label resolved to end of generated code */
|
||||
u8 opSeek; /* Seek opcode */
|
||||
|
||||
/* Vdbe is guaranteed to have been allocated by this stage. */
|
||||
assert( v );
|
||||
VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
|
||||
iDataCur, iIdxCur, iPk, (int)nPk));
|
||||
|
||||
/* Seek cursor iCur to the row to delete. If this row no longer exists
|
||||
** (this can happen if a trigger program has already deleted it), do
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
|
||||
/* If there are any triggers to fire, allocate a range of registers to
|
||||
** use for the old.* references in the triggers. */
|
||||
@ -516,10 +587,10 @@ void sqlite3GenerateRowDelete(
|
||||
|
||||
/* Populate the OLD.* pseudo-table register array. These values will be
|
||||
** used by any BEFORE and AFTER triggers that exist. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld);
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( mask==0xffffffff || mask&(1<<iCol) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld+iCol+1);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,7 +603,7 @@ void sqlite3GenerateRowDelete(
|
||||
** the BEFORE triggers coded above have already removed the row
|
||||
** being deleted. Do not attempt to delete the row a second time, and
|
||||
** do not fire AFTER triggers. */
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
|
||||
|
||||
/* Do FK processing. This call checks that any FK constraints that
|
||||
** refer to this table (i.e. constraints attached to other tables)
|
||||
@ -544,8 +615,8 @@ void sqlite3GenerateRowDelete(
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
if( pTab->pSelect==0 ){
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
@ -565,49 +636,62 @@ void sqlite3GenerateRowDelete(
|
||||
** trigger programs were invoked. Or if a trigger program throws a
|
||||
** RAISE(IGNORE) exception. */
|
||||
sqlite3VdbeResolveLabel(v, iLabel);
|
||||
VdbeModuleComment((v, "END: GenRowDel()"));
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine generates VDBE code that causes the deletion of all
|
||||
** index entries associated with a single row of a single table.
|
||||
** index entries associated with a single row of a single table, pTab
|
||||
**
|
||||
** The VDBE must be in a particular state when this routine is called.
|
||||
** These are the requirements:
|
||||
** Preconditions:
|
||||
**
|
||||
** 1. A read/write cursor pointing to pTab, the table containing the row
|
||||
** to be deleted, must be opened as cursor number "iCur".
|
||||
** 1. A read/write cursor "iDataCur" must be open on the canonical storage
|
||||
** btree for the table pTab. (This will be either the table itself
|
||||
** for rowid tables or to the primary key index for WITHOUT ROWID
|
||||
** tables.)
|
||||
**
|
||||
** 2. Read/write cursors for all indices of pTab must be open as
|
||||
** cursor number iCur+i for the i-th index.
|
||||
** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex
|
||||
** index is the 0-th index.)
|
||||
**
|
||||
** 3. The "iCur" cursor must be pointing to the row that is to be
|
||||
** deleted.
|
||||
** 3. The "iDataCur" cursor must be already be positioned on the row
|
||||
** that is to be deleted.
|
||||
*/
|
||||
void sqlite3GenerateRowIndexDelete(
|
||||
Parse *pParse, /* Parsing and code generating context */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int iDataCur, /* Cursor of table holding data. */
|
||||
int iIdxCur, /* First index cursor */
|
||||
int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
|
||||
){
|
||||
int i;
|
||||
Index *pIdx;
|
||||
int r1;
|
||||
int iPartIdxLabel;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i; /* Index loop counter */
|
||||
int r1; /* Register holding an index key */
|
||||
int iPartIdxLabel; /* Jump destination for skipping partial index entries */
|
||||
Index *pIdx; /* Current index */
|
||||
Vdbe *v; /* The prepared statement under construction */
|
||||
Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
|
||||
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0, &iPartIdxLabel);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iCur+i, r1, pIdx->nColumn+1);
|
||||
v = pParse->pVdbe;
|
||||
VdbeModuleComment((v, "BEGIN: GenRowIdxDel(%d,%d)", iDataCur, iIdxCur));
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
|
||||
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
|
||||
if( pIdx==pPk ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, &iPartIdxLabel);
|
||||
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
|
||||
sqlite3VdbeResolveLabel(v, iPartIdxLabel);
|
||||
}
|
||||
VdbeModuleComment((v, "END: GenRowIdxDel()"));
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code that will assemble an index key and put it in register
|
||||
** Generate code that will assemble an index key and stores it in register
|
||||
** regOut. The key with be for index pIdx which is an index on pTab.
|
||||
** iCur is the index of a cursor open on the pTab table and pointing to
|
||||
** the entry that needs indexing.
|
||||
** the entry that needs indexing. If pTab is a WITHOUT ROWID table, then
|
||||
** iCur must be the cursor of the PRIMARY KEY index.
|
||||
**
|
||||
** Return a register number which is the first in a block of
|
||||
** registers that holds the elements of the index key. The
|
||||
@ -624,9 +708,9 @@ void sqlite3GenerateRowIndexDelete(
|
||||
int sqlite3GenerateIndexKey(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Index *pIdx, /* The index for which to generate a key */
|
||||
int iCur, /* Cursor number for the pIdx->pTable table */
|
||||
int regOut, /* Write the new index key to this register */
|
||||
int doMakeRec, /* Run the OP_MakeRecord instruction if true */
|
||||
int iDataCur, /* Cursor number from which to take column data */
|
||||
int regOut, /* Put the new key into this register if not 0 */
|
||||
int prefixOnly, /* Compute only a unique prefix of the key */
|
||||
int *piPartIdxLabel /* OUT: Jump to this label to skip partial index */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
@ -634,30 +718,32 @@ int sqlite3GenerateIndexKey(
|
||||
Table *pTab = pIdx->pTable;
|
||||
int regBase;
|
||||
int nCol;
|
||||
Index *pPk;
|
||||
|
||||
if( piPartIdxLabel ){
|
||||
if( pIdx->pPartIdxWhere ){
|
||||
*piPartIdxLabel = sqlite3VdbeMakeLabel(v);
|
||||
pParse->iPartIdxTab = iCur;
|
||||
pParse->iPartIdxTab = iDataCur;
|
||||
sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
|
||||
SQLITE_JUMPIFNULL);
|
||||
}else{
|
||||
*piPartIdxLabel = 0;
|
||||
}
|
||||
}
|
||||
nCol = pIdx->nColumn;
|
||||
regBase = sqlite3GetTempRange(pParse, nCol+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol);
|
||||
nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
|
||||
regBase = sqlite3GetTempRange(pParse, nCol);
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(j=0; j<nCol; j++){
|
||||
int idx = pIdx->aiColumn[j];
|
||||
if( idx==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j);
|
||||
i16 idx = pIdx->aiColumn[j];
|
||||
if( pPk ) idx = sqlite3ColumnOfIndex(pPk, idx);
|
||||
if( idx<0 || idx==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regBase+j);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j);
|
||||
sqlite3ColumnDefault(v, pTab, idx, -1);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, idx, regBase+j);
|
||||
sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[j], -1);
|
||||
}
|
||||
}
|
||||
if( doMakeRec ){
|
||||
if( regOut ){
|
||||
const char *zAff;
|
||||
if( pTab->pSelect
|
||||
|| OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt)
|
||||
@ -666,9 +752,9 @@ int sqlite3GenerateIndexKey(
|
||||
}else{
|
||||
zAff = sqlite3IndexAffinityStr(v, pIdx);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
|
||||
}
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
return regBase;
|
||||
}
|
||||
|
39
src/expr.c
39
src/expr.c
@ -1532,8 +1532,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
Table *pTab; /* Table <table>. */
|
||||
Expr *pExpr; /* Expression <column> */
|
||||
int iCol; /* Index of column <column> */
|
||||
int iDb; /* Database idx for pTab */
|
||||
i16 iCol; /* Index of column <column> */
|
||||
i16 iDb; /* Database idx for pTab */
|
||||
|
||||
assert( p ); /* Because of isCandidateForInOpt(p) */
|
||||
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
@ -1541,7 +1541,7 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
|
||||
pTab = p->pSrc->a[0].pTab;
|
||||
pExpr = p->pEList->a[0].pExpr;
|
||||
iCol = pExpr->iColumn;
|
||||
iCol = (i16)pExpr->iColumn;
|
||||
|
||||
/* Code an OP_VerifyCookie and OP_TableLock for <table>. */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
@ -1579,16 +1579,11 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
|
||||
for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
|
||||
if( (pIdx->aiColumn[0]==iCol)
|
||||
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
|
||||
&& (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
|
||||
&& (!mustBeUnique || (pIdx->nKeyCol==1 && pIdx->onError!=OE_None))
|
||||
){
|
||||
int iAddr;
|
||||
char *pKey;
|
||||
|
||||
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
iAddr = sqlite3CodeOnce(pParse);
|
||||
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
|
||||
pKey,P4_KEYINFO_HANDOFF);
|
||||
int iAddr = sqlite3CodeOnce(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
|
||||
eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
|
||||
@ -1728,7 +1723,7 @@ int sqlite3CodeSubselect(
|
||||
pExpr->iTable = pParse->nTab++;
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
|
||||
if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1);
|
||||
pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
/* Case 1: expr IN (SELECT ...)
|
||||
@ -1746,13 +1741,14 @@ int sqlite3CodeSubselect(
|
||||
pExpr->x.pSelect->iLimit = 0;
|
||||
testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
|
||||
if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
|
||||
sqlite3DbFree(pParse->db, pKeyInfo);
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
return 0;
|
||||
}
|
||||
pEList = pExpr->x.pSelect->pEList;
|
||||
assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
|
||||
assert( pEList!=0 );
|
||||
assert( pEList->nExpr>0 );
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
|
||||
pEList->a[0].pExpr);
|
||||
}else if( ALWAYS(pExpr->x.pList!=0) ){
|
||||
@ -1772,6 +1768,7 @@ int sqlite3CodeSubselect(
|
||||
affinity = SQLITE_AFF_NONE;
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
|
||||
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
|
||||
}
|
||||
|
||||
@ -1813,7 +1810,7 @@ int sqlite3CodeSubselect(
|
||||
sqlite3ReleaseTempReg(pParse, r2);
|
||||
}
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2214,15 +2211,19 @@ static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
|
||||
void sqlite3ExprCodeGetColumnOfTable(
|
||||
Vdbe *v, /* The VDBE under construction */
|
||||
Table *pTab, /* The table containing the value */
|
||||
int iTabCur, /* The cursor for this table */
|
||||
int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
|
||||
int iCol, /* Index of the column to extract */
|
||||
int regOut /* Extract the valud into this register */
|
||||
int regOut /* Extract the value into this register */
|
||||
){
|
||||
if( iCol<0 || iCol==pTab->iPKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
|
||||
}else{
|
||||
int op = IsVirtual(pTab) ? OP_VColumn : OP_Column;
|
||||
sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut);
|
||||
int x = iCol;
|
||||
if( !HasRowid(pTab) ){
|
||||
x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut);
|
||||
}
|
||||
if( iCol>=0 ){
|
||||
sqlite3ColumnDefault(v, pTab, iCol, regOut);
|
||||
@ -2968,7 +2969,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
|
||||
}else{
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
|
||||
pExpr->affinity, pExpr->u.zToken, 0);
|
||||
pExpr->affinity, pExpr->u.zToken, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
170
src/fkey.c
170
src/fkey.c
@ -225,7 +225,7 @@ int sqlite3FkLocateIndex(
|
||||
}
|
||||
|
||||
for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){
|
||||
if( pIdx->nKeyCol==nCol && pIdx->onError!=OE_None ){
|
||||
/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
|
||||
** of columns. If each indexed column corresponds to a foreign key
|
||||
** column of pFKey, then this index is a winner. */
|
||||
@ -248,7 +248,7 @@ int sqlite3FkLocateIndex(
|
||||
** the default collation sequences for each column. */
|
||||
int i, j;
|
||||
for(i=0; i<nCol; i++){
|
||||
int iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
|
||||
i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
|
||||
char *zDfltColl; /* Def. collation for column */
|
||||
char *zIdxCol; /* Name of indexed column */
|
||||
|
||||
@ -379,10 +379,9 @@ static void fkLookupParent(
|
||||
int nCol = pFKey->nCol;
|
||||
int regTemp = sqlite3GetTempRange(pParse, nCol);
|
||||
int regRec = sqlite3GetTempReg(pParse);
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
|
||||
}
|
||||
@ -432,8 +431,7 @@ static void fkLookupParent(
|
||||
** generated for will not open a statement transaction. */
|
||||
assert( nIncr==1 );
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
OE_Abort, "foreign key constraint failed", P4_STATIC
|
||||
);
|
||||
OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
||||
}else{
|
||||
if( nIncr>0 && pFKey->isDeferred==0 ){
|
||||
sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
||||
@ -445,6 +443,62 @@ static void fkLookupParent(
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCur);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return an Expr object that refers to a memory register corresponding
|
||||
** to column iCol of table pTab.
|
||||
**
|
||||
** regBase is the first of an array of register that contains the data
|
||||
** for pTab. regBase itself holds the rowid. regBase+1 holds the first
|
||||
** column. regBase+2 holds the second column, and so forth.
|
||||
*/
|
||||
static Expr *exprTableRegister(
|
||||
Parse *pParse, /* Parsing and code generating context */
|
||||
Table *pTab, /* The table whose content is at r[regBase]... */
|
||||
int regBase, /* Contents of table pTab */
|
||||
i16 iCol /* Which column of pTab is desired */
|
||||
){
|
||||
Expr *pExpr;
|
||||
Column *pCol;
|
||||
const char *zColl;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
pExpr = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
if( pExpr ){
|
||||
if( iCol>=0 && iCol!=pTab->iPKey ){
|
||||
pCol = &pTab->aCol[iCol];
|
||||
pExpr->iTable = regBase + iCol + 1;
|
||||
pExpr->affinity = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
|
||||
}else{
|
||||
pExpr->iTable = regBase;
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an Expr object that refers to column iCol of table pTab which
|
||||
** has cursor iCur.
|
||||
*/
|
||||
static Expr *exprTableColumn(
|
||||
sqlite3 *db, /* The database connection */
|
||||
Table *pTab, /* The table whose column is desired */
|
||||
int iCursor, /* The open cursor on the table */
|
||||
i16 iCol /* The column that is wanted */
|
||||
){
|
||||
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
|
||||
if( pExpr ){
|
||||
pExpr->pTab = pTab;
|
||||
pExpr->iTable = iCursor;
|
||||
pExpr->iColumn = iCol;
|
||||
}
|
||||
return pExpr;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to generate code executed when a row is deleted
|
||||
** from the parent table of foreign key constraint pFKey and, if pFKey is
|
||||
@ -460,13 +514,13 @@ static void fkLookupParent(
|
||||
** --------------------------------------------------------------------------
|
||||
** DELETE immediate Increment the "immediate constraint counter".
|
||||
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
|
||||
** throw a "foreign key constraint failed" exception.
|
||||
** throw a "FOREIGN KEY constraint failed" exception.
|
||||
**
|
||||
** INSERT immediate Decrement the "immediate constraint counter".
|
||||
**
|
||||
** DELETE deferred Increment the "deferred constraint counter".
|
||||
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
|
||||
** throw a "foreign key constraint failed" exception.
|
||||
** throw a "FOREIGN KEY constraint failed" exception.
|
||||
**
|
||||
** INSERT deferred Decrement the "deferred constraint counter".
|
||||
**
|
||||
@ -475,12 +529,12 @@ static void fkLookupParent(
|
||||
*/
|
||||
static void fkScanChildren(
|
||||
Parse *pParse, /* Parse context */
|
||||
SrcList *pSrc, /* SrcList containing the table to scan */
|
||||
Table *pTab,
|
||||
Index *pIdx, /* Foreign key index */
|
||||
FKey *pFKey, /* Foreign key relationship */
|
||||
SrcList *pSrc, /* The child table to be scanned */
|
||||
Table *pTab, /* The parent table */
|
||||
Index *pIdx, /* Index on parent covering the foreign key */
|
||||
FKey *pFKey, /* The foreign key linking pSrc to pTab */
|
||||
int *aiCol, /* Map from pIdx cols to child table cols */
|
||||
int regData, /* Referenced table data starts here */
|
||||
int regData, /* Parent row data starts here */
|
||||
int nIncr /* Amount to increment deferred counter by */
|
||||
){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
@ -491,7 +545,9 @@ static void fkScanChildren(
|
||||
int iFkIfZero = 0; /* Address of OP_FkIfZero */
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
|
||||
assert( !pIdx || pIdx->pTable==pTab );
|
||||
assert( pIdx==0 || pIdx->pTable==pTab );
|
||||
assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol );
|
||||
assert( pIdx!=0 || pFKey->nCol==1 );
|
||||
|
||||
if( nIncr<0 ){
|
||||
iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0);
|
||||
@ -509,29 +565,11 @@ static void fkScanChildren(
|
||||
Expr *pLeft; /* Value from parent table row */
|
||||
Expr *pRight; /* Column ref to child table */
|
||||
Expr *pEq; /* Expression (pLeft = pRight) */
|
||||
int iCol; /* Index of column in child table */
|
||||
i16 iCol; /* Index of column in child table */
|
||||
const char *zCol; /* Name of column in child table */
|
||||
|
||||
pLeft = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
if( pLeft ){
|
||||
/* Set the collation sequence and affinity of the LHS of each TK_EQ
|
||||
** expression to the parent key column defaults. */
|
||||
if( pIdx ){
|
||||
Column *pCol;
|
||||
const char *zColl;
|
||||
iCol = pIdx->aiColumn[i];
|
||||
pCol = &pTab->aCol[iCol];
|
||||
if( pTab->iPKey==iCol ) iCol = -1;
|
||||
pLeft->iTable = regData+iCol+1;
|
||||
pLeft->affinity = pCol->affinity;
|
||||
zColl = pCol->zColl;
|
||||
if( zColl==0 ) zColl = db->pDfltColl->zName;
|
||||
pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl);
|
||||
}else{
|
||||
pLeft->iTable = regData;
|
||||
pLeft->affinity = SQLITE_AFF_INTEGER;
|
||||
}
|
||||
}
|
||||
iCol = pIdx ? pIdx->aiColumn[i] : -1;
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
|
||||
assert( iCol>=0 );
|
||||
zCol = pFKey->pFrom->aCol[iCol].zName;
|
||||
@ -540,24 +578,39 @@ static void fkScanChildren(
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
}
|
||||
|
||||
/* If the child table is the same as the parent table, and this scan
|
||||
** is taking place as part of a DELETE operation (operation D.2), omit the
|
||||
** row being deleted from the scan by adding ($rowid != rowid) to the WHERE
|
||||
** clause, where $rowid is the rowid of the row being deleted. */
|
||||
/* If the child table is the same as the parent table, then add terms
|
||||
** to the WHERE clause that prevent this entry from being scanned.
|
||||
** The added WHERE clause terms are like this:
|
||||
**
|
||||
** $current_rowid!=rowid
|
||||
** NOT( $current_a==a AND $current_b==b AND ... )
|
||||
**
|
||||
** The first form is used for rowid tables. The second form is used
|
||||
** for WITHOUT ROWID tables. In the second form, the primary key is
|
||||
** (a,b,...)
|
||||
*/
|
||||
if( pTab==pFKey->pFrom && nIncr>0 ){
|
||||
Expr *pEq; /* Expression (pLeft = pRight) */
|
||||
Expr *pNe; /* Expression (pLeft != pRight) */
|
||||
Expr *pLeft; /* Value from parent table row */
|
||||
Expr *pRight; /* Column ref to child table */
|
||||
pLeft = sqlite3Expr(db, TK_REGISTER, 0);
|
||||
pRight = sqlite3Expr(db, TK_COLUMN, 0);
|
||||
if( pLeft && pRight ){
|
||||
pLeft->iTable = regData;
|
||||
pLeft->affinity = SQLITE_AFF_INTEGER;
|
||||
pRight->iTable = pSrc->a[0].iCursor;
|
||||
pRight->iColumn = -1;
|
||||
if( HasRowid(pTab) ){
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, -1);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
|
||||
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
|
||||
}else{
|
||||
int i;
|
||||
Expr *pEq, *pAll = 0;
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
i16 iCol = pIdx->aiColumn[i];
|
||||
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
||||
pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
|
||||
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
|
||||
pAll = sqlite3ExprAnd(db, pAll, pEq);
|
||||
}
|
||||
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
|
||||
}
|
||||
pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
||||
pWhere = sqlite3ExprAnd(db, pWhere, pNe);
|
||||
}
|
||||
|
||||
/* Resolve the references in the WHERE clause. */
|
||||
@ -587,8 +640,8 @@ static void fkScanChildren(
|
||||
}
|
||||
|
||||
/*
|
||||
** This function returns a pointer to the head of a linked list of FK
|
||||
** constraints for which table pTab is the parent table. For example,
|
||||
** This function returns a linked list of FKey objects (connected by
|
||||
** FKey.pNextTo) holding all children of table pTab. For example,
|
||||
** given the following schema:
|
||||
**
|
||||
** CREATE TABLE t1(a PRIMARY KEY);
|
||||
@ -679,8 +732,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
|
||||
if( (db->flags & SQLITE_DeferFKs)==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
|
||||
OE_Abort, "foreign key constraint failed", P4_STATIC
|
||||
);
|
||||
OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
||||
}
|
||||
|
||||
if( iSkip ){
|
||||
@ -890,7 +942,8 @@ void sqlite3FkCheck(
|
||||
sqlite3DbFree(db, aiFree);
|
||||
}
|
||||
|
||||
/* Loop through all the foreign key constraints that refer to this table */
|
||||
/* Loop through all the foreign key constraints that refer to this table.
|
||||
** (the "child" constraints) */
|
||||
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
|
||||
Index *pIdx = 0; /* Foreign key index for pFKey */
|
||||
SrcList *pSrc;
|
||||
@ -915,9 +968,8 @@ void sqlite3FkCheck(
|
||||
}
|
||||
assert( aiCol || pFKey->nCol==1 );
|
||||
|
||||
/* Create a SrcList structure containing a single table (the table
|
||||
** the foreign key that refers to this table is attached to). This
|
||||
** is required for the sqlite3WhereXXX() interface. */
|
||||
/* Create a SrcList structure containing the child table. We need the
|
||||
** child table as a SrcList for sqlite3WhereBegin() */
|
||||
pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
|
||||
if( pSrc ){
|
||||
struct SrcList_item *pItem = pSrc->a;
|
||||
@ -966,7 +1018,7 @@ u32 sqlite3FkOldmask(
|
||||
Index *pIdx = 0;
|
||||
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
|
||||
if( pIdx ){
|
||||
for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
||||
for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1158,7 +1210,7 @@ static Trigger *fkActionTrigger(
|
||||
|
||||
tFrom.z = zFrom;
|
||||
tFrom.n = nFrom;
|
||||
pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed");
|
||||
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
|
||||
if( pRaise ){
|
||||
pRaise->affinity = OE_Abort;
|
||||
}
|
||||
|
684
src/insert.c
684
src/insert.c
File diff suppressed because it is too large
Load Diff
28
src/main.c
28
src/main.c
@ -1135,6 +1135,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break;
|
||||
case SQLITE_CONSTRAINT_FUNCTION:
|
||||
zName = "SQLITE_CONSTRAINT_FUNCTION"; break;
|
||||
case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break;
|
||||
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
|
||||
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
|
||||
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
|
||||
@ -1950,6 +1951,32 @@ const char *sqlite3_errstr(int rc){
|
||||
return sqlite3ErrStr(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Invalidate all cached KeyInfo objects for database connection "db"
|
||||
*/
|
||||
static void invalidateCachedKeyInfo(sqlite3 *db){
|
||||
Db *pDb; /* A single database */
|
||||
int iDb; /* The database index number */
|
||||
HashElem *k; /* For looping over tables in pDb */
|
||||
Table *pTab; /* A table in the database */
|
||||
Index *pIdx; /* Each index */
|
||||
|
||||
for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
|
||||
if( pDb->pBt==0 ) continue;
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
|
||||
pTab = (Table*)sqliteHashData(k);
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){
|
||||
sqlite3KeyInfoUnref(pIdx->pKeyInfo);
|
||||
pIdx->pKeyInfo = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a new collating function for database "db". The name is zName
|
||||
** and the encoding is enc.
|
||||
@ -1994,6 +2021,7 @@ static int createCollation(
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
invalidateCachedKeyInfo(db);
|
||||
|
||||
/* If collation sequence pColl was created directly by a call to
|
||||
** sqlite3_create_collation, and not generated by synthCollSeq(),
|
||||
|
32
src/parse.y
32
src/parse.y
@ -163,13 +163,23 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
|
||||
temp(A) ::= TEMP. {A = 1;}
|
||||
%endif SQLITE_OMIT_TEMPDB
|
||||
temp(A) ::= . {A = 0;}
|
||||
create_table_args ::= LP columnlist conslist_opt(X) RP(Y). {
|
||||
sqlite3EndTable(pParse,&X,&Y,0);
|
||||
create_table_args ::= LP columnlist conslist_opt(X) RP(E) table_options(F). {
|
||||
sqlite3EndTable(pParse,&X,&E,F,0);
|
||||
}
|
||||
create_table_args ::= AS select(S). {
|
||||
sqlite3EndTable(pParse,0,0,S);
|
||||
sqlite3EndTable(pParse,0,0,0,S);
|
||||
sqlite3SelectDelete(pParse->db, S);
|
||||
}
|
||||
%type table_options {u8}
|
||||
table_options(A) ::= . {A = 0;}
|
||||
table_options(A) ::= WITHOUT nm(X). {
|
||||
if( X.n==5 && sqlite3_strnicmp(X.z,"rowid",5)==0 ){
|
||||
A = TF_WithoutRowid;
|
||||
}else{
|
||||
A = 0;
|
||||
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z);
|
||||
}
|
||||
}
|
||||
columnlist ::= columnlist COMMA column.
|
||||
columnlist ::= column.
|
||||
|
||||
@ -205,7 +215,7 @@ id(A) ::= INDEXED(X). {A = X;}
|
||||
CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
|
||||
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
|
||||
QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK
|
||||
SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL
|
||||
SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITHOUT
|
||||
%ifdef SQLITE_OMIT_COMPOUND_SELECT
|
||||
EXCEPT INTERSECT UNION
|
||||
%endif SQLITE_OMIT_COMPOUND_SELECT
|
||||
@ -573,7 +583,7 @@ indexed_opt(A) ::= NOT INDEXED. {A.z=0; A.n=1;}
|
||||
|
||||
%type using_opt {IdList*}
|
||||
%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
|
||||
using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
|
||||
using_opt(U) ::= USING LP idlist(L) RP. {U = L;}
|
||||
using_opt(U) ::= . {U = 0;}
|
||||
|
||||
|
||||
@ -740,14 +750,14 @@ valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. {
|
||||
|
||||
%type inscollist_opt {IdList*}
|
||||
%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);}
|
||||
%type inscollist {IdList*}
|
||||
%destructor inscollist {sqlite3IdListDelete(pParse->db, $$);}
|
||||
%type idlist {IdList*}
|
||||
%destructor idlist {sqlite3IdListDelete(pParse->db, $$);}
|
||||
|
||||
inscollist_opt(A) ::= . {A = 0;}
|
||||
inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
|
||||
inscollist(A) ::= inscollist(X) COMMA nm(Y).
|
||||
inscollist_opt(A) ::= LP idlist(X) RP. {A = X;}
|
||||
idlist(A) ::= idlist(X) COMMA nm(Y).
|
||||
{A = sqlite3IdListAppend(pParse->db,X,&Y);}
|
||||
inscollist(A) ::= nm(Y).
|
||||
idlist(A) ::= nm(Y).
|
||||
{A = sqlite3IdListAppend(pParse->db,0,&Y);}
|
||||
|
||||
/////////////////////////// Expression Processing /////////////////////////////
|
||||
@ -1227,7 +1237,7 @@ trigger_time(A) ::= . { A = TK_BEFORE; }
|
||||
%destructor trigger_event {sqlite3IdListDelete(pParse->db, $$.b);}
|
||||
trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;}
|
||||
trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;}
|
||||
trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;}
|
||||
trigger_event(A) ::= UPDATE OF idlist(X). {A.a = TK_UPDATE; A.b = X;}
|
||||
|
||||
foreach_clause ::= .
|
||||
foreach_clause ::= FOR EACH ROW.
|
||||
|
82
src/pragma.c
82
src/pragma.c
@ -1425,8 +1425,7 @@ void sqlite3Pragma(
|
||||
int i, k;
|
||||
int nHidden = 0;
|
||||
Column *pCol;
|
||||
Index *pPk;
|
||||
for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){}
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
sqlite3VdbeSetNumCols(v, 6);
|
||||
pParse->nMem = 6;
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
@ -1509,8 +1508,8 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC);
|
||||
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC);
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int cnum = pIdx->aiColumn[i];
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
i16 cnum = pIdx->aiColumn[i];
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2);
|
||||
assert( pTab->nCol>cnum );
|
||||
@ -1681,9 +1680,8 @@ void sqlite3Pragma(
|
||||
if( pIdx==0 ){
|
||||
sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
|
||||
}else{
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
}
|
||||
}else{
|
||||
k = 0;
|
||||
@ -1847,16 +1845,20 @@ void sqlite3Pragma(
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
|
||||
cnt++;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
|
||||
VdbeComment((v, "%s", pTab->zName));
|
||||
cnt++;
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure sufficient number of registers have been allocated */
|
||||
pParse->nMem = MAX( pParse->nMem, cnt+7 );
|
||||
pParse->nMem = MAX( pParse->nMem, cnt+8 );
|
||||
|
||||
/* Do the b-tree integrity checks */
|
||||
sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
|
||||
@ -1874,58 +1876,60 @@ void sqlite3Pragma(
|
||||
*/
|
||||
for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
Index *pIdx, *pPk;
|
||||
int loopTop;
|
||||
int iDataCur, iIdxCur;
|
||||
|
||||
if( pTab->pIndex==0 ) continue;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead,
|
||||
1, &iDataCur, &iIdxCur);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7+j); /* index entries counter */
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
|
||||
}
|
||||
pParse->nMem = MAX(pParse->nMem, 7+j);
|
||||
loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0) + 1;
|
||||
pParse->nMem = MAX(pParse->nMem, 8+j);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0);
|
||||
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int jmp2, jmp3;
|
||||
int jmp2, jmp3, jmp4;
|
||||
int r1;
|
||||
static const VdbeOpList idxErr[] = {
|
||||
{ OP_AddImm, 1, -1, 0},
|
||||
{ OP_String8, 0, 3, 0}, /* 1 */
|
||||
{ OP_Rowid, 1, 4, 0},
|
||||
{ OP_String8, 0, 5, 0}, /* 3 */
|
||||
{ OP_String8, 0, 6, 0}, /* 4 */
|
||||
{ OP_Concat, 4, 3, 3},
|
||||
{ OP_Concat, 5, 3, 3},
|
||||
{ OP_Concat, 6, 3, 3},
|
||||
{ OP_ResultRow, 3, 1, 0},
|
||||
{ OP_IfPos, 1, 0, 0}, /* 9 */
|
||||
{ OP_Halt, 0, 0, 0},
|
||||
};
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0, &jmp3);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 7+j, 1); /* increment entry count */
|
||||
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1);
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
|
||||
sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
|
||||
sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
|
||||
sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeJumpHere(v, addr+9);
|
||||
if( pPk==pIdx ) continue;
|
||||
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */
|
||||
jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, 0, r1,
|
||||
pIdx->nColumn);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, " missing from index ",
|
||||
P4_STATIC);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, pIdx->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
|
||||
jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
sqlite3VdbeJumpHere(v, jmp4);
|
||||
sqlite3VdbeJumpHere(v, jmp2);
|
||||
sqlite3VdbeResolveLabel(v, jmp3);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop);
|
||||
sqlite3VdbeJumpHere(v, loopTop-1);
|
||||
#ifndef SQLITE_OMIT_BTREECOUNT
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0,
|
||||
"wrong # of entries in index ", P4_STATIC);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
if( pPk==pIdx ) continue;
|
||||
addr = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2);
|
||||
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, j+2, 3);
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, 7+j, addr+8, 3);
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
|
||||
sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
|
||||
|
@ -226,7 +226,9 @@ static int lookupName(
|
||||
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
|
||||
NameContext *pTopNC = pNC; /* First namecontext in the list */
|
||||
Schema *pSchema = 0; /* Schema of the expression */
|
||||
int isTrigger = 0;
|
||||
int isTrigger = 0; /* True if resolved to a trigger column */
|
||||
Table *pTab = 0; /* Table hold the row */
|
||||
Column *pCol; /* A column of pTab */
|
||||
|
||||
assert( pNC ); /* the name context cannot be NULL. */
|
||||
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
|
||||
@ -267,9 +269,6 @@ static int lookupName(
|
||||
|
||||
if( pSrcList ){
|
||||
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
|
||||
Table *pTab;
|
||||
Column *pCol;
|
||||
|
||||
pTab = pItem->pTab;
|
||||
assert( pTab!=0 && pTab->zName!=0 );
|
||||
assert( pTab->nCol>0 );
|
||||
@ -329,9 +328,8 @@ static int lookupName(
|
||||
/* If we have not already resolved the name, then maybe
|
||||
** it is a new.* or old.* trigger argument reference
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
|
||||
int op = pParse->eTriggerOp;
|
||||
Table *pTab = 0;
|
||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
pExpr->iTable = 1;
|
||||
@ -354,7 +352,7 @@ static int lookupName(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){
|
||||
if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
|
||||
iCol = -1; /* IMP: R-44911-55124 */
|
||||
}
|
||||
if( iCol<pTab->nCol ){
|
||||
@ -381,7 +379,8 @@ static int lookupName(
|
||||
/*
|
||||
** Perhaps the name is a reference to the ROWID
|
||||
*/
|
||||
if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
|
||||
assert( pTab!=0 || cntTab==0 );
|
||||
if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
|
||||
cnt = 1;
|
||||
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
|
||||
pExpr->affinity = SQLITE_AFF_INTEGER;
|
||||
|
96
src/select.c
96
src/select.c
@ -803,24 +803,57 @@ static void selectInnerLoop(
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate a KeyInfo object sufficient for an index of N columns.
|
||||
**
|
||||
** Actually, always allocate one extra column for the rowid at the end
|
||||
** of the index. So the KeyInfo returned will have space sufficient for
|
||||
** N+1 columns.
|
||||
** Allocate a KeyInfo object sufficient for an index of N key columns and
|
||||
** X extra columns.
|
||||
*/
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N){
|
||||
KeyInfo *p = sqlite3DbMallocZero(db,
|
||||
sizeof(KeyInfo) + (N+1)*(sizeof(CollSeq*)+1));
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
KeyInfo *p = sqlite3DbMallocZero(0,
|
||||
sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1));
|
||||
if( p ){
|
||||
p->aSortOrder = (u8*)&p->aColl[N+1];
|
||||
p->aSortOrder = (u8*)&p->aColl[N+X];
|
||||
p->nField = (u16)N;
|
||||
p->nXField = (u16)X;
|
||||
p->enc = ENC(db);
|
||||
p->db = db;
|
||||
p->nRef = 1;
|
||||
}else{
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate a KeyInfo object
|
||||
*/
|
||||
void sqlite3KeyInfoUnref(KeyInfo *p){
|
||||
if( p ){
|
||||
assert( p->nRef>0 );
|
||||
p->nRef--;
|
||||
if( p->nRef==0 ) sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Make a new pointer to a KeyInfo object
|
||||
*/
|
||||
KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){
|
||||
if( p ){
|
||||
assert( p->nRef>0 );
|
||||
p->nRef++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Return TRUE if a KeyInfo object can be change. The KeyInfo object
|
||||
** can only be changed if this is just a single reference to the object.
|
||||
**
|
||||
** This routine is used only inside of assert() statements.
|
||||
*/
|
||||
int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
/*
|
||||
** Given an expression list, generate a KeyInfo structure that records
|
||||
** the collating sequence for each expression in that expression list.
|
||||
@ -833,8 +866,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N){
|
||||
**
|
||||
** Space to hold the KeyInfo structure is obtain from malloc. The calling
|
||||
** function is responsible for seeing that this structure is eventually
|
||||
** freed. Add the KeyInfo structure to the P4 field of an opcode using
|
||||
** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
|
||||
** freed.
|
||||
*/
|
||||
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
|
||||
int nExpr;
|
||||
@ -844,8 +876,9 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
|
||||
int i;
|
||||
|
||||
nExpr = pList->nExpr;
|
||||
pInfo = sqlite3KeyInfoAlloc(db, nExpr);
|
||||
pInfo = sqlite3KeyInfoAlloc(db, nExpr, 1);
|
||||
if( pInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
||||
for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
|
||||
CollSeq *pColl;
|
||||
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
|
||||
@ -1988,7 +2021,7 @@ static int multiSelect(
|
||||
|
||||
assert( p->pRightmost==p );
|
||||
nCol = p->pEList->nExpr;
|
||||
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol);
|
||||
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
|
||||
if( !pKeyInfo ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto multi_select_end;
|
||||
@ -2010,11 +2043,12 @@ static int multiSelect(
|
||||
break;
|
||||
}
|
||||
sqlite3VdbeChangeP2(v, addr, nCol);
|
||||
sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO);
|
||||
sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo),
|
||||
P4_KEYINFO);
|
||||
pLoop->addrOpenEphm[i] = -1;
|
||||
}
|
||||
}
|
||||
sqlite3DbFree(db, pKeyInfo);
|
||||
sqlite3KeyInfoUnref(pKeyInfo);
|
||||
}
|
||||
|
||||
multi_select_end:
|
||||
@ -2053,7 +2087,6 @@ static int generateOutputSubroutine(
|
||||
int regReturn, /* The return address register */
|
||||
int regPrev, /* Previous result register. No uniqueness if 0 */
|
||||
KeyInfo *pKeyInfo, /* For comparing with previous entry */
|
||||
int p4type, /* The p4 type for pKeyInfo */
|
||||
int iBreak /* Jump here if we hit the LIMIT */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
@ -2069,7 +2102,7 @@ static int generateOutputSubroutine(
|
||||
int j1, j2;
|
||||
j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev);
|
||||
j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
|
||||
(char*)pKeyInfo, p4type);
|
||||
(char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2);
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1);
|
||||
@ -2367,7 +2400,7 @@ static int multiSelectOrderBy(
|
||||
assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
|
||||
aPermute[i] = pItem->iOrderByCol - 1;
|
||||
}
|
||||
pKeyMerge = sqlite3KeyInfoAlloc(db, nOrderBy);
|
||||
pKeyMerge = sqlite3KeyInfoAlloc(db, nOrderBy, 1);
|
||||
if( pKeyMerge ){
|
||||
for(i=0; i<nOrderBy; i++){
|
||||
CollSeq *pColl;
|
||||
@ -2380,6 +2413,7 @@ static int multiSelectOrderBy(
|
||||
pOrderBy->a[i].pExpr =
|
||||
sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName);
|
||||
}
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyMerge) );
|
||||
pKeyMerge->aColl[i] = pColl;
|
||||
pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder;
|
||||
}
|
||||
@ -2405,8 +2439,9 @@ static int multiSelectOrderBy(
|
||||
regPrev = pParse->nMem+1;
|
||||
pParse->nMem += nExpr+1;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev);
|
||||
pKeyDup = sqlite3KeyInfoAlloc(db, nExpr);
|
||||
pKeyDup = sqlite3KeyInfoAlloc(db, nExpr, 1);
|
||||
if( pKeyDup ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
|
||||
for(i=0; i<nExpr; i++){
|
||||
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
|
||||
pKeyDup->aSortOrder[i] = 0;
|
||||
@ -2488,7 +2523,7 @@ static int multiSelectOrderBy(
|
||||
VdbeNoopComment((v, "Output routine for A"));
|
||||
addrOutA = generateOutputSubroutine(pParse,
|
||||
p, &destA, pDest, regOutA,
|
||||
regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd);
|
||||
regPrev, pKeyDup, labelEnd);
|
||||
|
||||
/* Generate a subroutine that outputs the current row of the B
|
||||
** select as the next output row of the compound select.
|
||||
@ -2497,8 +2532,9 @@ static int multiSelectOrderBy(
|
||||
VdbeNoopComment((v, "Output routine for B"));
|
||||
addrOutB = generateOutputSubroutine(pParse,
|
||||
p, &destB, pDest, regOutB,
|
||||
regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd);
|
||||
regPrev, pKeyDup, labelEnd);
|
||||
}
|
||||
sqlite3KeyInfoUnref(pKeyDup);
|
||||
|
||||
/* Generate a subroutine to run when the results from select A
|
||||
** are exhausted and only data in select B remains.
|
||||
@ -2577,7 +2613,7 @@ static int multiSelectOrderBy(
|
||||
sqlite3VdbeResolveLabel(v, labelCmpr);
|
||||
sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
|
||||
sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy,
|
||||
(char*)pKeyMerge, P4_KEYINFO_HANDOFF);
|
||||
(char*)pKeyMerge, P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
|
||||
|
||||
@ -3803,7 +3839,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
}else{
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4258,7 +4294,7 @@ int sqlite3Select(
|
||||
p->addrOpenEphm[2] = addrSortIndex =
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
}else{
|
||||
addrSortIndex = -1;
|
||||
}
|
||||
@ -4286,7 +4322,7 @@ int sqlite3Select(
|
||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
sDistinct.tabTnct, 0, 0,
|
||||
(char*)keyInfoFromExprList(pParse, p->pEList),
|
||||
P4_KEYINFO_HANDOFF);
|
||||
P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
||||
}else{
|
||||
@ -4410,7 +4446,7 @@ int sqlite3Select(
|
||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
|
||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
||||
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
||||
0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
0, (char*)pKeyInfo, P4_KEYINFO);
|
||||
|
||||
/* Initialize memory locations used by GROUP BY aggregate processing
|
||||
*/
|
||||
@ -4524,7 +4560,7 @@ int sqlite3Select(
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
(char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
|
||||
j1 = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1);
|
||||
|
||||
@ -4650,13 +4686,13 @@ int sqlite3Select(
|
||||
}
|
||||
if( pBest ){
|
||||
iRoot = pBest->tnum;
|
||||
pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
|
||||
pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
|
||||
}
|
||||
|
||||
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb);
|
||||
sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1);
|
||||
if( pKeyInfo ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||
|
@ -496,6 +496,7 @@ int sqlite3_exec(
|
||||
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
|
||||
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
|
||||
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
|
||||
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||
|
@ -1418,6 +1418,7 @@ struct Table {
|
||||
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
|
||||
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
|
||||
#define TF_Virtual 0x10 /* Is a virtual table */
|
||||
#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */
|
||||
|
||||
|
||||
/*
|
||||
@ -1433,6 +1434,9 @@ struct Table {
|
||||
# define IsHiddenColumn(X) 0
|
||||
#endif
|
||||
|
||||
/* Does the table have a rowid */
|
||||
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
|
||||
|
||||
/*
|
||||
** Each foreign key constraint is an instance of the following structure.
|
||||
**
|
||||
@ -1447,26 +1451,35 @@ struct Table {
|
||||
** );
|
||||
**
|
||||
** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
|
||||
** Equivalent names:
|
||||
**
|
||||
** from-table == child-table
|
||||
** to-table == parent-table
|
||||
**
|
||||
** Each REFERENCES clause generates an instance of the following structure
|
||||
** which is attached to the from-table. The to-table need not exist when
|
||||
** the from-table is created. The existence of the to-table is not checked.
|
||||
**
|
||||
** The list of all parents for child Table X is held at X.pFKey.
|
||||
**
|
||||
** A list of all children for a table named Z (which might not even exist)
|
||||
** is held in Schema.fkeyHash with a hash key of Z.
|
||||
*/
|
||||
struct FKey {
|
||||
Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */
|
||||
FKey *pNextFrom; /* Next foreign key in pFrom */
|
||||
FKey *pNextFrom; /* Next FKey with the same in pFrom. Next parent of pFrom */
|
||||
char *zTo; /* Name of table that the key points to (aka: Parent) */
|
||||
FKey *pNextTo; /* Next foreign key on table named zTo */
|
||||
FKey *pPrevTo; /* Previous foreign key on table named zTo */
|
||||
FKey *pNextTo; /* Next with the same zTo. Next child of zTo. */
|
||||
FKey *pPrevTo; /* Previous with the same zTo */
|
||||
int nCol; /* Number of columns in this key */
|
||||
/* EV: R-30323-21917 */
|
||||
u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
|
||||
u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
|
||||
Trigger *apTrigger[2]; /* Triggers for aAction[] actions */
|
||||
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
|
||||
int iFrom; /* Index of column in pFrom */
|
||||
char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
|
||||
} aCol[1]; /* One entry for each of nCol column s */
|
||||
u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
|
||||
u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
|
||||
Trigger *apTrigger[2];/* Triggers for aAction[] actions */
|
||||
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
|
||||
int iFrom; /* Index of column in pFrom */
|
||||
char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
|
||||
} aCol[1]; /* One entry for each of nCol columns */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1519,9 +1532,11 @@ struct FKey {
|
||||
** for the rowid at the end.
|
||||
*/
|
||||
struct KeyInfo {
|
||||
sqlite3 *db; /* The database connection */
|
||||
u32 nRef; /* Number of references to this KeyInfo object */
|
||||
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
|
||||
u16 nField; /* Maximum index for aColl[] and aSortOrder[] */
|
||||
u16 nField; /* Number of key columns in the index */
|
||||
u16 nXField; /* Number of columns beyond the key columns */
|
||||
sqlite3 *db; /* The database connection */
|
||||
u8 *aSortOrder; /* Sort order for each column. */
|
||||
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
|
||||
};
|
||||
@ -1544,7 +1559,6 @@ struct UnpackedRecord {
|
||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||
u16 nField; /* Number of entries in apMem[] */
|
||||
u8 flags; /* Boolean settings. UNPACKED_... below */
|
||||
i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
|
||||
Mem *aMem; /* Values */
|
||||
};
|
||||
|
||||
@ -1553,7 +1567,6 @@ struct UnpackedRecord {
|
||||
*/
|
||||
#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */
|
||||
#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */
|
||||
#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */
|
||||
|
||||
/*
|
||||
** Each SQL index is represented in memory by an
|
||||
@ -1583,7 +1596,7 @@ struct UnpackedRecord {
|
||||
*/
|
||||
struct Index {
|
||||
char *zName; /* Name of this index */
|
||||
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
|
||||
tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
|
||||
Table *pTable; /* The SQL table being indexed */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
@ -1592,13 +1605,17 @@ struct Index {
|
||||
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
|
||||
char **azColl; /* Array of collation sequence names for index */
|
||||
Expr *pPartIdxWhere; /* WHERE clause for partial indices */
|
||||
KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */
|
||||
int tnum; /* DB Page containing root of this index */
|
||||
LogEst szIdxRow; /* Estimated average row size in bytes */
|
||||
u16 nColumn; /* Number of columns in table used by this index */
|
||||
u16 nKeyCol; /* Number of columns forming the key */
|
||||
u16 nColumn; /* Number of columns stored in the index */
|
||||
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
|
||||
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 */
|
||||
unsigned isResized:1; /* True if resizeIndexObject() has been called */
|
||||
unsigned isCovering:1; /* True if this is a covering index */
|
||||
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
|
||||
@ -2277,6 +2294,8 @@ struct Parse {
|
||||
/* Information used while coding trigger programs. */
|
||||
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
||||
Table *pTriggerTab; /* Table triggers are being coded for */
|
||||
int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
|
||||
int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */
|
||||
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
|
||||
u32 oldmask; /* Mask of old.* columns referenced */
|
||||
u32 newmask; /* Mask of new.* columns referenced */
|
||||
@ -2289,6 +2308,7 @@ struct Parse {
|
||||
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
|
||||
@ -2302,7 +2322,6 @@ struct Parse {
|
||||
#endif
|
||||
char **azVar; /* Pointers to names of parameters */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
int *aAlias; /* Register used to hold aliased result */
|
||||
const char *zTail; /* All SQL text past the last semicolon parsed */
|
||||
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
||||
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
|
||||
@ -2771,6 +2790,8 @@ void sqlite3BeginParse(Parse*,int);
|
||||
void sqlite3CommitInternalChanges(sqlite3*);
|
||||
Table *sqlite3ResultSetOfSelect(Parse*,Select*);
|
||||
void sqlite3OpenMasterTable(Parse *, int);
|
||||
Index *sqlite3PrimaryKeyIndex(Table*);
|
||||
i16 sqlite3ColumnOfIndex(Index*, i16);
|
||||
void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
|
||||
void sqlite3AddColumn(Parse*,Token*);
|
||||
void sqlite3AddNotNull(Parse*, int);
|
||||
@ -2779,7 +2800,7 @@ void sqlite3AddCheckConstraint(Parse*, Expr*);
|
||||
void sqlite3AddColumnType(Parse*,Token*);
|
||||
void sqlite3AddDefaultValue(Parse*,ExprSpan*);
|
||||
void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
sqlite3_vfs**,char**,char **);
|
||||
Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
|
||||
@ -2832,6 +2853,7 @@ void sqlite3SrcListShiftJoinType(SrcList*);
|
||||
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
|
||||
void sqlite3IdListDelete(sqlite3*, IdList*);
|
||||
void sqlite3SrcListDelete(sqlite3*, SrcList*);
|
||||
Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
|
||||
Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
|
||||
Expr*, int, int);
|
||||
void sqlite3DropIndex(Parse*, SrcList*, int);
|
||||
@ -2908,17 +2930,19 @@ int sqlite3ExprCanBeNull(const Expr*);
|
||||
void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int);
|
||||
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
|
||||
int sqlite3IsRowid(const char*);
|
||||
void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
|
||||
void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
|
||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
||||
int*,int,int,int,int,int*);
|
||||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
||||
u8,u8,int,int*);
|
||||
void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*);
|
||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||
void sqlite3MultiWrite(Parse*);
|
||||
void sqlite3MayAbort(Parse*);
|
||||
void sqlite3HaltConstraint(Parse*, int, int, char*, int);
|
||||
void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
|
||||
void sqlite3UniqueConstraint(Parse*, int, Index*);
|
||||
void sqlite3RowidConstraint(Parse*, int, Table*);
|
||||
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
||||
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
|
||||
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
|
||||
@ -3136,8 +3160,13 @@ void sqlite3MinimumFileFormat(Parse*, int, int);
|
||||
void sqlite3SchemaClear(void *);
|
||||
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
|
||||
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int);
|
||||
KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
|
||||
void sqlite3KeyInfoUnref(KeyInfo*);
|
||||
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
|
||||
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
|
||||
#ifdef SQLITE_DEBUG
|
||||
int sqlite3KeyInfoIsWriteable(KeyInfo*);
|
||||
#endif
|
||||
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
|
||||
void (*)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
|
||||
|
@ -506,7 +506,6 @@ abort_parse:
|
||||
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
|
||||
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
|
||||
sqlite3DbFree(db, pParse->azVar);
|
||||
sqlite3DbFree(db, pParse->aAlias);
|
||||
while( pParse->pAinc ){
|
||||
AutoincInfo *p = pParse->pAinc;
|
||||
pParse->pAinc = p->pNext;
|
||||
|
215
src/update.c
215
src/update.c
@ -95,18 +95,22 @@ void sqlite3Update(
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
Table *pTab; /* The table to be updated */
|
||||
int addr = 0; /* VDBE instruction address of the start of the loop */
|
||||
int addrTop = 0; /* VDBE instruction address of the start of the loop */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Index *pIdx; /* For looping over indices */
|
||||
Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
|
||||
int nIdx; /* Number of indices that need updating */
|
||||
int iCur; /* VDBE Cursor number of pTab */
|
||||
int iDataCur; /* Cursor for the canonical data btree */
|
||||
int iIdxCur; /* Cursor for the first index */
|
||||
sqlite3 *db; /* The database structure */
|
||||
int *aRegIdx = 0; /* One register assigned to each index to be updated */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
int chngRowid; /* True if the record number is being changed */
|
||||
u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */
|
||||
u8 chngRowid; /* Rowid changed in a normal table */
|
||||
u8 chngKey; /* Either chngPk or chngRowid */
|
||||
Expr *pRowidExpr = 0; /* Expression defining the new record number */
|
||||
int openAll = 0; /* True if all indices need to be opened */
|
||||
AuthContext sContext; /* The authorization context */
|
||||
@ -114,6 +118,8 @@ void sqlite3Update(
|
||||
int iDb; /* Database containing the table being updated */
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int hasFK; /* True if foreign key processing is required */
|
||||
int labelBreak; /* Jump here to break out of UPDATE loop */
|
||||
int labelContinue; /* Jump here to continue next step of UPDATE loop */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True when updating a view (INSTEAD OF trigger) */
|
||||
@ -121,6 +127,7 @@ void sqlite3Update(
|
||||
int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
|
||||
#endif
|
||||
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
|
||||
int iEph = 0; /* Ephemeral table holding all primary key values */
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
@ -129,6 +136,7 @@ void sqlite3Update(
|
||||
int regNew; /* Content of the NEW.* table in triggers */
|
||||
int regOld = 0; /* Content of OLD.* table in triggers */
|
||||
int regRowSet = 0; /* Rowset of rows to be updated */
|
||||
int regKey = 0; /* composite PRIMARY KEY value */
|
||||
|
||||
memset(&sContext, 0, sizeof(sContext));
|
||||
db = pParse->db;
|
||||
@ -175,8 +183,14 @@ void sqlite3Update(
|
||||
** need to occur right after the database cursor. So go ahead and
|
||||
** allocate enough space, just in case.
|
||||
*/
|
||||
pTabList->a[0].iCursor = iCur = pParse->nTab++;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
pTabList->a[0].iCursor = iDataCur = pParse->nTab++;
|
||||
iIdxCur = iDataCur+1;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
|
||||
if( pIdx->autoIndex==2 && pPk!=0 ){
|
||||
iDataCur = pParse->nTab;
|
||||
pTabList->a[0].iCursor = iDataCur;
|
||||
}
|
||||
pParse->nTab++;
|
||||
}
|
||||
|
||||
@ -191,7 +205,7 @@ void sqlite3Update(
|
||||
** column to be updated, make sure we have authorization to change
|
||||
** that column.
|
||||
*/
|
||||
chngRowid = 0;
|
||||
chngRowid = chngPk = 0;
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
|
||||
goto update_cleanup;
|
||||
@ -201,13 +215,15 @@ void sqlite3Update(
|
||||
if( j==pTab->iPKey ){
|
||||
chngRowid = 1;
|
||||
pRowidExpr = pChanges->a[i].pExpr;
|
||||
}else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
|
||||
chngPk = 1;
|
||||
}
|
||||
aXRef[j] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j>=pTab->nCol ){
|
||||
if( sqlite3IsRowid(pChanges->a[i].zName) ){
|
||||
if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
|
||||
j = -1;
|
||||
chngRowid = 1;
|
||||
pRowidExpr = pChanges->a[i].pExpr;
|
||||
@ -231,26 +247,29 @@ void sqlite3Update(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
assert( (chngRowid & chngPk)==0 );
|
||||
assert( chngRowid==0 || chngRowid==1 );
|
||||
assert( chngPk==0 || chngPk==1 );
|
||||
chngKey = chngRowid + chngPk;
|
||||
|
||||
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid);
|
||||
hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
|
||||
|
||||
/* Allocate memory for the array aRegIdx[]. There is one entry in the
|
||||
** array for each index associated with table being updated. Fill in
|
||||
** the value with a register number for indices that are to be used
|
||||
** and with zero for unused indices.
|
||||
*/
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
|
||||
if( nIdx>0 ){
|
||||
aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
|
||||
if( aRegIdx==0 ) goto update_cleanup;
|
||||
}
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int reg;
|
||||
if( hasFK || chngRowid || pIdx->pPartIdxWhere ){
|
||||
if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
|
||||
reg = ++pParse->nMem;
|
||||
}else{
|
||||
reg = 0;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
if( aXRef[pIdx->aiColumn[i]]>=0 ){
|
||||
reg = ++pParse->nMem;
|
||||
break;
|
||||
@ -280,11 +299,11 @@ void sqlite3Update(
|
||||
/* Allocate required registers. */
|
||||
regRowSet = ++pParse->nMem;
|
||||
regOldRowid = regNewRowid = ++pParse->nMem;
|
||||
if( pTrigger || hasFK ){
|
||||
if( chngPk || pTrigger || hasFK ){
|
||||
regOld = pParse->nMem + 1;
|
||||
pParse->nMem += pTab->nCol;
|
||||
}
|
||||
if( chngRowid || pTrigger || hasFK ){
|
||||
if( chngKey || pTrigger || hasFK ){
|
||||
regNewRowid = ++pParse->nMem;
|
||||
}
|
||||
regNew = pParse->nMem + 1;
|
||||
@ -300,7 +319,7 @@ void sqlite3Update(
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( isView ){
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
|
||||
sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -313,24 +332,49 @@ void sqlite3Update(
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo);
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo);
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else{
|
||||
int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
regKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
for(i=0; i<nPk; i++){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
|
||||
iPk+i);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
|
||||
sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
okOnePass = 0;
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
@ -345,7 +389,10 @@ void sqlite3Update(
|
||||
** action, then we need to open all indices because we might need
|
||||
** to be deleting some records.
|
||||
*/
|
||||
if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
|
||||
if( !okOnePass && HasRowid(pTab) ){
|
||||
sqlite3OpenTable(pParse, iDataCur, iDb, pTab, OP_OpenWrite);
|
||||
}
|
||||
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
|
||||
if( onError==OE_Replace ){
|
||||
openAll = 1;
|
||||
}else{
|
||||
@ -360,53 +407,58 @@ void sqlite3Update(
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb,
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
assert( pParse->nTab>iCur+i+1 );
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, iIdxCur+i, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
assert( pParse->nTab>iIdxCur+i );
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( okOnePass ){
|
||||
int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
|
||||
addr = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, a1);
|
||||
labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
if( pPk ){
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
|
||||
addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
||||
}else if( okOnePass ){
|
||||
labelContinue = labelBreak;
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, regOldRowid, labelBreak);
|
||||
}else{
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
|
||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
|
||||
regOldRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
}
|
||||
|
||||
/* Make cursor iCur point to the record that is being updated. If
|
||||
** this record does not exist for some reason (deleted by a trigger,
|
||||
** for example, then jump to the next iteration of the RowSet loop. */
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
|
||||
|
||||
/* If the record number will change, set register regNewRowid to
|
||||
** contain the new value. If the record number is not being modified,
|
||||
** then regNewRowid is the same register as regOldRowid, which is
|
||||
** already populated. */
|
||||
assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid );
|
||||
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
|
||||
if( chngRowid ){
|
||||
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
|
||||
}
|
||||
|
||||
/* If there are triggers on this table, populate an array of registers
|
||||
** with the required old.* column data. */
|
||||
if( hasFK || pTrigger ){
|
||||
/* Compute the old pre-UPDATE content of the row being changed, if that
|
||||
** information is needed */
|
||||
if( chngPk || hasFK || pTrigger ){
|
||||
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
|
||||
oldmask |= sqlite3TriggerColmask(pParse,
|
||||
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
|
||||
);
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i);
|
||||
if( oldmask==0xffffffff
|
||||
|| (i<32 && (oldmask & (1<<i)))
|
||||
|| (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
|
||||
){
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
|
||||
}
|
||||
}
|
||||
if( chngRowid==0 ){
|
||||
if( chngRowid==0 && pPk==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
|
||||
}
|
||||
}
|
||||
@ -443,8 +495,7 @@ void sqlite3Update(
|
||||
*/
|
||||
testcase( i==31 );
|
||||
testcase( i==32 );
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
|
||||
sqlite3ColumnDefault(v, pTab, i, regNew+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -456,7 +507,7 @@ void sqlite3Update(
|
||||
sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
|
||||
sqlite3TableAffinityStr(v, pTab);
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
|
||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
|
||||
|
||||
/* The row-trigger may have deleted the row being updated. In this
|
||||
** case, jump to the next row. No updates or AFTER triggers are
|
||||
@ -464,7 +515,11 @@ void sqlite3Update(
|
||||
** is deleted or renamed by a BEFORE trigger - is left undefined in the
|
||||
** documentation.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
|
||||
if( pPk ){
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
||||
}
|
||||
|
||||
/* If it did not delete it, the row-trigger may still have modified
|
||||
** some of the columns of the row being updated. Load the values for
|
||||
@ -473,8 +528,7 @@ void sqlite3Update(
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( aXRef[i]<0 && i!=pTab->iPKey ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
|
||||
sqlite3ColumnDefault(v, pTab, i, regNew+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -483,36 +537,46 @@ void sqlite3Update(
|
||||
int j1; /* Address of jump instruction */
|
||||
|
||||
/* Do constraint checks. */
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
|
||||
aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
|
||||
assert( regOldRowid>0 );
|
||||
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
|
||||
regNewRowid, regOldRowid, chngKey, onError, labelContinue, 0);
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngRowid);
|
||||
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
|
||||
}
|
||||
|
||||
/* Delete the index entries associated with the current record. */
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
|
||||
if( pPk ){
|
||||
j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, 0);
|
||||
}else{
|
||||
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
|
||||
}
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
|
||||
|
||||
/* If changing the record number, delete the old record. */
|
||||
if( hasFK || chngRowid ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
|
||||
if( hasFK || chngKey || pPk!=0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
|
||||
}
|
||||
if( sqlite3VdbeCurrentAddr(v)==j1+1 ){
|
||||
sqlite3VdbeChangeToNoop(v, j1);
|
||||
}else{
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, j1);
|
||||
|
||||
if( hasFK ){
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngRowid);
|
||||
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0);
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regNewRowid, aRegIdx, 1, 0, 0);
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
** to the row just updated. */
|
||||
if( hasFK ){
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngRowid);
|
||||
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,22 +587,27 @@ void sqlite3Update(
|
||||
}
|
||||
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||
TRIGGER_AFTER, pTab, regOldRowid, onError, addr);
|
||||
TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
|
||||
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
if( pPk ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop);
|
||||
}else if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, labelBreak);
|
||||
|
||||
/* Close all tables */
|
||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||
assert( aRegIdx );
|
||||
if( openAll || aRegIdx[i]>0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iIdxCur, 0);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
|
||||
if( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0);
|
||||
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
|
@ -234,7 +234,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
|
||||
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
|
||||
" AND rootpage>0"
|
||||
" AND coalesce(rootpage,1)>0"
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
rc = execExecSql(db, pzErrMsg,
|
||||
@ -255,7 +255,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
"|| ' SELECT * FROM main.' || quote(name) || ';'"
|
||||
"FROM main.sqlite_master "
|
||||
"WHERE type = 'table' AND name!='sqlite_sequence' "
|
||||
" AND rootpage>0"
|
||||
" AND coalesce(rootpage,1)>0"
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
|
279
src/vdbe.c
279
src/vdbe.c
@ -804,12 +804,13 @@ case OP_Yield: { /* in1 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: HaltIfNull P1 P2 P3 P4 *
|
||||
/* Opcode: HaltIfNull P1 P2 P3 P4 P5
|
||||
** Synopsis: if r[P3] null then halt
|
||||
**
|
||||
** Check the value in register P3. If it is NULL then Halt using
|
||||
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
|
||||
** value in register P3 is not NULL, then this routine is a no-op.
|
||||
** The P5 parameter should be 1.
|
||||
*/
|
||||
case OP_HaltIfNull: { /* in3 */
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
@ -817,7 +818,7 @@ case OP_HaltIfNull: { /* in3 */
|
||||
/* Fall through into OP_Halt */
|
||||
}
|
||||
|
||||
/* Opcode: Halt P1 P2 * P4 *
|
||||
/* Opcode: Halt P1 P2 * P4 P5
|
||||
**
|
||||
** Exit immediately. All open cursors, etc are closed
|
||||
** automatically.
|
||||
@ -832,11 +833,25 @@ case OP_HaltIfNull: { /* in3 */
|
||||
**
|
||||
** If P4 is not null then it is an error message string.
|
||||
**
|
||||
** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
|
||||
**
|
||||
** 0: (no change)
|
||||
** 1: NOT NULL contraint failed: P4
|
||||
** 2: UNIQUE constraint failed: P4
|
||||
** 3: CHECK constraint failed: P4
|
||||
** 4: FOREIGN KEY constraint failed: P4
|
||||
**
|
||||
** If P5 is not zero and P4 is NULL, then everything after the ":" is
|
||||
** omitted.
|
||||
**
|
||||
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
|
||||
** every program. So a jump past the last instruction of the program
|
||||
** is the same as executing Halt.
|
||||
*/
|
||||
case OP_Halt: {
|
||||
const char *zType;
|
||||
const char *zLogFmt;
|
||||
|
||||
if( pOp->p1==SQLITE_OK && p->pFrame ){
|
||||
/* Halt the sub-program. Return control to the parent frame. */
|
||||
VdbeFrame *pFrame = p->pFrame;
|
||||
@ -857,18 +872,34 @@ case OP_Halt: {
|
||||
aMem = p->aMem;
|
||||
break;
|
||||
}
|
||||
|
||||
p->rc = pOp->p1;
|
||||
p->errorAction = (u8)pOp->p2;
|
||||
p->pc = pc;
|
||||
if( pOp->p4.z ){
|
||||
assert( p->rc!=SQLITE_OK );
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
|
||||
}else if( p->rc ){
|
||||
testcase( sqlite3GlobalConfig.xLog!=0 );
|
||||
sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
|
||||
if( p->rc ){
|
||||
if( pOp->p5 ){
|
||||
static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
|
||||
"FOREIGN KEY" };
|
||||
assert( pOp->p5>=1 && pOp->p5<=4 );
|
||||
testcase( pOp->p5==1 );
|
||||
testcase( pOp->p5==2 );
|
||||
testcase( pOp->p5==3 );
|
||||
testcase( pOp->p5==4 );
|
||||
zType = azType[pOp->p5-1];
|
||||
}else{
|
||||
zType = 0;
|
||||
}
|
||||
zLogFmt = "abort at %d in [%s]: %s";
|
||||
if( zType && pOp->p4.z ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s",
|
||||
zType, pOp->p4.z);
|
||||
}else if( pOp->p4.z ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
|
||||
}else if( zType ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
|
||||
}else{
|
||||
zLogFmt = "abort at %d in [%s]";
|
||||
}
|
||||
sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg);
|
||||
}
|
||||
rc = sqlite3VdbeHalt(p);
|
||||
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
|
||||
@ -1119,7 +1150,7 @@ case OP_Copy: {
|
||||
** during the lifetime of the copy. Use OP_Copy to make a complete
|
||||
** copy.
|
||||
*/
|
||||
case OP_SCopy: { /* in1, out2 */
|
||||
case OP_SCopy: { /* out2 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
pOut = &aMem[pOp->p2];
|
||||
assert( pOut!=pIn1 );
|
||||
@ -1127,12 +1158,11 @@ case OP_SCopy: { /* in1, out2 */
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
|
||||
#endif
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ResultRow P1 P2 * * *
|
||||
** Synopsis: output=r[P1].. columns=P1
|
||||
** Synopsis: output=r[P1@P2]
|
||||
**
|
||||
** The registers P1 through P1+P2-1 contain a single row of
|
||||
** results. This opcode causes the sqlite3_step() call to terminate
|
||||
@ -1204,7 +1234,7 @@ case OP_ResultRow: {
|
||||
}
|
||||
|
||||
/* Opcode: Concat P1 P2 P3 * *
|
||||
** Synopsis: r[P3]=r[P2]+r[P3]
|
||||
** Synopsis: r[P3]=r[P2]+r[P1]
|
||||
**
|
||||
** Add the text in register P1 onto the end of the text in
|
||||
** register P2 and store the result in register P3.
|
||||
@ -1749,7 +1779,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
#endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */
|
||||
|
||||
/* Opcode: Lt P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] < r[P3]
|
||||
** Synopsis: if r[P1]<r[P3] goto P3
|
||||
**
|
||||
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
|
||||
** jump to address P2.
|
||||
@ -1784,7 +1814,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
** bit set.
|
||||
*/
|
||||
/* Opcode: Ne P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] != r[P3]
|
||||
** Synopsis: if r[P1]!=r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are not equal. See the Lt opcode for
|
||||
@ -1797,7 +1827,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Eq P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] == r[P3]
|
||||
** Synopsis: if r[P1]==r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are equal.
|
||||
@ -1810,21 +1840,21 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
*/
|
||||
/* Opcode: Le P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] <= r[P3]
|
||||
** Synopsis: if r[P1]<=r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is less than or equal to the content of
|
||||
** register P1. See the Lt opcode for additional information.
|
||||
*/
|
||||
/* Opcode: Gt P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] > r[P3]
|
||||
** Synopsis: if r[P1]>r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is greater than the content of
|
||||
** register P1. See the Lt opcode for additional information.
|
||||
*/
|
||||
/* Opcode: Ge P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1] >= r[P3]
|
||||
** Synopsis: if r[P1]>=r[P3] goto P2
|
||||
**
|
||||
** This works just like the Lt opcode except that the jump is taken if
|
||||
** the content of register P3 is greater than or equal to the content of
|
||||
@ -3258,8 +3288,9 @@ case OP_OpenWrite: {
|
||||
}
|
||||
if( pOp->p4type==P4_KEYINFO ){
|
||||
pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pKeyInfo->enc = ENC(p->db);
|
||||
nField = pKeyInfo->nField+1;
|
||||
assert( pKeyInfo->enc==ENC(db) );
|
||||
assert( pKeyInfo->db==db );
|
||||
nField = pKeyInfo->nField+pKeyInfo->nXField;
|
||||
}else if( pOp->p4type==P4_INT32 ){
|
||||
nField = pOp->p4.i;
|
||||
}
|
||||
@ -3315,13 +3346,14 @@ case OP_OpenWrite: {
|
||||
case OP_OpenAutoindex:
|
||||
case OP_OpenEphemeral: {
|
||||
VdbeCursor *pCx;
|
||||
KeyInfo *pKeyInfo;
|
||||
|
||||
static const int vfsFlags =
|
||||
SQLITE_OPEN_READWRITE |
|
||||
SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_EXCLUSIVE |
|
||||
SQLITE_OPEN_DELETEONCLOSE |
|
||||
SQLITE_OPEN_TRANSIENT_DB;
|
||||
|
||||
assert( pOp->p1>=0 );
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
@ -3337,16 +3369,16 @@ case OP_OpenEphemeral: {
|
||||
** opening it. If a transient table is required, just use the
|
||||
** automatically created table with root-page 1 (an BLOB_INTKEY table).
|
||||
*/
|
||||
if( pOp->p4.pKeyInfo ){
|
||||
if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
|
||||
int pgno;
|
||||
assert( pOp->p4type==P4_KEYINFO );
|
||||
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pgno==MASTER_ROOT+1 );
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
|
||||
(KeyInfo*)pOp->p4.z, pCx->pCursor);
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pCx->pKeyInfo->enc = ENC(p->db);
|
||||
assert( pKeyInfo->db==db );
|
||||
assert( pKeyInfo->enc==ENC(db) );
|
||||
pCx->pKeyInfo = pKeyInfo;
|
||||
rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
|
||||
}
|
||||
pCx->isTable = 0;
|
||||
}else{
|
||||
@ -3359,8 +3391,7 @@ case OP_OpenEphemeral: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SorterOpen P1 P2 * P4 *
|
||||
** Synopsis: nColumn=P2
|
||||
/* Opcode: SorterOpen P1 * * P4 *
|
||||
**
|
||||
** This opcode works like OP_OpenEphemeral except that it opens
|
||||
** a transient index that is specifically designed to sort large
|
||||
@ -3372,7 +3403,8 @@ case OP_SorterOpen: {
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->pKeyInfo = pOp->p4.pKeyInfo;
|
||||
pCx->pKeyInfo->enc = ENC(p->db);
|
||||
assert( pCx->pKeyInfo->db==db );
|
||||
assert( pCx->pKeyInfo->enc==ENC(db) );
|
||||
pCx->isSorter = 1;
|
||||
rc = sqlite3VdbeSorterInit(db, pCx);
|
||||
break;
|
||||
@ -3667,6 +3699,8 @@ case OP_Seek: { /* in2 */
|
||||
** Cursor P1 is on an index btree. If the record identified by P3 and P4
|
||||
** is a prefix of any entry in P1 then a jump is made to P2 and
|
||||
** P1 is left pointing at the matching entry.
|
||||
**
|
||||
** See also: NotFound, NoConflict, NotExists. SeekGe
|
||||
*/
|
||||
/* Opcode: NotFound P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
@ -3681,20 +3715,41 @@ case OP_Seek: { /* in2 */
|
||||
** falls through to the next instruction and P1 is left pointing at the
|
||||
** matching entry.
|
||||
**
|
||||
** See also: Found, NotExists, IsUnique
|
||||
** See also: Found, NotExists, NoConflict
|
||||
*/
|
||||
/* Opcode: NoConflict P1 P2 P3 P4 *
|
||||
** Synopsis: key=r[P3@P4]
|
||||
**
|
||||
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
|
||||
** P4>0 then register P3 is the first of P4 registers that form an unpacked
|
||||
** record.
|
||||
**
|
||||
** Cursor P1 is on an index btree. If the record identified by P3 and P4
|
||||
** contains any NULL value, jump immediately to P2. If all terms of the
|
||||
** record are not-NULL then a check is done to determine if any row in the
|
||||
** P1 index btree has a matching key prefix. If there are no matches, jump
|
||||
** immediately to P2. If there is a match, fall through and leave the P1
|
||||
** cursor pointing to the matching row.
|
||||
**
|
||||
** This opcode is similar to OP_NotFound with the exceptions that the
|
||||
** branch is always taken if any part of the search key input is NULL.
|
||||
**
|
||||
** See also: NotFound, Found, NotExists
|
||||
*/
|
||||
case OP_NoConflict: /* jump, in3 */
|
||||
case OP_NotFound: /* jump, in3 */
|
||||
case OP_Found: { /* jump, in3 */
|
||||
int alreadyExists;
|
||||
int ii;
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
char *pFree;
|
||||
UnpackedRecord *pIdxKey;
|
||||
UnpackedRecord r;
|
||||
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
|
||||
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*4 + 7];
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
sqlite3_found_count++;
|
||||
if( pOp->opcode!=OP_NoConflict ) sqlite3_found_count++;
|
||||
#endif
|
||||
|
||||
alreadyExists = 0;
|
||||
@ -3711,7 +3766,13 @@ case OP_Found: { /* jump, in3 */
|
||||
r.nField = (u16)pOp->p4.i;
|
||||
r.aMem = pIn3;
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<r.nField; i++){
|
||||
assert( memIsValid(&r.aMem[i]) );
|
||||
if( i ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
r.flags = UNPACKED_PREFIX_MATCH;
|
||||
pIdxKey = &r;
|
||||
@ -3725,6 +3786,17 @@ case OP_Found: { /* jump, in3 */
|
||||
sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
|
||||
pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
|
||||
}
|
||||
if( pOp->opcode==OP_NoConflict ){
|
||||
/* For the OP_NoConflict opcode, take the jump if any of the
|
||||
** input fields are NULL, since any key with a NULL will not
|
||||
** conflict */
|
||||
for(ii=0; ii<r.nField; ii++){
|
||||
if( r.aMem[ii].flags & MEM_Null ){
|
||||
pc = pOp->p2 - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
|
||||
if( pOp->p4.i==0 ){
|
||||
sqlite3DbFree(db, pFree);
|
||||
@ -3732,7 +3804,9 @@ case OP_Found: { /* jump, in3 */
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
}
|
||||
pC->seekResult = res;
|
||||
alreadyExists = (res==0);
|
||||
pC->nullRow = 1-alreadyExists;
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
}
|
||||
@ -3744,107 +3818,19 @@ case OP_Found: { /* jump, in3 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IsUnique P1 P2 P3 P4 *
|
||||
**
|
||||
** Cursor P1 is open on an index b-tree - that is to say, a btree which
|
||||
** no data and where the key are records generated by OP_MakeRecord with
|
||||
** the list field being the integer ROWID of the entry that the index
|
||||
** entry refers to.
|
||||
**
|
||||
** The P3 register contains an integer record number. Call this record
|
||||
** number R. Register P4 is the first in a set of N contiguous registers
|
||||
** that make up an unpacked index key that can be used with cursor P1.
|
||||
** The value of N can be inferred from the cursor. N includes the rowid
|
||||
** value appended to the end of the index record. This rowid value may
|
||||
** or may not be the same as R.
|
||||
**
|
||||
** If any of the N registers beginning with register P4 contains a NULL
|
||||
** value, jump immediately to P2.
|
||||
**
|
||||
** Otherwise, this instruction checks if cursor P1 contains an entry
|
||||
** where the first (N-1) fields match but the rowid value at the end
|
||||
** of the index entry is not R. If there is no such entry, control jumps
|
||||
** to instruction P2. Otherwise, the rowid of the conflicting index
|
||||
** entry is copied to register P3 and control falls through to the next
|
||||
** instruction.
|
||||
**
|
||||
** See also: NotFound, NotExists, Found
|
||||
*/
|
||||
case OP_IsUnique: { /* jump, in3 */
|
||||
u16 ii;
|
||||
VdbeCursor *pCx;
|
||||
BtCursor *pCrsr;
|
||||
u16 nField;
|
||||
Mem *aMx;
|
||||
UnpackedRecord r; /* B-Tree index search key */
|
||||
i64 R; /* Rowid stored in register P3 */
|
||||
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
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-p->nCursor) );
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
|
||||
/* Find the index cursor. */
|
||||
pCx = p->apCsr[pOp->p1];
|
||||
assert( pCx->deferredMoveto==0 );
|
||||
pCx->seekResult = 0;
|
||||
pCx->cacheStatus = CACHE_STALE;
|
||||
pCrsr = pCx->pCursor;
|
||||
|
||||
/* If any of the values are NULL, take the jump. */
|
||||
nField = pCx->pKeyInfo->nField;
|
||||
for(ii=0; ii<nField; ii++){
|
||||
if( aMx[ii].flags & MEM_Null ){
|
||||
pc = pOp->p2 - 1;
|
||||
pCrsr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert( (aMx[nField].flags & MEM_Null)==0 );
|
||||
|
||||
if( pCrsr!=0 ){
|
||||
/* Populate the index search key. */
|
||||
r.pKeyInfo = pCx->pKeyInfo;
|
||||
r.nField = nField + 1;
|
||||
r.flags = UNPACKED_PREFIX_SEARCH;
|
||||
r.aMem = aMx;
|
||||
#ifdef SQLITE_DEBUG
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
|
||||
/* Extract the value of R from register P3. */
|
||||
sqlite3VdbeMemIntegerify(pIn3);
|
||||
R = pIn3->u.i;
|
||||
|
||||
/* Search the B-Tree index. If no conflicting record is found, jump
|
||||
** to P2. Otherwise, copy the rowid of the conflicting record to
|
||||
** register P3 and fall through to the next instruction. */
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &pCx->seekResult);
|
||||
if( (r.flags & UNPACKED_PREFIX_SEARCH) || r.rowid==R ){
|
||||
pc = pOp->p2 - 1;
|
||||
}else{
|
||||
pIn3->u.i = r.rowid;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: NotExists P1 P2 P3 * *
|
||||
** Synopsis: intkey=r[P3]
|
||||
**
|
||||
** Use the content of register P3 as an integer key. If a record
|
||||
** with that key does not exist in table of P1, then jump to P2.
|
||||
** If the record does exist, then fall through. The cursor is left
|
||||
** pointing to the record if it exists.
|
||||
** P1 is the index of a cursor open on an SQL table btree (with integer
|
||||
** keys). P3 is an integer rowid. If P1 does not contain a record with
|
||||
** rowid P3 then jump immediately to P2. If P1 does contain a record
|
||||
** with rowid P3 then leave the cursor pointing at that record and fall
|
||||
** through to the next instruction.
|
||||
**
|
||||
** The difference between this operation and NotFound is that this
|
||||
** operation assumes the key is an integer and that P1 is a table whereas
|
||||
** NotFound assumes key is a blob constructed from MakeRecord and
|
||||
** P1 is an index.
|
||||
** The OP_NotFound opcode performs the same operation on index btrees
|
||||
** (with arbitrary multi-value keys).
|
||||
**
|
||||
** See also: Found, NotFound, IsUnique
|
||||
** See also: Found, NotFound, NoConflict
|
||||
*/
|
||||
case OP_NotExists: { /* jump, in3 */
|
||||
VdbeCursor *pC;
|
||||
@ -4242,22 +4228,32 @@ case OP_ResetCount: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SorterCompare P1 P2 P3
|
||||
** Synopsis: if key(P1)!=r[P3] goto P2
|
||||
/* Opcode: SorterCompare P1 P2 P3 P4
|
||||
** Synopsis: if key(P1)!=rtrim(r[P3],P4) goto P2
|
||||
**
|
||||
** P1 is a sorter cursor. This instruction compares the record blob in
|
||||
** register P3 with the entry that the sorter cursor currently points to.
|
||||
** If, excluding the rowid fields at the end, the two records are a match,
|
||||
** fall through to the next instruction. Otherwise, jump to instruction P2.
|
||||
** P1 is a sorter cursor. This instruction compares a prefix of the
|
||||
** the record blob in register P3 against a prefix of the entry that
|
||||
** the sorter cursor currently points to. The final P4 fields of both
|
||||
** the P3 and sorter record are ignored.
|
||||
**
|
||||
** If either P3 or the sorter contains a NULL in one of their significant
|
||||
** fields (not counting the P4 fields at the end which are ignored) then
|
||||
** the comparison is assumed to be equal.
|
||||
**
|
||||
** Fall through to next instruction if the two records compare equal to
|
||||
** each other. Jump to P2 if they are different.
|
||||
*/
|
||||
case OP_SorterCompare: {
|
||||
VdbeCursor *pC;
|
||||
int res;
|
||||
int nIgnore;
|
||||
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( isSorter(pC) );
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
rc = sqlite3VdbeSorterCompare(pC, pIn3, &res);
|
||||
nIgnore = pOp->p4.i;
|
||||
rc = sqlite3VdbeSorterCompare(pC, pIn3, nIgnore, &res);
|
||||
if( res ){
|
||||
pc = pOp->p2-1;
|
||||
}
|
||||
@ -4360,6 +4356,7 @@ case OP_RowData: {
|
||||
}
|
||||
pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4618,6 +4615,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
pIn2 = &aMem[pOp->p2];
|
||||
assert( pIn2->flags & MEM_Blob );
|
||||
pCrsr = pC->pCursor;
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( ALWAYS(pCrsr!=0) ){
|
||||
assert( pC->isTable==0 );
|
||||
rc = ExpandBlob(pIn2);
|
||||
@ -4638,7 +4636,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxDelete P1 P2 P3 * *
|
||||
/* Opcode: IdxDelete P1 P2 P3 * P5
|
||||
** Synopsis: key=r[P2@P3]
|
||||
**
|
||||
** The content of P3 registers starting at register P2 form
|
||||
@ -4657,6 +4655,7 @@ case OP_IdxDelete: {
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
pCrsr = pC->pCursor;
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( ALWAYS(pCrsr!=0) ){
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp->p3;
|
||||
|
29
src/vdbe.h
29
src/vdbe.h
@ -117,15 +117,11 @@ typedef struct VdbeOpList VdbeOpList;
|
||||
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
|
||||
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
|
||||
|
||||
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
|
||||
** is made. That copy is freed when the Vdbe is finalized. But if the
|
||||
** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still
|
||||
** gets freed when the Vdbe is finalized so it still should be obtained
|
||||
** from a single sqliteMalloc(). But no copy is made and the calling
|
||||
** function should *not* try to free the KeyInfo.
|
||||
*/
|
||||
#define P4_KEYINFO_HANDOFF (-16)
|
||||
#define P4_KEYINFO_STATIC (-17)
|
||||
/* Error message codes for OP_Halt */
|
||||
#define P5_ConstraintNotNull 1
|
||||
#define P5_ConstraintUnique 2
|
||||
#define P5_ConstraintCheck 3
|
||||
#define P5_ConstraintFK 4
|
||||
|
||||
/*
|
||||
** The Vdbe.aColName array contains 5n Mem structures, where n is the
|
||||
@ -180,6 +176,7 @@ void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
|
||||
void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
|
||||
void sqlite3VdbeUsesBtree(Vdbe*, int);
|
||||
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
|
||||
int sqlite3VdbeMakeLabel(Vdbe*);
|
||||
@ -218,15 +215,27 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
|
||||
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
||||
#endif
|
||||
|
||||
|
||||
/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
|
||||
** each VDBE opcode.
|
||||
**
|
||||
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
|
||||
** comments in VDBE programs that show key decision points in the code
|
||||
** generator.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
void sqlite3VdbeComment(Vdbe*, const char*, ...);
|
||||
# define VdbeComment(X) sqlite3VdbeComment X
|
||||
void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
|
||||
# define VdbeNoopComment(X) sqlite3VdbeNoopComment X
|
||||
# ifdef SQLITE_ENABLE_MODULE_COMMENTS
|
||||
# define VdbeModuleComment(X) sqlite3VdbeNoopComment X
|
||||
# else
|
||||
# define VdbeModuleComment(X)
|
||||
# endif
|
||||
#else
|
||||
# define VdbeComment(X)
|
||||
# define VdbeNoopComment(X)
|
||||
# define VdbeModuleComment(X)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -82,8 +82,7 @@ struct VdbeCursor {
|
||||
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
|
||||
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
|
||||
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||
** OP_IsUnique opcode on this cursor. */
|
||||
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists */
|
||||
int seekResult;
|
||||
|
||||
/* Cached information about the header for the data record that the
|
||||
@ -448,7 +447,7 @@ int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *);
|
||||
int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *);
|
||||
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
|
||||
void sqlite3VdbeEnter(Vdbe*);
|
||||
|
@ -107,6 +107,17 @@ static int growOpArray(Vdbe *p){
|
||||
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* This routine is just a convenient place to set a breakpoint that will
|
||||
** fire after each opcode is inserted and displayed using
|
||||
** "PRAGMA vdbe_addoptrace=on".
|
||||
*/
|
||||
static void test_addop_breakpoint(void){
|
||||
static int n = 0;
|
||||
n++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Add a new instruction to the list of instructions current in the
|
||||
** VDBE. Return the address of the new instruction.
|
||||
@ -150,6 +161,7 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( p->db->flags & SQLITE_VdbeAddopTrace ){
|
||||
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
|
||||
test_addop_breakpoint();
|
||||
}
|
||||
#endif
|
||||
#ifdef VDBE_PROFILE
|
||||
@ -625,12 +637,14 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
|
||||
case P4_REAL:
|
||||
case P4_INT64:
|
||||
case P4_DYNAMIC:
|
||||
case P4_KEYINFO:
|
||||
case P4_INTARRAY:
|
||||
case P4_KEYINFO_HANDOFF: {
|
||||
case P4_INTARRAY: {
|
||||
sqlite3DbFree(db, p4);
|
||||
break;
|
||||
}
|
||||
case P4_KEYINFO: {
|
||||
if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
|
||||
break;
|
||||
}
|
||||
case P4_MPRINTF: {
|
||||
if( db->pnBytesFreed==0 ) sqlite3_free(p4);
|
||||
break;
|
||||
@ -695,6 +709,7 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||||
memset(pOp, 0, sizeof(pOp[0]));
|
||||
pOp->opcode = OP_Noop;
|
||||
if( addr==p->nOp-1 ) p->nOp--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,14 +723,6 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
** the string is made into memory obtained from sqlite3_malloc().
|
||||
** A value of n==0 means copy bytes of zP4 up to and including the
|
||||
** first null byte. If n>0 then copy n+1 bytes of zP4.
|
||||
**
|
||||
** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure.
|
||||
** A copy is made of the KeyInfo structure into memory obtained from
|
||||
** sqlite3_malloc, to be freed when the Vdbe is finalized.
|
||||
** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure
|
||||
** stored in memory that the caller has obtained from sqlite3_malloc. The
|
||||
** caller should not free the allocation, it will be freed when the Vdbe is
|
||||
** finalized.
|
||||
**
|
||||
** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
|
||||
** to a string or structure that is guaranteed to exist for the lifetime of
|
||||
@ -730,7 +737,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
db = p->db;
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
if( p->aOp==0 || db->mallocFailed ){
|
||||
if ( n!=P4_KEYINFO && n!=P4_VTAB ) {
|
||||
if( n!=P4_VTAB ){
|
||||
freeP4(db, n, (void*)*(char**)&zP4);
|
||||
}
|
||||
return;
|
||||
@ -753,19 +760,6 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
pOp->p4.p = 0;
|
||||
pOp->p4type = P4_NOTUSED;
|
||||
}else if( n==P4_KEYINFO ){
|
||||
KeyInfo *pOrig, *pNew;
|
||||
|
||||
pOrig = (KeyInfo*)zP4;
|
||||
pOp->p4.pKeyInfo = pNew = sqlite3KeyInfoAlloc(db, pOrig->nField);
|
||||
if( pNew ){
|
||||
memcpy(pNew->aColl, pOrig->aColl, pOrig->nField*sizeof(pNew->aColl[0]));
|
||||
memcpy(pNew->aSortOrder, pOrig->aSortOrder, pOrig->nField);
|
||||
pOp->p4type = P4_KEYINFO;
|
||||
}else{
|
||||
p->db->mallocFailed = 1;
|
||||
pOp->p4type = P4_NOTUSED;
|
||||
}
|
||||
}else if( n==P4_KEYINFO_HANDOFF ){
|
||||
pOp->p4.p = (void*)zP4;
|
||||
pOp->p4type = P4_KEYINFO;
|
||||
}else if( n==P4_VTAB ){
|
||||
@ -783,6 +777,18 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the P4 on the most recently added opcode to the KeyInfo for the
|
||||
** index given.
|
||||
*/
|
||||
void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
assert( pIdx!=0 );
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx),
|
||||
P4_KEYINFO);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
/*
|
||||
** Change the comment on the most recently coded instruction. Or
|
||||
@ -943,17 +949,20 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
char *zP4 = zTemp;
|
||||
assert( nTemp>=20 );
|
||||
switch( pOp->p4type ){
|
||||
case P4_KEYINFO_STATIC:
|
||||
case P4_KEYINFO: {
|
||||
int i, j;
|
||||
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField);
|
||||
sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField);
|
||||
i = sqlite3Strlen30(zTemp);
|
||||
for(j=0; j<pKeyInfo->nField; j++){
|
||||
CollSeq *pColl = pKeyInfo->aColl[j];
|
||||
const char *zColl = pColl ? pColl->zName : "nil";
|
||||
int n = sqlite3Strlen30(zColl);
|
||||
if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
|
||||
zColl = "B";
|
||||
n = 1;
|
||||
}
|
||||
if( i+n>nTemp-6 ){
|
||||
memcpy(&zTemp[i],",...",4);
|
||||
break;
|
||||
@ -1127,7 +1136,7 @@ void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
|
||||
char *zP4;
|
||||
char zPtr[50];
|
||||
char zCom[100];
|
||||
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
|
||||
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
|
||||
if( pOut==0 ) pOut = stdout;
|
||||
zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
|
||||
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
@ -2179,7 +2188,7 @@ int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
|
||||
){
|
||||
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
|
||||
p->errorAction = OE_Abort;
|
||||
sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
|
||||
sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@ -3110,7 +3119,7 @@ int sqlite3VdbeRecordCompare(
|
||||
|
||||
idx1 = getVarint32(aKey1, szHdr1);
|
||||
d1 = szHdr1;
|
||||
assert( pKeyInfo->nField+1>=pPKey2->nField );
|
||||
assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField );
|
||||
assert( pKeyInfo->aSortOrder!=0 );
|
||||
while( idx1<szHdr1 && i<pPKey2->nField ){
|
||||
u32 serial_type1;
|
||||
@ -3139,24 +3148,9 @@ int sqlite3VdbeRecordCompare(
|
||||
rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
|
||||
if( rc!=0 ){
|
||||
assert( mem1.zMalloc==0 ); /* See comment below */
|
||||
|
||||
/* Invert the result if we are using DESC sort order. */
|
||||
if( pKeyInfo->aSortOrder[i] ){
|
||||
rc = -rc;
|
||||
rc = -rc; /* Invert the result for DESC sort order. */
|
||||
}
|
||||
|
||||
/* If the PREFIX_SEARCH flag is set and all fields except the final
|
||||
** rowid field were equal, then clear the PREFIX_SEARCH flag and set
|
||||
** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
|
||||
** This is used by the OP_IsUnique opcode.
|
||||
*/
|
||||
if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
|
||||
assert( idx1==szHdr1 && rc );
|
||||
assert( mem1.flags & MEM_Int );
|
||||
pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
|
||||
pPKey2->rowid = mem1.u.i;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
i++;
|
||||
|
@ -178,6 +178,10 @@ int sqlite3_blob_open(
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
|
||||
}
|
||||
if( pTab && !HasRowid(pTab) ){
|
||||
pTab = 0;
|
||||
sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( pTab && pTab->pSelect ){
|
||||
pTab = 0;
|
||||
@ -235,7 +239,7 @@ int sqlite3_blob_open(
|
||||
#endif
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int j;
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
for(j=0; j<pIdx->nKeyCol; j++){
|
||||
if( pIdx->aiColumn[j]==iCol ){
|
||||
zFault = "indexed";
|
||||
}
|
||||
|
@ -1031,15 +1031,15 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
|
||||
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 */
|
||||
int nCol = pIdx->nColumn; /* 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);
|
||||
pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
|
||||
if( pRec->pKeyInfo ){
|
||||
assert( pRec->pKeyInfo->nField+1==nCol );
|
||||
pRec->pKeyInfo->enc = ENC(db);
|
||||
assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
|
||||
assert( pRec->pKeyInfo->enc==ENC(db) );
|
||||
pRec->flags = UNPACKED_PREFIX_MATCH;
|
||||
pRec->aMem = (Mem *)&pRec[1];
|
||||
for(i=0; i<nCol; i++){
|
||||
@ -1362,13 +1362,13 @@ int sqlite3Stat4ProbeSetValue(
|
||||
void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
|
||||
if( pRec ){
|
||||
int i;
|
||||
int nCol = pRec->pKeyInfo->nField+1;
|
||||
int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
|
||||
Mem *aMem = pRec->aMem;
|
||||
sqlite3 *db = aMem[0].db;
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3DbFree(db, aMem[i].zMalloc);
|
||||
}
|
||||
sqlite3DbFree(db, pRec->pKeyInfo);
|
||||
sqlite3KeyInfoUnref(pRec->pKeyInfo);
|
||||
sqlite3DbFree(db, pRec);
|
||||
}
|
||||
}
|
||||
|
@ -386,7 +386,7 @@ static int vdbeSorterIterInit(
|
||||
*/
|
||||
static void vdbeSorterCompare(
|
||||
const VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
|
||||
int bOmitRowid, /* Ignore rowid field at end of keys */
|
||||
int nIgnore, /* Ignore the last nIgnore fields */
|
||||
const void *pKey1, int nKey1, /* Left side of comparison */
|
||||
const void *pKey2, int nKey2, /* Right side of comparison */
|
||||
int *pRes /* OUT: Result of comparison */
|
||||
@ -400,8 +400,8 @@ static void vdbeSorterCompare(
|
||||
sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
|
||||
}
|
||||
|
||||
if( bOmitRowid ){
|
||||
r2->nField = pKeyInfo->nField;
|
||||
if( nIgnore ){
|
||||
r2->nField = pKeyInfo->nField - nIgnore;
|
||||
assert( r2->nField>0 );
|
||||
for(i=0; i<r2->nField; i++){
|
||||
if( r2->aMem[i].flags & MEM_Null ){
|
||||
@ -1027,12 +1027,13 @@ int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){
|
||||
int sqlite3VdbeSorterCompare(
|
||||
const VdbeCursor *pCsr, /* Sorter cursor */
|
||||
Mem *pVal, /* Value to compare to current sorter key */
|
||||
int nIgnore, /* Ignore this many fields at the end */
|
||||
int *pRes /* OUT: Result of comparison */
|
||||
){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
void *pKey; int nKey; /* Sorter key to compare pVal with */
|
||||
|
||||
pKey = vdbeSorterRowkey(pSorter, &nKey);
|
||||
vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
|
||||
vdbeSorterCompare(pCsr, nIgnore, pVal->z, pVal->n, pKey, nKey, pRes);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
180
src/where.c
180
src/where.c
@ -971,7 +971,7 @@ static WhereTerm *whereScanInit(
|
||||
if( pIdx && iColumn>=0 ){
|
||||
pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
|
||||
for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
|
||||
if( NEVER(j>=pIdx->nColumn) ) return 0;
|
||||
if( NEVER(j>=pIdx->nKeyCol) ) return 0;
|
||||
}
|
||||
pScan->zCollName = pIdx->azColl[j];
|
||||
}else{
|
||||
@ -1901,16 +1901,16 @@ static int isDistinctRedundant(
|
||||
*/
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->onError==OE_None ) continue;
|
||||
for(i=0; i<pIdx->nColumn; i++){
|
||||
int iCol = pIdx->aiColumn[i];
|
||||
for(i=0; i<pIdx->nKeyCol; i++){
|
||||
i16 iCol = pIdx->aiColumn[i];
|
||||
if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) ){
|
||||
int iIdxCol = findIndexCol(pParse, pDistinct, iBase, pIdx, i);
|
||||
if( iIdxCol<0 || pTab->aCol[pIdx->aiColumn[i]].notNull==0 ){
|
||||
if( iIdxCol<0 || pTab->aCol[iCol].notNull==0 ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( i==pIdx->nColumn ){
|
||||
if( i==pIdx->nKeyCol ){
|
||||
/* This index implies that the DISTINCT qualifier is redundant. */
|
||||
return 1;
|
||||
}
|
||||
@ -2008,15 +2008,13 @@ static void constructAutomaticIndex(
|
||||
Bitmask notReady, /* Mask of cursors that are not available */
|
||||
WhereLevel *pLevel /* Write new index here */
|
||||
){
|
||||
int nColumn; /* Number of columns in the constructed index */
|
||||
int nKeyCol; /* Number of columns in the constructed index */
|
||||
WhereTerm *pTerm; /* A single term of the WHERE clause */
|
||||
WhereTerm *pWCEnd; /* End of pWC->a[] */
|
||||
int nByte; /* Byte of memory needed for pIdx */
|
||||
Index *pIdx; /* Object describing the transient index */
|
||||
Vdbe *v; /* Prepared statement under construction */
|
||||
int addrInit; /* Address of the initialization bypass jump */
|
||||
Table *pTable; /* The table being indexed */
|
||||
KeyInfo *pKeyinfo; /* Key information for the index */
|
||||
int addrTop; /* Top of the index fill loop */
|
||||
int regRecord; /* Register holding an index record */
|
||||
int n; /* Column counter */
|
||||
@ -2024,6 +2022,7 @@ static void constructAutomaticIndex(
|
||||
int mxBitCol; /* Maximum column in pSrc->colUsed */
|
||||
CollSeq *pColl; /* Collating sequence to on a column */
|
||||
WhereLoop *pLoop; /* The Loop object */
|
||||
char *zNotUsed; /* Extra space on the end of pIdx */
|
||||
Bitmask idxCols; /* Bitmap of columns used for indexing */
|
||||
Bitmask extraCols; /* Bitmap of additional columns */
|
||||
u8 sentWarning = 0; /* True if a warnning has been issued */
|
||||
@ -2036,7 +2035,7 @@ static void constructAutomaticIndex(
|
||||
|
||||
/* Count the number of columns that will be added to the index
|
||||
** and used to match WHERE clause constraints */
|
||||
nColumn = 0;
|
||||
nKeyCol = 0;
|
||||
pTable = pSrc->pTab;
|
||||
pWCEnd = &pWC->a[pWC->nTerm];
|
||||
pLoop = pLevel->pWLoop;
|
||||
@ -2054,14 +2053,14 @@ static void constructAutomaticIndex(
|
||||
sentWarning = 1;
|
||||
}
|
||||
if( (idxCols & cMask)==0 ){
|
||||
if( whereLoopResize(pParse->db, pLoop, nColumn+1) ) return;
|
||||
pLoop->aLTerm[nColumn++] = pTerm;
|
||||
if( whereLoopResize(pParse->db, pLoop, nKeyCol+1) ) return;
|
||||
pLoop->aLTerm[nKeyCol++] = pTerm;
|
||||
idxCols |= cMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert( nColumn>0 );
|
||||
pLoop->u.btree.nEq = pLoop->nLTerm = nColumn;
|
||||
assert( nKeyCol>0 );
|
||||
pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol;
|
||||
pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
|
||||
| WHERE_AUTO_INDEX;
|
||||
|
||||
@ -2078,26 +2077,18 @@ static void constructAutomaticIndex(
|
||||
testcase( pTable->nCol==BMS-1 );
|
||||
testcase( pTable->nCol==BMS-2 );
|
||||
for(i=0; i<mxBitCol; i++){
|
||||
if( extraCols & MASKBIT(i) ) nColumn++;
|
||||
if( extraCols & MASKBIT(i) ) nKeyCol++;
|
||||
}
|
||||
if( pSrc->colUsed & MASKBIT(BMS-1) ){
|
||||
nColumn += pTable->nCol - BMS + 1;
|
||||
nKeyCol += pTable->nCol - BMS + 1;
|
||||
}
|
||||
pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY;
|
||||
|
||||
/* Construct the Index object to describe this index */
|
||||
nByte = sizeof(Index);
|
||||
nByte += nColumn*sizeof(int); /* Index.aiColumn */
|
||||
nByte += nColumn*sizeof(char*); /* Index.azColl */
|
||||
nByte += nColumn; /* Index.aSortOrder */
|
||||
pIdx = sqlite3DbMallocZero(pParse->db, nByte);
|
||||
pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
|
||||
if( pIdx==0 ) return;
|
||||
pLoop->u.btree.pIndex = pIdx;
|
||||
pIdx->azColl = (char**)&pIdx[1];
|
||||
pIdx->aiColumn = (int*)&pIdx->azColl[nColumn];
|
||||
pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn];
|
||||
pIdx->zName = "auto-index";
|
||||
pIdx->nColumn = nColumn;
|
||||
pIdx->pTable = pTable;
|
||||
n = 0;
|
||||
idxCols = 0;
|
||||
@ -2135,20 +2126,21 @@ static void constructAutomaticIndex(
|
||||
n++;
|
||||
}
|
||||
}
|
||||
assert( n==nColumn );
|
||||
assert( n==nKeyCol );
|
||||
pIdx->aiColumn[n] = -1;
|
||||
pIdx->azColl[n] = "BINARY";
|
||||
|
||||
/* Create the automatic index */
|
||||
pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
|
||||
assert( pLevel->iIdxCur>=0 );
|
||||
pLevel->iIdxCur = pParse->nTab++;
|
||||
sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0,
|
||||
(char*)pKeyinfo, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "for %s", pTable->zName));
|
||||
|
||||
/* Fill the automatic index with content */
|
||||
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur);
|
||||
regRecord = sqlite3GetTempReg(pParse);
|
||||
sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1, 0);
|
||||
sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
|
||||
@ -2395,7 +2387,7 @@ static void whereKeyStats(
|
||||
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->nColumn>iCol ? pIdx->aAvgEq[iCol] : 1);
|
||||
aStat[1] = (pIdx->nKeyCol>iCol ? pIdx->aAvgEq[iCol] : 1);
|
||||
if( iLower>=iUpper ){
|
||||
iGap = 0;
|
||||
}else{
|
||||
@ -2495,7 +2487,7 @@ static int whereRangeScanEst(
|
||||
tRowcnt iLower;
|
||||
tRowcnt iUpper;
|
||||
|
||||
if( nEq==p->nColumn ){
|
||||
if( nEq==p->nKeyCol ){
|
||||
aff = SQLITE_AFF_INTEGER;
|
||||
}else{
|
||||
aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
|
||||
@ -2613,7 +2605,7 @@ static int whereEqualScanEst(
|
||||
int bOk;
|
||||
|
||||
assert( nEq>=1 );
|
||||
assert( nEq<=(p->nColumn+1) );
|
||||
assert( nEq<=(p->nKeyCol+1) );
|
||||
assert( p->aSample!=0 );
|
||||
assert( p->nSample>0 );
|
||||
assert( pBuilder->nRecValid<nEq );
|
||||
@ -2626,7 +2618,7 @@ static int whereEqualScanEst(
|
||||
|
||||
/* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
|
||||
** below would return the same value. */
|
||||
if( nEq>p->nColumn ){
|
||||
if( nEq>p->nKeyCol ){
|
||||
*pnRow = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -3013,7 +3005,7 @@ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
|
||||
int nEq = pLoop->u.btree.nEq;
|
||||
int i, j;
|
||||
Column *aCol = pTab->aCol;
|
||||
int *aiColumn = pIndex->aiColumn;
|
||||
i16 *aiColumn = pIndex->aiColumn;
|
||||
StrAccum txt;
|
||||
|
||||
if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
|
||||
@ -3023,17 +3015,17 @@ static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
|
||||
txt.db = db;
|
||||
sqlite3StrAccumAppend(&txt, " (", 2);
|
||||
for(i=0; i<nEq; i++){
|
||||
char *z = (i==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[i]].zName;
|
||||
char *z = (i==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[i]].zName;
|
||||
explainAppendTerm(&txt, i, z, "=");
|
||||
}
|
||||
|
||||
j = i;
|
||||
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
|
||||
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
explainAppendTerm(&txt, i++, z, ">");
|
||||
}
|
||||
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
|
||||
char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
char *z = (j==pIndex->nKeyCol ) ? "rowid" : aCol[aiColumn[j]].zName;
|
||||
explainAppendTerm(&txt, i, z, "<");
|
||||
}
|
||||
sqlite3StrAccumAppend(&txt, ")", 1);
|
||||
@ -3417,7 +3409,7 @@ static Bitmask codeOneLoopStart(
|
||||
*/
|
||||
if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
|
||||
&& (pWInfo->bOBSat!=0)
|
||||
&& (pIdx->nColumn>nEq)
|
||||
&& (pIdx->nKeyCol>nEq)
|
||||
){
|
||||
/* assert( pOrderBy->nExpr==1 ); */
|
||||
/* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
|
||||
@ -3450,8 +3442,8 @@ static Bitmask codeOneLoopStart(
|
||||
** a forward order scan on a descending index, interchange the
|
||||
** start and end terms (pRangeStart and pRangeEnd).
|
||||
*/
|
||||
if( (nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
|
||||
|| (bRev && pIdx->nColumn==nEq)
|
||||
if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
|
||||
|| (bRev && pIdx->nKeyCol==nEq)
|
||||
){
|
||||
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
|
||||
}
|
||||
@ -3560,11 +3552,22 @@ static Bitmask codeOneLoopStart(
|
||||
/* Seek the table cursor, if required */
|
||||
disableTerm(pLevel, pRangeStart);
|
||||
disableTerm(pLevel, pRangeEnd);
|
||||
if( !omitTable ){
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
}else if( HasRowid(pIdx->pTable) ){
|
||||
iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
|
||||
}else{
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
|
||||
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
||||
for(j=0; j<pPk->nKeyCol; j++){
|
||||
k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
|
||||
}
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
|
||||
iRowidReg, pPk->nKeyCol);
|
||||
}
|
||||
|
||||
/* Record the instruction used to terminate the loop. Disable
|
||||
@ -3991,6 +3994,7 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
|
||||
p->u.vtab.idxStr = 0;
|
||||
}else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
|
||||
sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
|
||||
sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo);
|
||||
sqlite3DbFree(db, p->u.btree.pIndex);
|
||||
p->u.btree.pIndex = 0;
|
||||
}
|
||||
@ -4290,8 +4294,8 @@ static int whereLoopAddBtreeIndex(
|
||||
}
|
||||
if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
|
||||
|
||||
assert( pNew->u.btree.nEq<=pProbe->nColumn );
|
||||
if( pNew->u.btree.nEq < pProbe->nColumn ){
|
||||
assert( pNew->u.btree.nEq<=pProbe->nKeyCol );
|
||||
if( pNew->u.btree.nEq < pProbe->nKeyCol ){
|
||||
iCol = pProbe->aiColumn[pNew->u.btree.nEq];
|
||||
nRowEst = sqlite3LogEst(pProbe->aiRowEst[pNew->u.btree.nEq+1]);
|
||||
if( nRowEst==0 && pProbe->onError==OE_None ) nRowEst = 1;
|
||||
@ -4348,7 +4352,7 @@ static int whereLoopAddBtreeIndex(
|
||||
pNew->wsFlags |= WHERE_COLUMN_EQ;
|
||||
if( iCol<0
|
||||
|| (pProbe->onError!=OE_None && nInMul==0
|
||||
&& pNew->u.btree.nEq==pProbe->nColumn-1)
|
||||
&& pNew->u.btree.nEq==pProbe->nKeyCol-1)
|
||||
){
|
||||
assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 );
|
||||
pNew->wsFlags |= WHERE_ONEROW;
|
||||
@ -4414,7 +4418,7 @@ static int whereLoopAddBtreeIndex(
|
||||
whereLoopOutputAdjust(pBuilder->pWC, pNew);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
|
||||
&& pNew->u.btree.nEq<(pProbe->nColumn + (pProbe->zName!=0))
|
||||
&& pNew->u.btree.nEq<(pProbe->nKeyCol + (pProbe->zName!=0))
|
||||
){
|
||||
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
|
||||
}
|
||||
@ -4453,7 +4457,7 @@ static int indexMightHelpWithOrderBy(
|
||||
Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
|
||||
if( pExpr->op!=TK_COLUMN ) return 0;
|
||||
if( pExpr->iTable==iCursor ){
|
||||
for(jj=0; jj<pIndex->nColumn; jj++){
|
||||
for(jj=0; jj<pIndex->nKeyCol; jj++){
|
||||
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
|
||||
}
|
||||
}
|
||||
@ -4470,10 +4474,11 @@ static Bitmask columnsInIndex(Index *pIdx){
|
||||
int j;
|
||||
for(j=pIdx->nColumn-1; j>=0; j--){
|
||||
int x = pIdx->aiColumn[j];
|
||||
assert( x>=0 );
|
||||
testcase( x==BMS-1 );
|
||||
testcase( x==BMS-2 );
|
||||
if( x<BMS-1 ) m |= MASKBIT(x);
|
||||
if( x>=0 ){
|
||||
testcase( x==BMS-1 );
|
||||
testcase( x==BMS-2 );
|
||||
if( x<BMS-1 ) m |= MASKBIT(x);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
@ -4503,7 +4508,7 @@ static int whereLoopAddBtree(
|
||||
Index *pProbe; /* An index we are evaluating */
|
||||
Index sPk; /* A fake index object for the primary key */
|
||||
tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
|
||||
int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
|
||||
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
|
||||
SrcList *pTabList; /* The FROM clause */
|
||||
struct SrcList_item *pSrc; /* The FROM clause btree term to add */
|
||||
WhereLoop *pNew; /* Template WhereLoop object */
|
||||
@ -4526,6 +4531,8 @@ static int whereLoopAddBtree(
|
||||
if( pSrc->pIndex ){
|
||||
/* An INDEXED BY clause specifies a particular index to use */
|
||||
pProbe = pSrc->pIndex;
|
||||
}else if( !HasRowid(pTab) ){
|
||||
pProbe = pTab->pIndex;
|
||||
}else{
|
||||
/* There is no INDEXED BY clause. Create a fake Index object in local
|
||||
** variable sPk to represent the rowid primary key index. Make this
|
||||
@ -4533,7 +4540,7 @@ static int whereLoopAddBtree(
|
||||
** indices to follow */
|
||||
Index *pFirst; /* First of real indices on the table */
|
||||
memset(&sPk, 0, sizeof(Index));
|
||||
sPk.nColumn = 1;
|
||||
sPk.nKeyCol = 1;
|
||||
sPk.aiColumn = &aiColumnPk;
|
||||
sPk.aiRowEst = aiRowEstPk;
|
||||
sPk.onError = OE_Replace;
|
||||
@ -4558,6 +4565,7 @@ static int whereLoopAddBtree(
|
||||
&& pSrc->pIndex==0
|
||||
&& !pSrc->viaCoroutine
|
||||
&& !pSrc->notIndexed
|
||||
&& HasRowid(pTab)
|
||||
&& !pSrc->isCorrelated
|
||||
){
|
||||
/* Generate auto-index WhereLoops */
|
||||
@ -4620,14 +4628,20 @@ static int whereLoopAddBtree(
|
||||
pNew->nOut = rSize;
|
||||
if( rc ) break;
|
||||
}else{
|
||||
Bitmask m = pSrc->colUsed & ~columnsInIndex(pProbe);
|
||||
pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
|
||||
Bitmask m;
|
||||
if( pProbe->isCovering ){
|
||||
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
|
||||
m = 0;
|
||||
}else{
|
||||
m = pSrc->colUsed & ~columnsInIndex(pProbe);
|
||||
pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
|
||||
}
|
||||
|
||||
/* Full scan via index */
|
||||
if( b
|
||||
|| ( m==0
|
||||
&& pProbe->bUnordered==0
|
||||
&& pProbe->szIdxRow<pTab->szTabRow
|
||||
&& (!HasRowid(pTab) || pProbe->szIdxRow<pTab->szTabRow)
|
||||
&& (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
|
||||
&& sqlite3GlobalConfig.bUseCis
|
||||
&& OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan)
|
||||
@ -4859,6 +4873,7 @@ static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
|
||||
pNew = pBuilder->pNew;
|
||||
memset(&sSum, 0, sizeof(sSum));
|
||||
pItem = pWInfo->pTabList->a + pNew->iTab;
|
||||
if( !HasRowid(pItem->pTab) ) return SQLITE_OK;
|
||||
iCur = pItem->iCursor;
|
||||
|
||||
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
|
||||
@ -5008,7 +5023,8 @@ static int wherePathSatisfiesOrderBy(
|
||||
u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
|
||||
u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
|
||||
u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
|
||||
u16 nColumn; /* Number of columns in pIndex */
|
||||
u16 nKeyCol; /* Number of key columns in pIndex */
|
||||
u16 nColumn; /* Total number of ordered columns in the index */
|
||||
u16 nOrderBy; /* Number terms in the ORDER BY clause */
|
||||
int iLoop; /* Index of WhereLoop in pPath being processed */
|
||||
int i, j; /* Loop counters */
|
||||
@ -5100,11 +5116,15 @@ static int wherePathSatisfiesOrderBy(
|
||||
if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){
|
||||
if( pLoop->wsFlags & WHERE_IPK ){
|
||||
pIndex = 0;
|
||||
nColumn = 0;
|
||||
nKeyCol = 0;
|
||||
nColumn = 1;
|
||||
}else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){
|
||||
return 0;
|
||||
}else{
|
||||
nKeyCol = pIndex->nKeyCol;
|
||||
nColumn = pIndex->nColumn;
|
||||
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
|
||||
assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable));
|
||||
isOrderDistinct = pIndex->onError!=OE_None;
|
||||
}
|
||||
|
||||
@ -5113,7 +5133,7 @@ static int wherePathSatisfiesOrderBy(
|
||||
*/
|
||||
rev = revSet = 0;
|
||||
distinctColumns = 0;
|
||||
for(j=0; j<=nColumn; j++){
|
||||
for(j=0; j<nColumn; j++){
|
||||
u8 bOnce; /* True to run the ORDER BY search loop */
|
||||
|
||||
/* Skip over == and IS NULL terms */
|
||||
@ -5130,20 +5150,17 @@ static int wherePathSatisfiesOrderBy(
|
||||
/* Get the column number in the table (iColumn) and sort order
|
||||
** (revIdx) for the j-th column of the index.
|
||||
*/
|
||||
if( j<nColumn ){
|
||||
/* Normal index columns */
|
||||
if( pIndex ){
|
||||
iColumn = pIndex->aiColumn[j];
|
||||
revIdx = pIndex->aSortOrder[j];
|
||||
if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
|
||||
}else{
|
||||
/* The ROWID column at the end */
|
||||
assert( j==nColumn );
|
||||
iColumn = -1;
|
||||
revIdx = 0;
|
||||
}
|
||||
|
||||
/* An unconstrained column that might be NULL means that this
|
||||
** WhereLoop is not well-ordered
|
||||
** WhereLoop is not well-ordered
|
||||
*/
|
||||
if( isOrderDistinct
|
||||
&& iColumn>=0
|
||||
@ -5194,7 +5211,7 @@ static int wherePathSatisfiesOrderBy(
|
||||
}
|
||||
}else{
|
||||
/* No match found */
|
||||
if( j==0 || j<nColumn ){
|
||||
if( j==0 || j<nKeyCol ){
|
||||
testcase( isOrderDistinct!=0 );
|
||||
isOrderDistinct = 0;
|
||||
}
|
||||
@ -5560,16 +5577,16 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
|
||||
assert( ArraySize(pLoop->aLTermSpace)==4 );
|
||||
if( pIdx->onError==OE_None
|
||||
|| pIdx->pPartIdxWhere!=0
|
||||
|| pIdx->nColumn>ArraySize(pLoop->aLTermSpace)
|
||||
|| pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
|
||||
) continue;
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
for(j=0; j<pIdx->nKeyCol; j++){
|
||||
pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx);
|
||||
if( pTerm==0 ) break;
|
||||
pLoop->aLTerm[j] = pTerm;
|
||||
}
|
||||
if( j!=pIdx->nColumn ) continue;
|
||||
if( j!=pIdx->nKeyCol ) continue;
|
||||
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
|
||||
if( (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
|
||||
if( pIdx->isCovering || (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
|
||||
pLoop->wsFlags |= WHERE_IDX_ONLY;
|
||||
}
|
||||
pLoop->nLTerm = j;
|
||||
@ -6007,7 +6024,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
|
||||
testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 );
|
||||
testcase( !pWInfo->okOnePass && pTab->nCol==BMS );
|
||||
if( !pWInfo->okOnePass && pTab->nCol<BMS ){
|
||||
if( !pWInfo->okOnePass && pTab->nCol<BMS && HasRowid(pTab) ){
|
||||
Bitmask b = pTabItem->colUsed;
|
||||
int n = 0;
|
||||
for(; b; b=b>>1, n++){}
|
||||
@ -6020,13 +6037,12 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
if( pLoop->wsFlags & WHERE_INDEXED ){
|
||||
Index *pIx = pLoop->u.btree.pIndex;
|
||||
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
|
||||
/* FIXME: As an optimization use pTabItem->iCursor if WHERE_IDX_ONLY */
|
||||
int iIndexCur = pLevel->iIdxCur = iIdxCur ? iIdxCur : pParse->nTab++;
|
||||
assert( pIx->pSchema==pTab->pSchema );
|
||||
assert( iIndexCur>=0 );
|
||||
sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb,
|
||||
(char*)pKey, P4_KEYINFO_HANDOFF);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
|
||||
VdbeComment((v, "%s", pIx->zName));
|
||||
}
|
||||
sqlite3CodeVerifySchema(pParse, iDb);
|
||||
@ -6167,7 +6183,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pIdx = pLevel->u.pCovidx;
|
||||
}
|
||||
if( pIdx && !db->mallocFailed ){
|
||||
int k, j, last;
|
||||
int k, last;
|
||||
VdbeOp *pOp;
|
||||
|
||||
last = sqlite3VdbeCurrentAddr(v);
|
||||
@ -6176,14 +6192,18 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
for(; k<last; k++, pOp++){
|
||||
if( pOp->p1!=pLevel->iTabCur ) continue;
|
||||
if( pOp->opcode==OP_Column ){
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
if( pOp->p2==pIdx->aiColumn[j] ){
|
||||
pOp->p2 = j;
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
break;
|
||||
}
|
||||
int x = pOp->p2;
|
||||
Table *pTab = pIdx->pTable;
|
||||
if( !HasRowid(pTab) ){
|
||||
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
x = pPk->aiColumn[x];
|
||||
}
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || j<pIdx->nColumn );
|
||||
x = sqlite3ColumnOfIndex(pIdx, x);
|
||||
if( x>=0 ){
|
||||
pOp->p2 = x;
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
}
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 );
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
pOp->opcode = OP_IdxRowid;
|
||||
|
@ -860,5 +860,19 @@ foreach {tn tbl} $system_table_list {
|
||||
} [list 1 "table $tbl may not be altered"]
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# Verify that ALTER TABLE works on tables with the WITHOUT rowid option.
|
||||
#
|
||||
do_execsql_test alter-16.1 {
|
||||
CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
INSERT INTO t16a VALUES('abc',1.25,99);
|
||||
ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy';
|
||||
INSERT INTO t16a VALUES('cba',5.5,98,'fizzle');
|
||||
SELECT * FROM t16a ORDER BY a;
|
||||
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
|
||||
do_execsql_test alter-16.2 {
|
||||
ALTER TABLE t16a RENAME TO t16a_rn;
|
||||
SELECT * FROM t16a_rn ORDER BY a;
|
||||
} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
|
||||
|
||||
finish_test
|
||||
|
@ -526,7 +526,7 @@ do_test autovacuum-4.2 {
|
||||
catchsql {
|
||||
CREATE UNIQUE INDEX av1_i ON av1(a);
|
||||
}
|
||||
} {1 {indexed columns are not unique}}
|
||||
} {1 {UNIQUE constraint failed: av1.a}}
|
||||
do_test autovacuum-4.3 {
|
||||
execsql {
|
||||
SELECT sum(a) FROM av1;
|
||||
|
@ -200,6 +200,42 @@ do_allbackcompat_test {
|
||||
|
||||
do_test backcompat-1.2.7 { sql1 { PRAGMA integrity_check } } {ok}
|
||||
do_test backcompat-1.2.8 { sql2 { PRAGMA integrity_check } } {ok}
|
||||
|
||||
do_test backcompat-2.1 {
|
||||
sql1 {
|
||||
CREATE TABLE t2(a UNIQUE, b PRIMARY KEY, c UNIQUE);
|
||||
INSERT INTO t2 VALUES(1,9,5);
|
||||
INSERT INTO t2 VALUES(5,5,1);
|
||||
INSERT INTO t2 VALUES(9,1,9);
|
||||
SELECT * FROM t2 ORDER BY a;
|
||||
}
|
||||
} {1 9 5 5 5 1 9 1 9}
|
||||
do_test backcompat-2.2 {
|
||||
sql2 {
|
||||
SELECT * FROM sqlite_master WHERE rootpage=-1;
|
||||
SELECT * FROM t2 ORDER BY a;
|
||||
}
|
||||
} {1 9 5 5 5 1 9 1 9}
|
||||
do_test backcompat-2.3 {
|
||||
sql1 {
|
||||
SELECT * FROM t2 ORDER BY b;
|
||||
}
|
||||
} {9 1 9 5 5 1 1 9 5}
|
||||
do_test backcompat-2.4 {
|
||||
sql2 {
|
||||
SELECT * FROM t2 ORDER BY b;
|
||||
}
|
||||
} {9 1 9 5 5 1 1 9 5}
|
||||
do_test backcompat-2.5 {
|
||||
sql1 {
|
||||
SELECT * FROM t2 ORDER BY c;
|
||||
}
|
||||
} {5 5 1 1 9 5 9 1 9}
|
||||
do_test backcompat-2.6 {
|
||||
sql2 {
|
||||
SELECT * FROM t2 ORDER BY c;
|
||||
}
|
||||
} {5 5 1 1 9 5 9 1 9}
|
||||
}
|
||||
foreach k [lsort [array names ::incompatible]] {
|
||||
puts "ERROR: Detected journal incompatibility with version $k"
|
||||
|
@ -237,7 +237,7 @@ do_test capi2-3.13b {db changes} {0}
|
||||
do_test capi2-3.14 {
|
||||
list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \
|
||||
[sqlite3_extended_errcode $DB]
|
||||
} {SQLITE_CONSTRAINT {column a is not unique} SQLITE_CONSTRAINT_UNIQUE}
|
||||
} {SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.a} SQLITE_CONSTRAINT_UNIQUE}
|
||||
do_test capi2-3.15 {
|
||||
set VM [sqlite3_prepare $DB {CREATE TABLE t2(a NOT NULL, b)} -1 TAIL]
|
||||
set TAIL
|
||||
@ -261,7 +261,7 @@ do_test capi2-3.18 {
|
||||
do_test capi2-3.19 {
|
||||
list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \
|
||||
[sqlite3_extended_errcode $DB]
|
||||
} {SQLITE_CONSTRAINT {t2.a may not be NULL} SQLITE_CONSTRAINT_NOTNULL}
|
||||
} {SQLITE_CONSTRAINT {NOT NULL constraint failed: t2.a} SQLITE_CONSTRAINT_NOTNULL}
|
||||
|
||||
do_test capi2-3.20 {
|
||||
execsql {
|
||||
@ -555,7 +555,7 @@ do_test capi2-6.27 {
|
||||
INSERT INTO t1 VALUES(2,4,5);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test capi2-6.28 {
|
||||
list [sqlite3_step $VM1] \
|
||||
[sqlite3_column_count $VM1] \
|
||||
|
@ -41,7 +41,7 @@ do_test check-1.3 {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(6,7);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-1.4 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -51,7 +51,7 @@ do_test check-1.5 {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(4,3);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-1.6 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -88,7 +88,7 @@ do_test check-1.12 {
|
||||
catchsql {
|
||||
UPDATE t1 SET x=7 WHERE x==2
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-1.13 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -98,7 +98,7 @@ do_test check-1.14 {
|
||||
catchsql {
|
||||
UPDATE t1 SET x=5 WHERE x==2
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-1.15 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -142,17 +142,17 @@ do_test check-2.4 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(1.1, NULL, NULL);
|
||||
}
|
||||
} {1 {constraint one failed}}
|
||||
} {1 {CHECK constraint failed: one}}
|
||||
do_test check-2.5 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(NULL, 5, NULL);
|
||||
}
|
||||
} {1 {constraint two failed}}
|
||||
} {1 {CHECK constraint failed: two}}
|
||||
do_test check-2.6 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(NULL, NULL, 3.14159);
|
||||
}
|
||||
} {1 {constraint three failed}}
|
||||
} {1 {CHECK constraint failed: three}}
|
||||
|
||||
# Undocumented behavior: The CONSTRAINT name clause can follow a constraint.
|
||||
# Such a clause is ignored. But the parser must accept it for backwards
|
||||
@ -172,7 +172,7 @@ do_test check-2.11 {
|
||||
catchsql {
|
||||
INSERT INTO t2b VALUES('xyzzy','hi',5);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2b}}
|
||||
do_test check-2.12 {
|
||||
execsql {
|
||||
CREATE TABLE t2c(
|
||||
@ -188,7 +188,7 @@ do_test check-2.13 {
|
||||
catchsql {
|
||||
INSERT INTO t2c VALUES('xyzzy',7,8);
|
||||
}
|
||||
} {1 {constraint x_two failed}}
|
||||
} {1 {CHECK constraint failed: x_two}}
|
||||
do_test check-2.cleanup {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t2b;
|
||||
@ -256,7 +256,7 @@ do_test check-3.9 {
|
||||
catchsql {
|
||||
INSERT INTO t3 VALUES(111,222,333);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t3}}
|
||||
|
||||
do_test check-4.1 {
|
||||
execsql {
|
||||
@ -298,7 +298,7 @@ do_test check-4.6 {
|
||||
catchsql {
|
||||
UPDATE t4 SET x=0, y=1;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t4}}
|
||||
do_test check-4.7 {
|
||||
execsql {
|
||||
SELECT * FROM t4;
|
||||
@ -316,7 +316,7 @@ do_test check-4.9 {
|
||||
PRAGMA ignore_check_constraints=OFF;
|
||||
UPDATE t4 SET x=0, y=2;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t4}}
|
||||
ifcapable vacuum {
|
||||
do_test check_4.10 {
|
||||
catchsql {
|
||||
@ -367,7 +367,7 @@ do_test check-6.5 {
|
||||
catchsql {
|
||||
UPDATE OR FAIL t1 SET x=7-x, y=y+1;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-6.6 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -379,7 +379,7 @@ do_test check-6.7 {
|
||||
INSERT INTO t1 VALUES(1,30.0);
|
||||
INSERT OR ROLLBACK INTO t1 VALUES(8,40.0);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-6.8 {
|
||||
catchsql {
|
||||
COMMIT;
|
||||
@ -398,7 +398,7 @@ do_test check-6.12 {
|
||||
catchsql {
|
||||
REPLACE INTO t1 VALUES(6,7);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test check-6.13 {
|
||||
execsql {SELECT * FROM t1}
|
||||
} {3 12.0 2 20.0}
|
||||
@ -426,7 +426,8 @@ db func myfunc myfunc
|
||||
|
||||
do_execsql_test 7.1 { CREATE TABLE t6(a CHECK (myfunc(a))) }
|
||||
do_execsql_test 7.2 { INSERT INTO t6 VALUES(9) }
|
||||
do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } {1 {constraint failed}}
|
||||
do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } \
|
||||
{1 {CHECK constraint failed: t6}}
|
||||
|
||||
do_test 7.4 {
|
||||
sqlite3 db2 test.db
|
||||
@ -449,7 +450,7 @@ do_test 7.7 {
|
||||
do_test 7.8 {
|
||||
db2 func myfunc myfunc
|
||||
catchsql { INSERT INTO t6 VALUES(12) } db2
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t6}}
|
||||
|
||||
# 2013-08-02: Silently ignore database name qualifiers in CHECK constraints.
|
||||
#
|
||||
|
@ -473,7 +473,7 @@ do_test collate4-3.1 {
|
||||
INSERT INTO collate4t1 VALUES('abc');
|
||||
INSERT INTO collate4t1 VALUES('ABC');
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.2 {
|
||||
execsql {
|
||||
SELECT * FROM collate4t1;
|
||||
@ -483,13 +483,13 @@ do_test collate4-3.3 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.4 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 VALUES(1);
|
||||
UPDATE collate4t1 SET a = 'abc';
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.5 {
|
||||
execsql {
|
||||
DROP TABLE collate4t1;
|
||||
@ -501,7 +501,7 @@ do_test collate4-3.6 {
|
||||
INSERT INTO collate4t1 VALUES('abc');
|
||||
INSERT INTO collate4t1 VALUES('ABC');
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.7 {
|
||||
execsql {
|
||||
SELECT * FROM collate4t1;
|
||||
@ -511,13 +511,13 @@ do_test collate4-3.8 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.9 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 VALUES(1);
|
||||
UPDATE collate4t1 SET a = 'abc';
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.10 {
|
||||
execsql {
|
||||
DROP TABLE collate4t1;
|
||||
@ -530,7 +530,7 @@ do_test collate4-3.11 {
|
||||
INSERT INTO collate4t1 VALUES('abc');
|
||||
INSERT INTO collate4t1 VALUES('ABC');
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.12 {
|
||||
execsql {
|
||||
SELECT * FROM collate4t1;
|
||||
@ -540,13 +540,13 @@ do_test collate4-3.13 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
do_test collate4-3.14 {
|
||||
catchsql {
|
||||
INSERT INTO collate4t1 VALUES(1);
|
||||
UPDATE collate4t1 SET a = 'abc';
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: collate4t1.a}}
|
||||
|
||||
do_test collate4-3.15 {
|
||||
execsql {
|
||||
|
@ -242,7 +242,7 @@ foreach {i conf1 cmd t0 t1 t2} {
|
||||
15 {} {INSERT OR ABORT} 1 {} 1
|
||||
16 {} {INSERT OR ROLLBACK} 1 {} {}
|
||||
} {
|
||||
if {$t0} {set t1 {t1.c may not be NULL}}
|
||||
if {$t0} {set t1 {NOT NULL constraint failed: t1.c}}
|
||||
do_test conflict-5.$i {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
set r0 [catch {execsql [subst {
|
||||
@ -306,7 +306,7 @@ foreach {i conf1 cmd t0 t1 t2 t3 t4} {
|
||||
15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1
|
||||
16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0
|
||||
} {
|
||||
if {$t0} {set t1 {column a is not unique}}
|
||||
if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
|
||||
if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
|
||||
set t3 0
|
||||
} else {
|
||||
@ -493,13 +493,13 @@ do_test conflict-9.5 {
|
||||
INSERT INTO t2 VALUES(3,1,3,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict-9.6 {
|
||||
catchsql {
|
||||
UPDATE t2 SET b=b+1 WHERE b=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict-9.7 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
@ -507,7 +507,7 @@ do_test conflict-9.7 {
|
||||
INSERT INTO t2 VALUES(3,1,3,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict-9.8 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
@ -519,7 +519,7 @@ do_test conflict-9.9 {
|
||||
UPDATE t2 SET b=b+1 WHERE b=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict-9.10 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
@ -529,13 +529,13 @@ do_test conflict-9.11 {
|
||||
INSERT INTO t2 VALUES(3,3,3,1,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column d is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict-9.12 {
|
||||
catchsql {
|
||||
UPDATE t2 SET d=d+1 WHERE d=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column d is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict-9.13 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
@ -543,7 +543,7 @@ do_test conflict-9.13 {
|
||||
INSERT INTO t2 VALUES(3,3,3,1,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column d is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict-9.14 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
@ -555,7 +555,7 @@ do_test conflict-9.15 {
|
||||
UPDATE t2 SET d=d+1 WHERE d=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column d is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict-9.16 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
@ -565,13 +565,13 @@ do_test conflict-9.17 {
|
||||
INSERT INTO t2 VALUES(3,3,3,3,1);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column e is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
do_test conflict-9.18 {
|
||||
catchsql {
|
||||
UPDATE t2 SET e=e+1 WHERE e=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column e is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
do_test conflict-9.19 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
@ -579,7 +579,7 @@ do_test conflict-9.19 {
|
||||
INSERT INTO t2 VALUES(3,3,3,3,1);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column e is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test conflict-9.20 {
|
||||
catch {execsql {COMMIT}}
|
||||
@ -592,7 +592,7 @@ do_test conflict-9.21 {
|
||||
UPDATE t2 SET e=e+1 WHERE e=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {column e is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test conflict-9.22 {
|
||||
catch {execsql {COMMIT}}
|
||||
@ -782,7 +782,7 @@ do_test conflict-12.3 {
|
||||
catchsql {
|
||||
UPDATE t5 SET a=a+1 WHERE a=1;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t5.a}}
|
||||
verify_ex_errcode conflict-12.3b SQLITE_CONSTRAINT_PRIMARYKEY
|
||||
do_test conflict-12.4 {
|
||||
execsql {
|
||||
@ -790,6 +790,14 @@ do_test conflict-12.4 {
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} {2 one}
|
||||
do_test conflict-12.5 {
|
||||
catchsql {
|
||||
CREATE TABLE t5b(x);
|
||||
INSERT INTO t5b(rowid, x) VALUES(1,10),(2,11);
|
||||
UPDATE t5b SET rowid=rowid+1 WHERE x=10;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t5b.rowid}}
|
||||
verify_ex_errcode conflict-12.5b SQLITE_CONSTRAINT_ROWID
|
||||
|
||||
|
||||
# Ticket [c38baa3d969eab7946dc50ba9d9b4f0057a19437]
|
||||
@ -804,7 +812,7 @@ do_test conflict-13.1 {
|
||||
catchsql {
|
||||
REPLACE INTO t13 VALUES(2);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t13}}
|
||||
verify_ex_errcode conflict-13.1b SQLITE_CONSTRAINT_CHECK
|
||||
do_test conflict-13.2 {
|
||||
execsql {
|
||||
|
817
test/conflict2.test
Normal file
817
test/conflict2.test
Normal file
@ -0,0 +1,817 @@
|
||||
# 2013-11-04
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for the conflict resolution extension
|
||||
# in WITHOUT ROWID tables
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !conflict {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Create tables for the first group of tests.
|
||||
#
|
||||
do_test conflict2-1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT c FROM t1 ORDER BY c;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# cmd An INSERT or REPLACE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "c" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t2
|
||||
# t3 Number of temporary files created by this test
|
||||
#
|
||||
foreach {i cmd t0 t1 t2 t3} {
|
||||
1 INSERT 1 {} 1 0
|
||||
2 {INSERT OR IGNORE} 0 3 1 0
|
||||
3 {INSERT OR REPLACE} 0 4 1 0
|
||||
4 REPLACE 0 4 1 0
|
||||
5 {INSERT OR FAIL} 1 {} 1 0
|
||||
6 {INSERT OR ABORT} 1 {} 1 0
|
||||
7 {INSERT OR ROLLBACK} 1 {} {} 0
|
||||
} {
|
||||
do_test conflict2-1.$i {
|
||||
set ::sqlite_opentemp_count 0
|
||||
set r0 [catch {execsql [subst {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(1);
|
||||
$cmd INTO t1 VALUES(1,2,4);
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
|
||||
set r2 [execsql {SELECT x FROM t2}]
|
||||
set r3 $::sqlite_opentemp_count
|
||||
list $r0 $r1 $r2 $r3
|
||||
} [list $t0 $t1 $t2 $t3]
|
||||
}
|
||||
|
||||
# Create tables for the first group of tests.
|
||||
#
|
||||
do_test conflict2-2.0 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT c FROM t1 ORDER BY c;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# cmd An INSERT or REPLACE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "c" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t2
|
||||
#
|
||||
foreach {i cmd t0 t1 t2} {
|
||||
1 INSERT 1 {} 1
|
||||
2 {INSERT OR IGNORE} 0 3 1
|
||||
3 {INSERT OR REPLACE} 0 4 1
|
||||
4 REPLACE 0 4 1
|
||||
5 {INSERT OR FAIL} 1 {} 1
|
||||
6 {INSERT OR ABORT} 1 {} 1
|
||||
7 {INSERT OR ROLLBACK} 1 {} {}
|
||||
} {
|
||||
do_test conflict2-2.$i {
|
||||
set r0 [catch {execsql [subst {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(1);
|
||||
$cmd INTO t1 VALUES(1,2,4);
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
|
||||
set r2 [execsql {SELECT x FROM t2}]
|
||||
list $r0 $r1 $r2
|
||||
} [list $t0 $t1 $t2]
|
||||
}
|
||||
|
||||
# Create tables for the first group of tests.
|
||||
#
|
||||
do_test conflict2-3.0 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t1(a, b, c INTEGER, PRIMARY KEY(c), UNIQUE(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT c FROM t1 ORDER BY c;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# cmd An INSERT or REPLACE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "c" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t2
|
||||
#
|
||||
foreach {i cmd t0 t1 t2} {
|
||||
1 INSERT 1 {} 1
|
||||
2 {INSERT OR IGNORE} 0 3 1
|
||||
3 {INSERT OR REPLACE} 0 4 1
|
||||
4 REPLACE 0 4 1
|
||||
5 {INSERT OR FAIL} 1 {} 1
|
||||
6 {INSERT OR ABORT} 1 {} 1
|
||||
7 {INSERT OR ROLLBACK} 1 {} {}
|
||||
} {
|
||||
do_test conflict2-3.$i {
|
||||
set r0 [catch {execsql [subst {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(1);
|
||||
$cmd INTO t1 VALUES(1,2,4);
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
|
||||
set r2 [execsql {SELECT x FROM t2}]
|
||||
list $r0 $r1 $r2
|
||||
} [list $t0 $t1 $t2]
|
||||
}
|
||||
|
||||
do_test conflict2-4.0 {
|
||||
execsql {
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT x FROM t2;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# conf1 The conflict resolution algorithm on the UNIQUE constraint
|
||||
# cmd An INSERT or REPLACE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "c" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t2
|
||||
#
|
||||
foreach {i conf1 cmd t0 t1 t2} {
|
||||
1 {} INSERT 1 {} 1
|
||||
2 REPLACE INSERT 0 4 1
|
||||
3 IGNORE INSERT 0 3 1
|
||||
4 FAIL INSERT 1 {} 1
|
||||
5 ABORT INSERT 1 {} 1
|
||||
6 ROLLBACK INSERT 1 {} {}
|
||||
7 REPLACE {INSERT OR IGNORE} 0 3 1
|
||||
8 IGNORE {INSERT OR REPLACE} 0 4 1
|
||||
9 FAIL {INSERT OR IGNORE} 0 3 1
|
||||
10 ABORT {INSERT OR REPLACE} 0 4 1
|
||||
11 ROLLBACK {INSERT OR IGNORE } 0 3 1
|
||||
} {
|
||||
do_test conflict2-4.$i {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
set r0 [catch {execsql [subst {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a,b,c,PRIMARY KEY(a,b) $conf1) WITHOUT rowid;
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(1);
|
||||
$cmd INTO t1 VALUES(1,2,4);
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
|
||||
set r2 [execsql {SELECT x FROM t2}]
|
||||
list $r0 $r1 $r2
|
||||
} [list $t0 $t1 $t2]
|
||||
}
|
||||
|
||||
do_test conflict2-5.0 {
|
||||
execsql {
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t2(x);
|
||||
SELECT x FROM t2;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# conf1 The conflict resolution algorithm on the NOT NULL constraint
|
||||
# cmd An INSERT or REPLACE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "c" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t2
|
||||
#
|
||||
foreach {i conf1 cmd t0 t1 t2} {
|
||||
1 {} INSERT 1 {} 1
|
||||
2 REPLACE INSERT 0 5 1
|
||||
3 IGNORE INSERT 0 {} 1
|
||||
4 FAIL INSERT 1 {} 1
|
||||
5 ABORT INSERT 1 {} 1
|
||||
6 ROLLBACK INSERT 1 {} {}
|
||||
7 REPLACE {INSERT OR IGNORE} 0 {} 1
|
||||
8 IGNORE {INSERT OR REPLACE} 0 5 1
|
||||
9 FAIL {INSERT OR IGNORE} 0 {} 1
|
||||
10 ABORT {INSERT OR REPLACE} 0 5 1
|
||||
11 ROLLBACK {INSERT OR IGNORE} 0 {} 1
|
||||
12 {} {INSERT OR IGNORE} 0 {} 1
|
||||
13 {} {INSERT OR REPLACE} 0 5 1
|
||||
14 {} {INSERT OR FAIL} 1 {} 1
|
||||
15 {} {INSERT OR ABORT} 1 {} 1
|
||||
16 {} {INSERT OR ROLLBACK} 1 {} {}
|
||||
} {
|
||||
if {$t0} {set t1 {NOT NULL constraint failed: t1.c}}
|
||||
do_test conflict2-5.$i {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
set r0 [catch {execsql [subst {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a,b,c NOT NULL $conf1 DEFAULT 5);
|
||||
DELETE FROM t2;
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(1);
|
||||
$cmd INTO t1 VALUES(1,2,NULL);
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {!$r0} {set r1 [execsql {SELECT c FROM t1}]}
|
||||
set r2 [execsql {SELECT x FROM t2}]
|
||||
list $r0 $r1 $r2
|
||||
} [list $t0 $t1 $t2]
|
||||
}
|
||||
|
||||
do_test conflict2-6.0 {
|
||||
execsql {
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t2(a,b,c);
|
||||
INSERT INTO t2 VALUES(1,2,1);
|
||||
INSERT INTO t2 VALUES(2,3,2);
|
||||
INSERT INTO t2 VALUES(3,4,1);
|
||||
INSERT INTO t2 VALUES(4,5,4);
|
||||
SELECT c FROM t2 ORDER BY b;
|
||||
CREATE TABLE t3(x);
|
||||
INSERT INTO t3 VALUES(1);
|
||||
}
|
||||
} {1 2 1 4}
|
||||
|
||||
# Six columns of configuration data as follows:
|
||||
#
|
||||
# i The reference number of the test
|
||||
# conf1 The conflict resolution algorithm on the UNIQUE constraint
|
||||
# cmd An UPDATE command to execute against table t1
|
||||
# t0 True if there is an error from $cmd
|
||||
# t1 Content of "b" column of t1 assuming no error in $cmd
|
||||
# t2 Content of "x" column of t3
|
||||
# t3 Number of temporary files for tables
|
||||
# t4 Number of temporary files for statement journals
|
||||
#
|
||||
# Update: Since temporary table files are now opened lazily, and none
|
||||
# of the following tests use large quantities of data, t3 is always 0.
|
||||
#
|
||||
foreach {i conf1 cmd t0 t1 t2 t3 t4} {
|
||||
1 {} UPDATE 1 {6 7 8 9} 1 0 1
|
||||
2 REPLACE UPDATE 0 {7 6 9} 1 0 0
|
||||
3 IGNORE UPDATE 0 {6 7 3 9} 1 0 0
|
||||
4 FAIL UPDATE 1 {6 7 3 4} 1 0 0
|
||||
5 ABORT UPDATE 1 {1 2 3 4} 1 0 1
|
||||
6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0 0
|
||||
7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
|
||||
8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1
|
||||
9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
|
||||
10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1
|
||||
11 ROLLBACK {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
|
||||
12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
|
||||
13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1
|
||||
14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0
|
||||
15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1
|
||||
16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0
|
||||
} {
|
||||
if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
|
||||
if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
|
||||
set t3 0
|
||||
} else {
|
||||
set t3 [expr {$t3+$t4}]
|
||||
}
|
||||
do_test conflict2-6.$i {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
execsql {pragma temp_store=file}
|
||||
set ::sqlite_opentemp_count 0
|
||||
set r0 [catch {execsql [subst {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a,b,c, PRIMARY KEY(a) $conf1) WITHOUT rowid;
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
UPDATE t3 SET x=0;
|
||||
BEGIN;
|
||||
$cmd t3 SET x=1;
|
||||
$cmd t1 SET b=b*2;
|
||||
$cmd t1 SET a=c+5;
|
||||
}]} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
|
||||
set r2 [execsql {SELECT x FROM t3}]
|
||||
list $r0 $r1 $r2 $::sqlite_opentemp_count
|
||||
} [list $t0 $t1 $t2 $t3]
|
||||
}
|
||||
|
||||
# Test to make sure a lot of IGNOREs don't cause a stack overflow
|
||||
#
|
||||
do_test conflict2-7.1 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t3;
|
||||
CREATE TABLE t1(a PRIMARY KEY, b) without rowid;
|
||||
}
|
||||
for {set i 1} {$i<=50} {incr i} {
|
||||
execsql "INSERT into t1 values($i,[expr {$i+1}]);"
|
||||
}
|
||||
execsql {
|
||||
SELECT count(*), min(a), max(b) FROM t1;
|
||||
}
|
||||
} {50 1 51}
|
||||
do_test conflict2-7.2 {
|
||||
execsql {
|
||||
PRAGMA count_changes=on;
|
||||
UPDATE OR IGNORE t1 SET a=1000;
|
||||
}
|
||||
} {1}
|
||||
do_test conflict2-7.2.1 {
|
||||
db changes
|
||||
} {1}
|
||||
do_test conflict2-7.3 {
|
||||
execsql {
|
||||
SELECT b FROM t1 WHERE a=1000;
|
||||
}
|
||||
} {2}
|
||||
do_test conflict2-7.4 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {50}
|
||||
do_test conflict2-7.5 {
|
||||
execsql {
|
||||
PRAGMA count_changes=on;
|
||||
UPDATE OR REPLACE t1 SET a=1001;
|
||||
}
|
||||
} {50}
|
||||
do_test conflict2-7.5.1 {
|
||||
db changes
|
||||
} {50}
|
||||
do_test conflict2-7.7 {
|
||||
execsql {
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {1}
|
||||
|
||||
# Update for version 3: A SELECT statement no longer resets the change
|
||||
# counter (Test result changes from 0 to 50).
|
||||
do_test conflict2-7.7.1 {
|
||||
db changes
|
||||
} {50}
|
||||
|
||||
# Make sure the row count is right for rows that are ignored on
|
||||
# an insert.
|
||||
#
|
||||
do_test conflict2-8.1 {
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
}
|
||||
execsql {
|
||||
INSERT OR IGNORE INTO t1 VALUES(2,3);
|
||||
}
|
||||
} {1}
|
||||
do_test conflict2-8.1.1 {
|
||||
db changes
|
||||
} {1}
|
||||
do_test conflict2-8.2 {
|
||||
execsql {
|
||||
INSERT OR IGNORE INTO t1 VALUES(2,4);
|
||||
}
|
||||
} {0}
|
||||
do_test conflict2-8.2.1 {
|
||||
db changes
|
||||
} {0}
|
||||
do_test conflict2-8.3 {
|
||||
execsql {
|
||||
INSERT OR REPLACE INTO t1 VALUES(2,4);
|
||||
}
|
||||
} {1}
|
||||
do_test conflict2-8.3.1 {
|
||||
db changes
|
||||
} {1}
|
||||
do_test conflict2-8.4 {
|
||||
execsql {
|
||||
INSERT OR IGNORE INTO t1 SELECT * FROM t1;
|
||||
}
|
||||
} {0}
|
||||
do_test conflict2-8.4.1 {
|
||||
db changes
|
||||
} {0}
|
||||
do_test conflict2-8.5 {
|
||||
execsql {
|
||||
INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1;
|
||||
}
|
||||
} {2}
|
||||
do_test conflict2-8.5.1 {
|
||||
db changes
|
||||
} {2}
|
||||
do_test conflict2-8.6 {
|
||||
execsql {
|
||||
INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1;
|
||||
}
|
||||
} {3}
|
||||
do_test conflict2-8.6.1 {
|
||||
db changes
|
||||
} {3}
|
||||
|
||||
integrity_check conflict2-8.99
|
||||
|
||||
do_test conflict2-9.1 {
|
||||
execsql {
|
||||
PRAGMA count_changes=0;
|
||||
CREATE TABLE t2(
|
||||
a INTEGER PRIMARY KEY ON CONFLICT IGNORE,
|
||||
b INTEGER UNIQUE ON CONFLICT FAIL,
|
||||
c INTEGER UNIQUE ON CONFLICT REPLACE,
|
||||
d INTEGER UNIQUE ON CONFLICT ABORT,
|
||||
e INTEGER UNIQUE ON CONFLICT ROLLBACK
|
||||
) WITHOUT rowid;
|
||||
CREATE TABLE t3(x);
|
||||
INSERT INTO t3 VALUES(1);
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {1}
|
||||
do_test conflict2-9.2 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(1,1,1,1,1);
|
||||
INSERT INTO t2 VALUES(2,2,2,2,2);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {1 1 1 1 1 2 2 2 2 2}}
|
||||
do_test conflict2-9.3 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(1,3,3,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {1 1 1 1 1 2 2 2 2 2}}
|
||||
do_test conflict2-9.4 {
|
||||
catchsql {
|
||||
UPDATE t2 SET a=a+1 WHERE a=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {1 1 1 1 1 2 2 2 2 2}}
|
||||
do_test conflict2-9.5 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(3,1,3,3,3);
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict2-9.5b {
|
||||
db eval {SELECT * FROM t2;}
|
||||
} {1 1 1 1 1 2 2 2 2 2}
|
||||
do_test conflict2-9.6 {
|
||||
catchsql {
|
||||
UPDATE t2 SET b=b+1 WHERE b=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict2-9.6b {
|
||||
db eval {SELECT * FROM t2;}
|
||||
} {1 1 1 1 1 2 2 2 2 2}
|
||||
do_test conflict2-9.7 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
INSERT INTO t2 VALUES(3,1,3,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict2-9.8 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {2}
|
||||
do_test conflict2-9.9 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
UPDATE t2 SET b=b+1 WHERE b=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
do_test conflict2-9.10 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {3}
|
||||
do_test conflict2-9.11 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(3,3,3,1,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict2-9.12 {
|
||||
catchsql {
|
||||
UPDATE t2 SET d=d+1 WHERE d=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict2-9.13 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
INSERT INTO t2 VALUES(3,3,3,1,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict2-9.14 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {4}
|
||||
do_test conflict2-9.15 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
UPDATE t2 SET d=d+1 WHERE d=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.d}}
|
||||
do_test conflict2-9.16 {
|
||||
execsql {COMMIT}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {5}
|
||||
do_test conflict2-9.17 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(3,3,3,3,1);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
do_test conflict2-9.18 {
|
||||
catchsql {
|
||||
UPDATE t2 SET e=e+1 WHERE e=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
do_test conflict2-9.19 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
INSERT INTO t2 VALUES(3,3,3,3,1);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
verify_ex_errcode conflict2-9.21b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test conflict2-9.20 {
|
||||
catch {execsql {COMMIT}}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {5}
|
||||
do_test conflict2-9.21 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
UPDATE t2 SET e=e+1 WHERE e=1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.e}}
|
||||
verify_ex_errcode conflict2-9.21b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test conflict2-9.22 {
|
||||
catch {execsql {COMMIT}}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {5}
|
||||
do_test conflict2-9.23 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(3,3,1,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {2 2 2 2 2 3 3 1 3 3}}
|
||||
do_test conflict2-9.24 {
|
||||
catchsql {
|
||||
UPDATE t2 SET c=c-1 WHERE c=2;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {2 2 1 2 2}}
|
||||
do_test conflict2-9.25 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
UPDATE t3 SET x=x+1;
|
||||
INSERT INTO t2 VALUES(3,3,1,3,3);
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {0 {3 3 1 3 3}}
|
||||
do_test conflict2-9.26 {
|
||||
catch {execsql {COMMIT}}
|
||||
execsql {SELECT * FROM t3}
|
||||
} {6}
|
||||
|
||||
do_test conflict2-10.1 {
|
||||
catchsql {
|
||||
DELETE FROM t1;
|
||||
BEGIN;
|
||||
INSERT OR ROLLBACK INTO t1 VALUES(1,2);
|
||||
INSERT OR ROLLBACK INTO t1 VALUES(1,3);
|
||||
COMMIT;
|
||||
}
|
||||
execsql {SELECT * FROM t1}
|
||||
} {}
|
||||
do_test conflict2-10.2 {
|
||||
catchsql {
|
||||
CREATE TABLE t4(x);
|
||||
CREATE UNIQUE INDEX t4x ON t4(x);
|
||||
BEGIN;
|
||||
INSERT OR ROLLBACK INTO t4 VALUES(1);
|
||||
INSERT OR ROLLBACK INTO t4 VALUES(1);
|
||||
COMMIT;
|
||||
}
|
||||
execsql {SELECT * FROM t4}
|
||||
} {}
|
||||
|
||||
# Ticket #1171. Make sure statement rollbacks do not
|
||||
# damage the database.
|
||||
#
|
||||
do_test conflict2-11.1 {
|
||||
execsql {
|
||||
-- Create a database object (pages 2, 3 of the file)
|
||||
BEGIN;
|
||||
CREATE TABLE abc(a PRIMARY KEY, b, c) WITHOUT rowid;
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
INSERT INTO abc VALUES(4, 5, 6);
|
||||
INSERT INTO abc VALUES(7, 8, 9);
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
|
||||
# Set a small cache size so that changes will spill into
|
||||
# the database file.
|
||||
execsql {
|
||||
PRAGMA cache_size = 10;
|
||||
}
|
||||
|
||||
# Make lots of changes. Because of the small cache, some
|
||||
# (most?) of these changes will spill into the disk file.
|
||||
# In other words, some of the changes will not be held in
|
||||
# cache.
|
||||
#
|
||||
execsql {
|
||||
BEGIN;
|
||||
-- Make sure the pager is in EXCLUSIVE state.
|
||||
CREATE TABLE def(d, e, f);
|
||||
INSERT INTO def VALUES
|
||||
('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
DELETE FROM abc WHERE a = 4;
|
||||
}
|
||||
|
||||
# Execute a statement that does a statement rollback due to
|
||||
# a constraint failure.
|
||||
#
|
||||
catchsql {
|
||||
INSERT INTO abc SELECT 10, 20, 30 FROM def;
|
||||
}
|
||||
|
||||
# Rollback the database. Verify that the state of the ABC table
|
||||
# is unchanged from the beginning of the transaction. In other words,
|
||||
# make sure the DELETE on table ABC that occurred within the transaction
|
||||
# had no effect.
|
||||
#
|
||||
execsql {
|
||||
ROLLBACK;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
integrity_check conflict2-11.2
|
||||
|
||||
# Repeat test conflict2-11.1 but this time commit.
|
||||
#
|
||||
do_test conflict2-11.3 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
-- Make sure the pager is in EXCLUSIVE state.
|
||||
UPDATE abc SET a=a+1;
|
||||
CREATE TABLE def(d, e, f);
|
||||
INSERT INTO def VALUES
|
||||
('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
DELETE FROM abc WHERE a = 4;
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO abc SELECT 10, 20, 30 FROM def;
|
||||
}
|
||||
execsql {
|
||||
ROLLBACK;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
# Repeat test conflict2-11.1 but this time commit.
|
||||
#
|
||||
do_test conflict2-11.5 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
-- Make sure the pager is in EXCLUSIVE state.
|
||||
CREATE TABLE def(d, e, f);
|
||||
INSERT INTO def VALUES
|
||||
('xxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzz');
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
INSERT INTO def SELECT * FROM def;
|
||||
DELETE FROM abc WHERE a = 4;
|
||||
}
|
||||
catchsql {
|
||||
INSERT INTO abc SELECT 10, 20, 30 FROM def;
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
SELECT * FROM abc;
|
||||
}
|
||||
} {1 2 3 7 8 9}
|
||||
integrity_check conflict2-11.6
|
||||
|
||||
# Make sure UPDATE OR REPLACE works on tables that have only
|
||||
# an INTEGER PRIMARY KEY.
|
||||
#
|
||||
do_test conflict2-12.1 {
|
||||
execsql {
|
||||
CREATE TABLE t5(a INTEGER PRIMARY KEY, b text) WITHOUT rowid;
|
||||
INSERT INTO t5 VALUES(1,'one');
|
||||
INSERT INTO t5 VALUES(2,'two');
|
||||
SELECT * FROM t5
|
||||
}
|
||||
} {1 one 2 two}
|
||||
do_test conflict2-12.2 {
|
||||
execsql {
|
||||
UPDATE OR IGNORE t5 SET a=a+1 WHERE a=1;
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} {1 one 2 two}
|
||||
do_test conflict2-12.3 {
|
||||
catchsql {
|
||||
UPDATE t5 SET a=a+1 WHERE a=1;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t5.a}}
|
||||
verify_ex_errcode conflict2-12.3b SQLITE_CONSTRAINT_PRIMARYKEY
|
||||
do_test conflict2-12.4 {
|
||||
execsql {
|
||||
UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1;
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} {2 one}
|
||||
|
||||
|
||||
# Ticket [c38baa3d969eab7946dc50ba9d9b4f0057a19437]
|
||||
# REPLACE works like ABORT on a CHECK constraint.
|
||||
#
|
||||
do_test conflict2-13.1 {
|
||||
execsql {
|
||||
CREATE TABLE t13(a PRIMARY KEY CHECK(a!=2)) WITHOUT rowid;
|
||||
BEGIN;
|
||||
REPLACE INTO t13 VALUES(1);
|
||||
}
|
||||
catchsql {
|
||||
REPLACE INTO t13 VALUES(2);
|
||||
}
|
||||
} {1 {CHECK constraint failed: t13}}
|
||||
verify_ex_errcode conflict2-13.1b SQLITE_CONSTRAINT_CHECK
|
||||
do_test conflict2-13.2 {
|
||||
execsql {
|
||||
REPLACE INTO t13 VALUES(3);
|
||||
COMMIT;
|
||||
SELECT * FROM t13;
|
||||
}
|
||||
} {1 3}
|
||||
|
||||
|
||||
finish_test
|
356
test/conflict3.test
Normal file
356
test/conflict3.test
Normal file
@ -0,0 +1,356 @@
|
||||
# 2013-11-05
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests for the conflict resolution extension
|
||||
# to SQLite.
|
||||
#
|
||||
# This file focuses on making sure that combinations of REPLACE,
|
||||
# IGNORE, and FAIL conflict resolution play well together.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !conflict {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test conflict-1.1 {
|
||||
CREATE TABLE t1(
|
||||
a INTEGER PRIMARY KEY ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-1.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-1.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-1.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Replete the tests above, but this time on a table non-INTEGER primary key.
|
||||
#
|
||||
do_execsql_test conflict-2.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a INT PRIMARY KEY ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-2.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-2.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-2.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Replete again on a WITHOUT ROWID table.
|
||||
#
|
||||
do_execsql_test conflict-3.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a INT PRIMARY KEY ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-3.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-3.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-3.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Arrange the table rows in a different order and repeat.
|
||||
#
|
||||
do_execsql_test conflict-4.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL,
|
||||
a INT PRIMARY KEY ON CONFLICT REPLACE
|
||||
) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-4.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-4.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-4.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Arrange the table rows in a different order and repeat.
|
||||
#
|
||||
do_execsql_test conflict-5.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
a INT PRIMARY KEY ON CONFLICT REPLACE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-5.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-5.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-5.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Arrange the table rows in a different order and repeat.
|
||||
#
|
||||
do_execsql_test conflict-6.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
c UNIQUE ON CONFLICT FAIL,
|
||||
a INT PRIMARY KEY ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-6.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-6.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-6.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Change which column is the PRIMARY KEY
|
||||
#
|
||||
do_execsql_test conflict-7.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a UNIQUE ON CONFLICT REPLACE,
|
||||
b INTEGER PRIMARY KEY ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-7.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-7.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-7.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Change which column is the PRIMARY KEY
|
||||
#
|
||||
do_execsql_test conflict-8.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a UNIQUE ON CONFLICT REPLACE,
|
||||
b INT PRIMARY KEY ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-8.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-8.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-8.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Change which column is the PRIMARY KEY
|
||||
#
|
||||
do_execsql_test conflict-9.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a UNIQUE ON CONFLICT REPLACE,
|
||||
b INT PRIMARY KEY ON CONFLICT IGNORE,
|
||||
c UNIQUE ON CONFLICT FAIL
|
||||
) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-9.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-9.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-9.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Change which column is the PRIMARY KEY
|
||||
#
|
||||
do_execsql_test conflict-10.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a UNIQUE ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c INTEGER PRIMARY KEY ON CONFLICT FAIL
|
||||
);
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-10.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-10.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-10.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
# Change which column is the PRIMARY KEY
|
||||
#
|
||||
do_execsql_test conflict-11.1 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
a UNIQUE ON CONFLICT REPLACE,
|
||||
b UNIQUE ON CONFLICT IGNORE,
|
||||
c PRIMARY KEY ON CONFLICT FAIL
|
||||
) WITHOUT ROWID;
|
||||
INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert a row that conflicts on column B. The insert should be ignored.
|
||||
#
|
||||
do_execsql_test conflict-11.2 {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,5);
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4}
|
||||
|
||||
# Insert two rows where the second conflicts on C. The first row show go
|
||||
# and and then there should be a constraint error.
|
||||
#
|
||||
do_test conflict-11.3 {
|
||||
catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
|
||||
} {1 {UNIQUE constraint failed: t1.c}}
|
||||
do_execsql_test conflict-11.4 {
|
||||
SELECT a,b,c FROM t1 ORDER BY a;
|
||||
} {1 2 3 2 3 4 4 5 6}
|
||||
|
||||
|
||||
finish_test
|
@ -1182,13 +1182,14 @@ do_execsql_test 4.3.0 {
|
||||
INSERT INTO t2 VALUES(X'ABCDEF', 'three');
|
||||
} {}
|
||||
|
||||
do_createtable_tests 4.3.1 -error { %s not unique } {
|
||||
do_createtable_tests 4.3.1 -error {UNIQUE constraint failed: t1.x} {
|
||||
1 "INSERT INTO t1 VALUES(0, 0)" {"column x is"}
|
||||
2 "INSERT INTO t1 VALUES(45.5, 'abc')" {"column x is"}
|
||||
3 "INSERT INTO t1 VALUES(0.0, 'abc')" {"column x is"}
|
||||
4 "INSERT INTO t1 VALUES('brambles', 'abc')" {"column x is"}
|
||||
5 "INSERT INTO t1 VALUES(X'ABCDEF', 'abc')" {"column x is"}
|
||||
|
||||
}
|
||||
do_createtable_tests 4.3.1 -error {UNIQUE constraint failed: t2.x, t2.y} {
|
||||
6 "INSERT INTO t2 VALUES(0, 'zero')" {"columns x, y are"}
|
||||
7 "INSERT INTO t2 VALUES(45.5, 'one')" {"columns x, y are"}
|
||||
8 "INSERT INTO t2 VALUES(0.0, 'zero')" {"columns x, y are"}
|
||||
@ -1208,13 +1209,14 @@ do_createtable_tests 4.3.2 {
|
||||
9 "INSERT INTO t2 VALUES('brambles', 'abc')" {}
|
||||
10 "INSERT INTO t2 VALUES(X'ABCDEF', 'abc')" {}
|
||||
}
|
||||
do_createtable_tests 4.3.3 -error { %s not unique } {
|
||||
do_createtable_tests 4.3.3 -error {UNIQUE constraint failed: t1.x} {
|
||||
1 "UPDATE t1 SET x=0 WHERE y='two'" {"column x is"}
|
||||
2 "UPDATE t1 SET x='brambles' WHERE y='three'" {"column x is"}
|
||||
3 "UPDATE t1 SET x=45.5 WHERE y='zero'" {"column x is"}
|
||||
4 "UPDATE t1 SET x=X'ABCDEF' WHERE y='one'" {"column x is"}
|
||||
5 "UPDATE t1 SET x=0.0 WHERE y='three'" {"column x is"}
|
||||
|
||||
}
|
||||
do_createtable_tests 4.3.3 -error {UNIQUE constraint failed: t2.x, t2.y} {
|
||||
6 "UPDATE t2 SET x=0, y='zero' WHERE y='two'" {"columns x, y are"}
|
||||
7 "UPDATE t2 SET x='brambles', y='two' WHERE y='three'"
|
||||
{"columns x, y are"}
|
||||
@ -1305,24 +1307,24 @@ do_execsql_test 4.7.0 {
|
||||
INSERT INTO t4 VALUES('xyx', 2, 1);
|
||||
INSERT INTO t4 VALUES('uvw', 1, 1);
|
||||
}
|
||||
do_createtable_tests 4.7.1 -error { %s not unique } {
|
||||
1 "INSERT INTO t1 VALUES(1, 'one')" {{column a is}}
|
||||
2 "INSERT INTO t1 VALUES(4.3, 'two')" {{column a is}}
|
||||
3 "INSERT INTO t1 VALUES('reveal', 'three')" {{column a is}}
|
||||
4 "INSERT INTO t1 VALUES(X'123456', 'four')" {{column a is}}
|
||||
do_createtable_tests 4.7.1 -error {UNIQUE constraint failed: %s} {
|
||||
1 "INSERT INTO t1 VALUES(1, 'one')" {{t1.a}}
|
||||
2 "INSERT INTO t1 VALUES(4.3, 'two')" {{t1.a}}
|
||||
3 "INSERT INTO t1 VALUES('reveal', 'three')" {{t1.a}}
|
||||
4 "INSERT INTO t1 VALUES(X'123456', 'four')" {{t1.a}}
|
||||
|
||||
5 "UPDATE t1 SET a = 1 WHERE rowid=2" {{column a is}}
|
||||
6 "UPDATE t1 SET a = 4.3 WHERE rowid=3" {{column a is}}
|
||||
7 "UPDATE t1 SET a = 'reveal' WHERE rowid=4" {{column a is}}
|
||||
8 "UPDATE t1 SET a = X'123456' WHERE rowid=1" {{column a is}}
|
||||
5 "UPDATE t1 SET a = 1 WHERE rowid=2" {{t1.a}}
|
||||
6 "UPDATE t1 SET a = 4.3 WHERE rowid=3" {{t1.a}}
|
||||
7 "UPDATE t1 SET a = 'reveal' WHERE rowid=4" {{t1.a}}
|
||||
8 "UPDATE t1 SET a = X'123456' WHERE rowid=1" {{t1.a}}
|
||||
|
||||
9 "INSERT INTO t4 VALUES('xyx', 1, 1)" {{columns a, b, c are}}
|
||||
10 "INSERT INTO t4 VALUES('xyx', 2, 1)" {{columns a, b, c are}}
|
||||
11 "INSERT INTO t4 VALUES('uvw', 1, 1)" {{columns a, b, c are}}
|
||||
9 "INSERT INTO t4 VALUES('xyx', 1, 1)" {{t4.a, t4.b, t4.c}}
|
||||
10 "INSERT INTO t4 VALUES('xyx', 2, 1)" {{t4.a, t4.b, t4.c}}
|
||||
11 "INSERT INTO t4 VALUES('uvw', 1, 1)" {{t4.a, t4.b, t4.c}}
|
||||
|
||||
12 "UPDATE t4 SET a='xyx' WHERE rowid=3" {{columns a, b, c are}}
|
||||
13 "UPDATE t4 SET b=1 WHERE rowid=2" {{columns a, b, c are}}
|
||||
14 "UPDATE t4 SET a=0, b=0, c=0" {{columns a, b, c are}}
|
||||
12 "UPDATE t4 SET a='xyx' WHERE rowid=3" {{t4.a, t4.b, t4.c}}
|
||||
13 "UPDATE t4 SET b=1 WHERE rowid=2" {{t4.a, t4.b, t4.c}}
|
||||
14 "UPDATE t4 SET a=0, b=0, c=0" {{t4.a, t4.b, t4.c}}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-21289-11559 As with PRIMARY KEY constraints, for the
|
||||
@ -1404,21 +1406,21 @@ do_execsql_test 4.11 {
|
||||
INSERT INTO t2 SELECT * FROM x2;
|
||||
}
|
||||
|
||||
do_createtable_tests 4.11 -error {constraint failed} {
|
||||
1a "INSERT INTO x1 VALUES('one', 0)" {}
|
||||
1b "INSERT INTO t1 VALUES('one', -4.0)" {}
|
||||
do_createtable_tests 4.11 -error {CHECK constraint failed: %s} {
|
||||
1a "INSERT INTO x1 VALUES('one', 0)" {x1}
|
||||
1b "INSERT INTO t1 VALUES('one', -4.0)" {t1}
|
||||
|
||||
2a "INSERT INTO x2 VALUES('abc', 1)" {}
|
||||
2b "INSERT INTO t2 VALUES('abc', 1)" {}
|
||||
2a "INSERT INTO x2 VALUES('abc', 1)" {x2}
|
||||
2b "INSERT INTO t2 VALUES('abc', 1)" {t2}
|
||||
|
||||
3a "INSERT INTO x2 VALUES(0, 'abc')" {}
|
||||
3b "INSERT INTO t2 VALUES(0, 'abc')" {}
|
||||
3a "INSERT INTO x2 VALUES(0, 'abc')" {x2}
|
||||
3b "INSERT INTO t2 VALUES(0, 'abc')" {t2}
|
||||
|
||||
4a "UPDATE t1 SET b=-1 WHERE rowid=1" {}
|
||||
4b "UPDATE x1 SET b=-1 WHERE rowid=1" {}
|
||||
4a "UPDATE t1 SET b=-1 WHERE rowid=1" {t1}
|
||||
4b "UPDATE x1 SET b=-1 WHERE rowid=1" {x1}
|
||||
|
||||
4a "UPDATE x2 SET a='' WHERE rowid=1" {}
|
||||
4b "UPDATE t2 SET a='' WHERE rowid=1" {}
|
||||
4a "UPDATE x2 SET a='' WHERE rowid=1" {x2}
|
||||
4b "UPDATE t2 SET a='' WHERE rowid=1" {t2}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-34109-39108 If the CHECK expression evaluates to NULL,
|
||||
@ -1469,9 +1471,7 @@ do_execsql_test 4.14.0 {
|
||||
INSERT INTO t3 VALUES('x', 'y', 'z');
|
||||
INSERT INTO t3 VALUES(1, 2, 3);
|
||||
}
|
||||
do_createtable_tests 4.14 -error {
|
||||
%s may not be NULL
|
||||
} {
|
||||
do_createtable_tests 4.14 -error {NOT NULL constraint failed: %s} {
|
||||
1 "INSERT INTO t1 VALUES(NULL, 'a')" {t1.a}
|
||||
2 "INSERT INTO t2 VALUES(NULL, 'b')" {t2.a}
|
||||
3 "INSERT INTO t3 VALUES('c', 'd', NULL)" {t3.c}
|
||||
@ -1537,12 +1537,12 @@ do_execsql_test 4.15.0 {
|
||||
}
|
||||
|
||||
foreach {tn tbl res ac data} {
|
||||
1 t1_ab {1 {column a is not unique}} 0 {1 one 2 two 3 three}
|
||||
2 t1_ro {1 {column a is not unique}} 1 {1 one 2 two}
|
||||
3 t1_fa {1 {column a is not unique}} 0 {1 one 2 two 3 three 4 string}
|
||||
1 t1_ab {1 {UNIQUE constraint failed: t1_ab.a}} 0 {1 one 2 two 3 three}
|
||||
2 t1_ro {1 {UNIQUE constraint failed: t1_ro.a}} 1 {1 one 2 two}
|
||||
3 t1_fa {1 {UNIQUE constraint failed: t1_fa.a}} 0 {1 one 2 two 3 three 4 string}
|
||||
4 t1_ig {0 {}} 0 {1 one 2 two 3 three 4 string 6 string}
|
||||
5 t1_re {0 {}} 0 {1 one 2 two 4 string 3 string 6 string}
|
||||
6 t1_xx {1 {column a is not unique}} 0 {1 one 2 two 3 three}
|
||||
6 t1_xx {1 {UNIQUE constraint failed: t1_xx.a}} 0 {1 one 2 two 3 three}
|
||||
} {
|
||||
catchsql COMMIT
|
||||
do_execsql_test 4.15.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
|
||||
@ -1555,12 +1555,12 @@ foreach {tn tbl res ac data} {
|
||||
do_execsql_test 4.15.$tn.4 "SELECT * FROM $tbl" $data
|
||||
}
|
||||
foreach {tn tbl res ac data} {
|
||||
1 t2_ab {1 {t2_ab.b may not be NULL}} 0 {1 one 2 two 3 three}
|
||||
2 t2_ro {1 {t2_ro.b may not be NULL}} 1 {1 one 2 two}
|
||||
3 t2_fa {1 {t2_fa.b may not be NULL}} 0 {1 one 2 two 3 three 4 xx}
|
||||
1 t2_ab {1 {NOT NULL constraint failed: t2_ab.b}} 0 {1 one 2 two 3 three}
|
||||
2 t2_ro {1 {NOT NULL constraint failed: t2_ro.b}} 1 {1 one 2 two}
|
||||
3 t2_fa {1 {NOT NULL constraint failed: t2_fa.b}} 0 {1 one 2 two 3 three 4 xx}
|
||||
4 t2_ig {0 {}} 0 {1 one 2 two 3 three 4 xx 6 xx}
|
||||
5 t2_re {1 {t2_re.b may not be NULL}} 0 {1 one 2 two 3 three}
|
||||
6 t2_xx {1 {t2_xx.b may not be NULL}} 0 {1 one 2 two 3 three}
|
||||
5 t2_re {1 {NOT NULL constraint failed: t2_re.b}} 0 {1 one 2 two 3 three}
|
||||
6 t2_xx {1 {NOT NULL constraint failed: t2_xx.b}} 0 {1 one 2 two 3 three}
|
||||
} {
|
||||
catchsql COMMIT
|
||||
do_execsql_test 4.16.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
|
||||
@ -1573,12 +1573,16 @@ foreach {tn tbl res ac data} {
|
||||
do_execsql_test 4.16.$tn.4 "SELECT * FROM $tbl" $data
|
||||
}
|
||||
foreach {tn tbl res ac data} {
|
||||
1 t3_ab {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three}
|
||||
2 t3_ro {1 {columns a, b are not unique}} 1 {1 one 2 two}
|
||||
3 t3_fa {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three 4 three}
|
||||
1 t3_ab {1 {UNIQUE constraint failed: t3_ab.a, t3_ab.b}}
|
||||
0 {1 one 2 two 3 three}
|
||||
2 t3_ro {1 {UNIQUE constraint failed: t3_ro.a, t3_ro.b}}
|
||||
1 {1 one 2 two}
|
||||
3 t3_fa {1 {UNIQUE constraint failed: t3_fa.a, t3_fa.b}}
|
||||
0 {1 one 2 two 3 three 4 three}
|
||||
4 t3_ig {0 {}} 0 {1 one 2 two 3 three 4 three 6 three}
|
||||
5 t3_re {0 {}} 0 {1 one 2 two 4 three 3 three 6 three}
|
||||
6 t3_xx {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three}
|
||||
6 t3_xx {1 {UNIQUE constraint failed: t3_xx.a, t3_xx.b}}
|
||||
0 {1 one 2 two 3 three}
|
||||
} {
|
||||
catchsql COMMIT
|
||||
do_execsql_test 4.17.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
|
||||
@ -1609,7 +1613,7 @@ do_execsql_test 4.18.1 {
|
||||
do_execsql_test 4.18.2 { BEGIN; INSERT INTO t4 VALUES(5, 6) }
|
||||
do_catchsql_test 4.18.3 {
|
||||
INSERT INTO t4 SELECT a+4, b+4 FROM t4
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t4}}
|
||||
do_test e_createtable-4.18.4 { sqlite3_get_autocommit db } 0
|
||||
do_execsql_test 4.18.5 { SELECT * FROM t4 } {1 2 3 4 5 6}
|
||||
|
||||
@ -1622,7 +1626,7 @@ do_execsql_test 4.19.0 {
|
||||
do_catchsql_test 4.19.1 { INSERT INTO t5 VALUES(NULL, 'not null') } {0 {}}
|
||||
do_execsql_test 4.19.2 { SELECT * FROM t5 } {}
|
||||
do_catchsql_test 4.19.3 { INSERT INTO t5 VALUES('not null', NULL) } \
|
||||
{1 {t5.b may not be NULL}}
|
||||
{1 {NOT NULL constraint failed: t5.b}}
|
||||
do_execsql_test 4.19.4 { SELECT * FROM t5 } {}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
@ -1747,16 +1751,16 @@ do_execsql_test 5.4.3 {
|
||||
|
||||
do_catchsql_test 5.4.4.1 {
|
||||
INSERT INTO t6 VALUES(2)
|
||||
} {1 {column pk is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t6.pk}}
|
||||
do_catchsql_test 5.4.4.2 {
|
||||
INSERT INTO t7 VALUES(2)
|
||||
} {1 {column pk is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t7.pk}}
|
||||
do_catchsql_test 5.4.4.3 {
|
||||
INSERT INTO t8 VALUES(2)
|
||||
} {1 {column pk is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t8.pk}}
|
||||
do_catchsql_test 5.4.4.4 {
|
||||
INSERT INTO t9 VALUES(2)
|
||||
} {1 {column pk is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t9.pk}}
|
||||
|
||||
# EVIDENCE-OF: R-56094-57830 the following three table declarations all
|
||||
# cause the column "x" to be an alias for the rowid (an integer primary
|
||||
|
106
test/e_fkey.test
106
test/e_fkey.test
@ -211,7 +211,7 @@ do_test e_fkey-6.1 {
|
||||
catchsql {
|
||||
DELETE FROM t1
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-6.2 {
|
||||
execsql { PRAGMA foreign_keys }
|
||||
} {1}
|
||||
@ -265,11 +265,11 @@ do_test e_fkey-7.1 {
|
||||
#
|
||||
do_test e_fkey-8.1 {
|
||||
catchsql { INSERT INTO track VALUES(1, 'track 1', 1) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-8.2 {
|
||||
execsql { INSERT INTO artist VALUES(2, 'artist 1') }
|
||||
catchsql { INSERT INTO track VALUES(1, 'track 1', 1) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-8.2 {
|
||||
execsql { INSERT INTO track VALUES(1, 'track 1', 2) }
|
||||
} {}
|
||||
@ -283,7 +283,7 @@ do_test e_fkey-8.2 {
|
||||
#
|
||||
do_test e_fkey-9.1 {
|
||||
catchsql { DELETE FROM artist WHERE artistid = 2 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-9.2 {
|
||||
execsql {
|
||||
DELETE FROM track WHERE trackartist = 2;
|
||||
@ -311,14 +311,14 @@ do_test e_fkey-10.2 {
|
||||
do_test e_fkey-10.3 {
|
||||
# Setting the trackid to a non-NULL value fails, of course.
|
||||
catchsql { UPDATE track SET trackartist = 5 WHERE trackid = 1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-10.4 {
|
||||
execsql {
|
||||
INSERT INTO artist VALUES(5, 'artist 5');
|
||||
UPDATE track SET trackartist = 5 WHERE trackid = 1;
|
||||
}
|
||||
catchsql { DELETE FROM artist WHERE artistid = 5}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-10.5 {
|
||||
execsql {
|
||||
UPDATE track SET trackartist = NULL WHERE trackid = 1;
|
||||
@ -344,8 +344,8 @@ proc test_r52486_21352 {tn sql} {
|
||||
set res [catchsql $sql]
|
||||
set results {
|
||||
{0 {}}
|
||||
{1 {PRIMARY KEY must be unique}}
|
||||
{1 {foreign key constraint failed}}
|
||||
{1 {UNIQUE constraint failed: artist.artistid}}
|
||||
{1 {FOREIGN KEY constraint failed}}
|
||||
}
|
||||
if {[lsearch $results $res]<0} {
|
||||
error $res
|
||||
@ -409,7 +409,7 @@ do_test e_fkey-12.1 {
|
||||
} {}
|
||||
do_test e_fkey-12.2 {
|
||||
catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) }
|
||||
} {1 {track.trackartist may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: track.trackartist}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-16127-35442
|
||||
@ -438,7 +438,7 @@ do_test e_fkey-13.1 {
|
||||
} {}
|
||||
do_test e_fkey-13.2 {
|
||||
catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', 3) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-13.3 {
|
||||
execsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) }
|
||||
} {}
|
||||
@ -446,7 +446,7 @@ do_test e_fkey-13.4 {
|
||||
catchsql {
|
||||
UPDATE track SET trackartist = 3 WHERE trackname = 'Mr. Bojangles';
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-13.5 {
|
||||
execsql {
|
||||
INSERT INTO artist VALUES(3, 'Sammy Davis Jr.');
|
||||
@ -464,7 +464,7 @@ do_test e_fkey-14.1 {
|
||||
catchsql {
|
||||
DELETE FROM artist WHERE artistname = 'Frank Sinatra';
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-14.2 {
|
||||
execsql {
|
||||
DELETE FROM track WHERE trackname = 'My Way';
|
||||
@ -475,7 +475,7 @@ do_test e_fkey-14.3 {
|
||||
catchsql {
|
||||
UPDATE artist SET artistid=4 WHERE artistname = 'Dean Martin';
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-14.4 {
|
||||
execsql {
|
||||
DELETE FROM track WHERE trackname IN('That''s Amore', 'Christmas Blues');
|
||||
@ -513,7 +513,7 @@ do_test e_fkey-15.1 {
|
||||
proc test_efkey_45 {tn isError sql} {
|
||||
do_test e_fkey-15.$tn.1 "
|
||||
catchsql {$sql}
|
||||
" [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
|
||||
" [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
|
||||
|
||||
do_test e_fkey-15.$tn.2 {
|
||||
execsql {
|
||||
@ -557,10 +557,10 @@ do_test e_fkey-16.2 {
|
||||
} {}
|
||||
do_test e_fkey-16.3 {
|
||||
catchsql { UPDATE t2 SET b = 'two' WHERE rowid = 1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-16.4 {
|
||||
catchsql { DELETE FROM t1 WHERE rowid = 1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Specifically, test that when comparing child and parent key values the
|
||||
@ -592,7 +592,7 @@ do_test e_fkey-17.3 {
|
||||
} {integer integer text}
|
||||
do_test e_fkey-17.4 {
|
||||
catchsql { DELETE FROM t1 WHERE rowid = 2 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
###########################################################################
|
||||
### SECTION 3: Required and Suggested Database Indexes
|
||||
@ -896,7 +896,7 @@ do_test e_fkey-23.1 {
|
||||
proc test_efkey_60 {tn isError sql} {
|
||||
do_test e_fkey-23.$tn "
|
||||
catchsql {$sql}
|
||||
" [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
|
||||
" [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
|
||||
}
|
||||
|
||||
test_efkey_60 2 1 "INSERT INTO c1 VALUES(239, 231)"
|
||||
@ -933,7 +933,7 @@ do_test e_fkey-24.1 {
|
||||
proc test_efkey_61 {tn isError sql} {
|
||||
do_test e_fkey-24.$tn "
|
||||
catchsql {$sql}
|
||||
" [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
|
||||
" [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
|
||||
}
|
||||
foreach {tn c} [list 2 c1 3 c2 4 c3] {
|
||||
test_efkey_61 $tn.1 1 "INSERT INTO $c VALUES(1, 2)"
|
||||
@ -998,7 +998,7 @@ do_test e_fkey-25.5 {
|
||||
concat \
|
||||
[execsql { SELECT rowid FROM track WHERE trackartist = 5 }] \
|
||||
[catchsql { DELETE FROM artist WHERE artistid = 5 }]
|
||||
} {1 1 {foreign key constraint failed}}
|
||||
} {1 1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test e_fkey-25.6 {
|
||||
concat \
|
||||
@ -1010,7 +1010,7 @@ do_test e_fkey-25.7 {
|
||||
concat \
|
||||
[execsql { SELECT rowid FROM track WHERE trackartist = 6 }] \
|
||||
[catchsql { DELETE FROM artist WHERE artistid = 6 }]
|
||||
} {2 1 {foreign key constraint failed}}
|
||||
} {2 1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-47936-10044 Or, more generally:
|
||||
@ -1199,7 +1199,7 @@ do_test e_fkey-29.3 {
|
||||
catchsql {
|
||||
INSERT INTO song VALUES(2, 'Elvis Presley', 'Elvis Is Back!', 'Fever');
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -1240,7 +1240,7 @@ do_test e_fkey-31.1 {
|
||||
do_test e_fkey-31.2 {
|
||||
# Execute a statement that violates the immediate FK constraint.
|
||||
catchsql { INSERT INTO prince VALUES(1, 2) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test e_fkey-31.3 {
|
||||
# This time, use a trigger to fix the constraint violation before the
|
||||
@ -1265,7 +1265,7 @@ do_test e_fkey-31.4 {
|
||||
DROP TRIGGER kt;
|
||||
}
|
||||
catchsql { INSERT INTO prince VALUES(3, 4) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-31.5 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@ -1296,7 +1296,7 @@ do_test e_fkey-31.5 {
|
||||
proc test_efkey_34 {tn isError sql} {
|
||||
do_test e_fkey-32.$tn "
|
||||
catchsql {$sql}
|
||||
" [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
|
||||
" [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
|
||||
}
|
||||
drop_all_tables
|
||||
|
||||
@ -1327,7 +1327,7 @@ drop_all_tables
|
||||
proc test_efkey_35 {tn isError sql} {
|
||||
do_test e_fkey-33.$tn "
|
||||
catchsql {$sql}
|
||||
" [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
|
||||
" [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
|
||||
}
|
||||
do_test e_fkey-33.1 {
|
||||
execsql {
|
||||
@ -1417,7 +1417,7 @@ do_test e_fkey-34.1 {
|
||||
|
||||
proc test_efkey_29 {tn sql isError} {
|
||||
do_test e_fkey-34.$tn "catchsql {$sql}" [
|
||||
lindex {{0 {}} {1 {foreign key constraint failed}}} $isError
|
||||
lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError
|
||||
]
|
||||
}
|
||||
test_efkey_29 2 "BEGIN" 0
|
||||
@ -1491,7 +1491,7 @@ do_test e_fkey-35.2 {
|
||||
INSERT INTO track VALUES(1, 'White Christmas', 5);
|
||||
}
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-35.3 {
|
||||
execsql {
|
||||
INSERT INTO artist VALUES(5, 'Bing Crosby');
|
||||
@ -1528,7 +1528,7 @@ do_test e_fkey-36.2 {
|
||||
} {}
|
||||
do_test e_fkey-36.3 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-36.4 {
|
||||
execsql {
|
||||
UPDATE t1 SET a = 5 WHERE a = 4;
|
||||
@ -1558,7 +1558,7 @@ do_test e_fkey-37.1 {
|
||||
} {}
|
||||
do_test e_fkey-37.2 {
|
||||
catchsql {RELEASE one}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-37.3 {
|
||||
execsql {
|
||||
UPDATE t1 SET a = 7 WHERE a = 6;
|
||||
@ -1575,7 +1575,7 @@ do_test e_fkey-37.4 {
|
||||
} {}
|
||||
do_test e_fkey-37.5 {
|
||||
catchsql {RELEASE one}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-37.6 {
|
||||
execsql {ROLLBACK TO one ; RELEASE one}
|
||||
} {}
|
||||
@ -1606,7 +1606,7 @@ do_test e_fkey-38.2 {
|
||||
} {1 1 2 2 3 3 4 4 5 6}
|
||||
do_test e_fkey-38.3 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-38.4 {
|
||||
execsql {
|
||||
ROLLBACK TO one;
|
||||
@ -1627,11 +1627,11 @@ do_test e_fkey-38.5 {
|
||||
} {}
|
||||
do_test e_fkey-38.6 {
|
||||
catchsql {RELEASE a}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-38.7 {
|
||||
execsql {ROLLBACK TO c}
|
||||
catchsql {RELEASE a}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-38.8 {
|
||||
execsql {
|
||||
ROLLBACK TO b;
|
||||
@ -1782,7 +1782,7 @@ do_test e_fkey-41.2 {
|
||||
} {j k l m}
|
||||
do_test e_fkey-41.3 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-41.4 {
|
||||
execsql ROLLBACK
|
||||
} {}
|
||||
@ -1820,10 +1820,10 @@ do_test e_fkey-41.2 {
|
||||
} {}
|
||||
do_test e_fkey-41.3 {
|
||||
catchsql { DELETE FROM parent WHERE p1 = 'a' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-41.4 {
|
||||
catchsql { UPDATE parent SET p2 = 'e' WHERE p1 = 'c' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that RESTRICT is slightly different from NO ACTION for IMMEDIATE
|
||||
@ -1857,7 +1857,7 @@ do_test e_fkey-42.1 {
|
||||
} {}
|
||||
do_test e_fkey-42.2 {
|
||||
catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-42.3 {
|
||||
execsql {
|
||||
UPDATE parent SET x = 'key two' WHERE x = 'key2';
|
||||
@ -1885,7 +1885,7 @@ do_test e_fkey-42.4 {
|
||||
} {}
|
||||
do_test e_fkey-42.5 {
|
||||
catchsql { DELETE FROM parent WHERE x = 'key1' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-42.6 {
|
||||
execsql {
|
||||
DELETE FROM parent WHERE x = 'key2';
|
||||
@ -1908,7 +1908,7 @@ do_test e_fkey-42.7 {
|
||||
} {}
|
||||
do_test e_fkey-42.8 {
|
||||
catchsql { REPLACE INTO parent VALUES('key1') }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-42.9 {
|
||||
execsql {
|
||||
REPLACE INTO parent VALUES('key2');
|
||||
@ -1944,13 +1944,13 @@ do_test e_fkey-43.1 {
|
||||
} {}
|
||||
do_test e_fkey-43.2 {
|
||||
catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-43.3 {
|
||||
execsql { UPDATE parent SET x = 'key two' WHERE x = 'key2' }
|
||||
} {}
|
||||
do_test e_fkey-43.4 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-43.5 {
|
||||
execsql {
|
||||
UPDATE child2 SET c = 'key two';
|
||||
@ -1978,13 +1978,13 @@ do_test e_fkey-43.6 {
|
||||
} {}
|
||||
do_test e_fkey-43.7 {
|
||||
catchsql { DELETE FROM parent WHERE x = 'key1' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-43.8 {
|
||||
execsql { DELETE FROM parent WHERE x = 'key2' }
|
||||
} {}
|
||||
do_test e_fkey-43.9 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-43.10 {
|
||||
execsql {
|
||||
UPDATE child2 SET c = NULL;
|
||||
@ -2240,7 +2240,7 @@ do_test e_fkey-49.3 {
|
||||
} {ONE two three}
|
||||
do_test e_fkey-49.4 {
|
||||
catchsql { UPDATE parent SET a = '' WHERE a = 'oNe' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -2275,7 +2275,7 @@ do_test e_fkey-50.1 {
|
||||
} {}
|
||||
do_test e_fkey-50.2 {
|
||||
catchsql { DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-50.3 {
|
||||
execsql {
|
||||
INSERT INTO artist VALUES(0, 'Unknown Artist');
|
||||
@ -2639,7 +2639,7 @@ do_test e_fkey-58.1 {
|
||||
}
|
||||
execsql { INSERT INTO c5 VALUES('a', 'b') }
|
||||
catchsql { DROP TABLE p }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-58.2 {
|
||||
execsql { SELECT * FROM p }
|
||||
} {a b}
|
||||
@ -2648,7 +2648,7 @@ do_test e_fkey-58.3 {
|
||||
BEGIN;
|
||||
DROP TABLE p;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-58.4 {
|
||||
execsql {
|
||||
SELECT * FROM p;
|
||||
@ -2682,11 +2682,11 @@ do_test e_fkey-59.2 {
|
||||
} {}
|
||||
do_test e_fkey-59.3 {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-59.4 {
|
||||
execsql { CREATE TABLE p(a, b, PRIMARY KEY(a, b)) }
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-59.5 {
|
||||
execsql { INSERT INTO p VALUES('a', 'b') }
|
||||
execsql COMMIT
|
||||
@ -2849,7 +2849,7 @@ foreach zMatch [list SIMPLE PARTIAL FULL Simple parTIAL FuLL ] {
|
||||
# Check that the FK is enforced properly if there are no NULL values
|
||||
# in the child key columns.
|
||||
catchsql { INSERT INTO c VALUES('a', 2, 4) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -2879,13 +2879,13 @@ do_test e_fkey-62.3 {
|
||||
} {}
|
||||
do_test e_fkey-62.4 {
|
||||
catchsql { INSERT INTO ci VALUES('x', 'y') }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-62.5 {
|
||||
catchsql { INSERT INTO cd VALUES('x', 'y') }
|
||||
} {0 {}}
|
||||
do_test e_fkey-62.6 {
|
||||
catchsql { COMMIT }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test e_fkey-62.7 {
|
||||
execsql {
|
||||
DELETE FROM cd;
|
||||
|
@ -371,22 +371,22 @@ do_execsql_test e_insert-4.1.0 {
|
||||
INSERT INTO a4 VALUES(3, 'a');
|
||||
} {}
|
||||
foreach {tn sql error ac data } {
|
||||
1.1 "INSERT INTO a4 VALUES(2,'b')" {column c is not unique} 1 {1 a 2 a 3 a}
|
||||
1.1 "INSERT INTO a4 VALUES(2,'b')" {UNIQUE constraint failed: a4.c} 1 {1 a 2 a 3 a}
|
||||
1.2 "INSERT OR REPLACE INTO a4 VALUES(2, 'b')" {} 1 {1 a 3 a 2 b}
|
||||
1.3 "INSERT OR IGNORE INTO a4 VALUES(3, 'c')" {} 1 {1 a 3 a 2 b}
|
||||
1.4 "BEGIN" {} 0 {1 a 3 a 2 b}
|
||||
1.5 "INSERT INTO a4 VALUES(1, 'd')" {column c is not unique} 0 {1 a 3 a 2 b}
|
||||
1.5 "INSERT INTO a4 VALUES(1, 'd')" {UNIQUE constraint failed: a4.c} 0 {1 a 3 a 2 b}
|
||||
1.6 "INSERT OR ABORT INTO a4 VALUES(1, 'd')"
|
||||
{column c is not unique} 0 {1 a 3 a 2 b}
|
||||
{UNIQUE constraint failed: a4.c} 0 {1 a 3 a 2 b}
|
||||
1.7 "INSERT OR ROLLBACK INTO a4 VALUES(1, 'd')"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b}
|
||||
{UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b}
|
||||
1.8 "INSERT INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b}
|
||||
{UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b}
|
||||
1.9 "INSERT OR FAIL INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b 4 e}
|
||||
{UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b 4 e}
|
||||
|
||||
2.1 "INSERT INTO a4 VALUES(2,'f')"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b 4 e}
|
||||
{UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b 4 e}
|
||||
2.2 "REPLACE INTO a4 VALUES(2, 'f')" {} 1 {1 a 3 a 4 e 2 f}
|
||||
} {
|
||||
do_catchsql_test e_insert-4.1.$tn.1 $sql [list [expr {$error!=""}] $error]
|
||||
|
@ -67,10 +67,10 @@ sqlite3 db test.db
|
||||
do_execsql_test e_reindex-1.3 {
|
||||
PRAGMA integrity_check;
|
||||
} [list \
|
||||
{rowid 4 missing from index i2} \
|
||||
{rowid 4 missing from index i1} \
|
||||
{rowid 5 missing from index i2} \
|
||||
{rowid 5 missing from index i1} \
|
||||
{row 3 missing from index i2} \
|
||||
{row 3 missing from index i1} \
|
||||
{row 4 missing from index i2} \
|
||||
{row 4 missing from index i1} \
|
||||
{wrong # of entries in index i2} \
|
||||
{wrong # of entries in index i1}
|
||||
]
|
||||
|
@ -278,30 +278,30 @@ do_execsql_test e_update-1.8.0 {
|
||||
} {}
|
||||
foreach {tn sql error ac data } {
|
||||
1 "UPDATE t3 SET b='one' WHERE a=3"
|
||||
{column b is not unique} 1 {1 one 2 two 3 three 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 1 {1 one 2 two 3 three 4 four}
|
||||
|
||||
2 "UPDATE OR REPLACE t3 SET b='one' WHERE a=3"
|
||||
{} 1 {2 two 3 one 4 four}
|
||||
|
||||
3 "UPDATE OR FAIL t3 SET b='three'"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
|
||||
|
||||
4 "UPDATE OR IGNORE t3 SET b='three' WHERE a=3"
|
||||
{} 1 {2 three 3 one 4 four}
|
||||
|
||||
5 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
|
||||
|
||||
6 "BEGIN" {} 0 {2 three 3 one 4 four}
|
||||
|
||||
7 "UPDATE t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 0 {2 three 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 0 {2 three 3 one 4 four}
|
||||
|
||||
8 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 0 {2 three 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 0 {2 three 3 one 4 four}
|
||||
|
||||
9 "UPDATE OR FAIL t3 SET b='two'"
|
||||
{column b is not unique} 0 {2 two 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 0 {2 two 3 one 4 four}
|
||||
|
||||
10 "UPDATE OR IGNORE t3 SET b='four' WHERE a=3"
|
||||
{} 0 {2 two 3 one 4 four}
|
||||
@ -310,7 +310,7 @@ foreach {tn sql error ac data } {
|
||||
{} 0 {2 two 3 four}
|
||||
|
||||
12 "UPDATE OR ROLLBACK t3 SET b='four'"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
{UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
|
||||
} {
|
||||
do_catchsql_test e_update-1.8.$tn.1 $sql [list [expr {$error!=""}] $error]
|
||||
do_execsql_test e_update-1.8.$tn.2 {SELECT * FROM t3} [list {*}$data]
|
||||
|
@ -78,14 +78,14 @@ do_test 2.2 {
|
||||
error_messages "INSERT INTO t1 VALUES('ghi', 'def')"
|
||||
} [list {*}{
|
||||
SQLITE_ERROR {SQL logic error or missing database}
|
||||
SQLITE_CONSTRAINT {column b is not unique}
|
||||
SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
|
||||
}]
|
||||
verify_ex_errcode 2.2b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test 2.3 {
|
||||
error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')"
|
||||
} [list {*}{
|
||||
SQLITE_CONSTRAINT {column b is not unique}
|
||||
SQLITE_CONSTRAINT {column b is not unique}
|
||||
SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
|
||||
SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
|
||||
}]
|
||||
verify_ex_errcode 2.3b SQLITE_CONSTRAINT_UNIQUE
|
||||
|
||||
|
150
test/fkey2.test
150
test/fkey2.test
@ -104,38 +104,38 @@ set FkeySimpleSchema {
|
||||
|
||||
|
||||
set FkeySimpleTests {
|
||||
1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {foreign key constraint failed}}
|
||||
1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
|
||||
1.2 "INSERT INTO t1 VALUES(1, 2)" {0 {}}
|
||||
1.3 "INSERT INTO t2 VALUES(1, 3)" {0 {}}
|
||||
1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {foreign key constraint failed}}
|
||||
1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
|
||||
1.5 "INSERT INTO t2 VALUES(NULL, 4)" {0 {}}
|
||||
1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {foreign key constraint failed}}
|
||||
1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
|
||||
1.7 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
|
||||
1.9 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
|
||||
1.10 "UPDATE t2 SET c=NULL WHERE d=4" {0 {}}
|
||||
1.11 "DELETE FROM t1 WHERE a=1" {1 {foreign key constraint failed}}
|
||||
1.12 "UPDATE t1 SET a = 2" {1 {foreign key constraint failed}}
|
||||
1.11 "DELETE FROM t1 WHERE a=1" {1 {FOREIGN KEY constraint failed}}
|
||||
1.12 "UPDATE t1 SET a = 2" {1 {FOREIGN KEY constraint failed}}
|
||||
1.13 "UPDATE t1 SET a = 1" {0 {}}
|
||||
|
||||
2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {foreign key constraint failed}}
|
||||
2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
|
||||
2.2 "INSERT INTO t3 VALUES(1, 2)" {0 {}}
|
||||
2.3 "INSERT INTO t4 VALUES(1, 3)" {0 {}}
|
||||
|
||||
4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {foreign key constraint failed}}
|
||||
4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
|
||||
4.2 "INSERT INTO t7 VALUES(2, 1)" {0 {}}
|
||||
4.3 "INSERT INTO t8 VALUES(1, 3)" {0 {}}
|
||||
4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {foreign key constraint failed}}
|
||||
4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
|
||||
4.5 "INSERT INTO t8 VALUES(NULL, 4)" {0 {}}
|
||||
4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {foreign key constraint failed}}
|
||||
4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
|
||||
4.7 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
|
||||
4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
|
||||
4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}}
|
||||
4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}}
|
||||
4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}}
|
||||
4.11 "DELETE FROM t7 WHERE b=1" {1 {FOREIGN KEY constraint failed}}
|
||||
4.12 "UPDATE t7 SET b = 2" {1 {FOREIGN KEY constraint failed}}
|
||||
4.13 "UPDATE t7 SET b = 1" {0 {}}
|
||||
4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}}
|
||||
4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}}
|
||||
4.16 "UPDATE t7 SET rowid = 5" {1 {foreign key constraint failed}}
|
||||
4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {FOREIGN KEY constraint failed}}
|
||||
4.15 "UPDATE t7 SET b = 5" {1 {FOREIGN KEY constraint failed}}
|
||||
4.16 "UPDATE t7 SET rowid = 5" {1 {FOREIGN KEY constraint failed}}
|
||||
4.17 "UPDATE t7 SET a = 10" {0 {}}
|
||||
|
||||
5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}}
|
||||
@ -215,7 +215,7 @@ do_test fkey2-1.5.1 {
|
||||
} {35.0 text}
|
||||
do_test fkey2-1.5.2 {
|
||||
catchsql { DELETE FROM i }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
# Same test using a regular primary key with integer affinity.
|
||||
drop_all_tables
|
||||
@ -231,7 +231,7 @@ do_test fkey2-1.6.1 {
|
||||
} {35.0 text 35 integer}
|
||||
do_test fkey2-1.6.2 {
|
||||
catchsql { DELETE FROM i }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
# Use a collation sequence on the parent key.
|
||||
drop_all_tables
|
||||
@ -243,7 +243,7 @@ do_test fkey2-1.7.1 {
|
||||
INSERT INTO j VALUES('sqlite');
|
||||
}
|
||||
catchsql { DELETE FROM i }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
# Use the parent key collation even if it is default and the child key
|
||||
# has an explicit value.
|
||||
@ -255,7 +255,7 @@ do_test fkey2-1.7.2 {
|
||||
INSERT INTO i VALUES('SQLite');
|
||||
}
|
||||
catchsql { INSERT INTO j VALUES('sqlite') }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-1.7.3 {
|
||||
execsql {
|
||||
INSERT INTO i VALUES('sqlite');
|
||||
@ -263,7 +263,7 @@ do_test fkey2-1.7.3 {
|
||||
DELETE FROM i WHERE i = 'SQLite';
|
||||
}
|
||||
catchsql { DELETE FROM i WHERE i = 'sqlite' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This section (test cases fkey2-2.*) contains tests to check that the
|
||||
@ -271,7 +271,7 @@ do_test fkey2-1.7.3 {
|
||||
#
|
||||
proc fkey2-2-test {tn nocommit sql {res {}}} {
|
||||
if {$res eq "FKV"} {
|
||||
set expected {1 {foreign key constraint failed}}
|
||||
set expected {1 {FOREIGN KEY constraint failed}}
|
||||
} else {
|
||||
set expected [list 0 $res]
|
||||
}
|
||||
@ -279,7 +279,7 @@ proc fkey2-2-test {tn nocommit sql {res {}}} {
|
||||
if {$nocommit} {
|
||||
do_test fkey2-2.${tn}c {
|
||||
catchsql COMMIT
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,7 +375,7 @@ fkey2-2-test 65 1 "INSERT INTO leaf VALUES('b', 2)"
|
||||
fkey2-2-test 66 1 "INSERT INTO leaf VALUES('c', 1)"
|
||||
do_test fkey2-2-test-67 {
|
||||
catchsql "INSERT INTO node SELECT parent, 3 FROM leaf"
|
||||
} {1 {column nodeid is not unique}}
|
||||
} {1 {UNIQUE constraint failed: node.nodeid}}
|
||||
fkey2-2-test 68 0 "COMMIT" FKV
|
||||
fkey2-2-test 69 1 "INSERT INTO node VALUES(1, NULL)"
|
||||
fkey2-2-test 70 0 "INSERT INTO node VALUES(2, NULL)"
|
||||
@ -417,14 +417,14 @@ do_test fkey2-3.1.2 {
|
||||
} {}
|
||||
do_test fkey2-3.1.3 {
|
||||
catchsql { UPDATE ab SET a = 5 }
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: ef}}
|
||||
do_test fkey2-3.1.4 {
|
||||
execsql { SELECT * FROM ab }
|
||||
} {1 b}
|
||||
do_test fkey2-3.1.4 {
|
||||
execsql BEGIN;
|
||||
catchsql { UPDATE ab SET a = 5 }
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: ef}}
|
||||
do_test fkey2-3.1.5 {
|
||||
execsql COMMIT;
|
||||
execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
|
||||
@ -433,7 +433,7 @@ do_test fkey2-3.1.5 {
|
||||
do_test fkey2-3.2.1 {
|
||||
execsql BEGIN;
|
||||
catchsql { DELETE FROM ab }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-3.2.2 {
|
||||
execsql COMMIT
|
||||
execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
|
||||
@ -555,7 +555,7 @@ do_test fkey2-7.1 {
|
||||
} {}
|
||||
do_test fkey2-7.2 {
|
||||
catchsql { INSERT INTO t2 VALUES(1, 'A'); }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-7.3 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
@ -568,19 +568,19 @@ do_test fkey2-7.4 {
|
||||
} {}
|
||||
do_test fkey2-7.5 {
|
||||
catchsql { UPDATE t2 SET c = 3 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-7.6 {
|
||||
catchsql { DELETE FROM t1 WHERE a = 2 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-7.7 {
|
||||
execsql { DELETE FROM t1 WHERE a = 1 }
|
||||
} {}
|
||||
do_test fkey2-7.8 {
|
||||
catchsql { UPDATE t1 SET a = 3 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-7.9 {
|
||||
catchsql { UPDATE t2 SET rowid = 3 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that it is not possible to enable/disable FK support while a
|
||||
@ -645,7 +645,7 @@ do_test fkey2-9.1.4 {
|
||||
} {2 two}
|
||||
do_test fkey2-9.1.5 {
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey2-9.2.1 {
|
||||
execsql {
|
||||
@ -780,13 +780,13 @@ do_test fkey2-12.1.3 {
|
||||
} {}
|
||||
do_test fkey2-12.1.4 {
|
||||
catchsql "UPDATE t1 SET b = 'five' WHERE b = 'two'"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-12.1.5 {
|
||||
execsql "DELETE FROM t1 WHERE b = 'two'"
|
||||
} {}
|
||||
do_test fkey2-12.1.6 {
|
||||
catchsql "COMMIT"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-12.1.7 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
@ -828,7 +828,7 @@ do_test fkey2-12.2.3 {
|
||||
INSERT INTO t2 VALUES('b');
|
||||
}
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-12.2.4 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -866,14 +866,14 @@ do_test fkey2-12.3.2 {
|
||||
} {no possibly}
|
||||
do_test fkey2-12.3.3 {
|
||||
catchsql { INSERT INTO down(c39, c38) VALUES('yes', 'no') }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-12.3.4 {
|
||||
execsql {
|
||||
INSERT INTO up(c34, c35) VALUES('yes', 'no');
|
||||
INSERT INTO down(c39, c38) VALUES('yes', 'no');
|
||||
}
|
||||
catchsql { DELETE FROM up WHERE c34 = 'yes' }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-12.3.5 {
|
||||
execsql {
|
||||
DELETE FROM up WHERE c34 = 'possibly';
|
||||
@ -901,7 +901,7 @@ foreach {tn stmt} {
|
||||
} {
|
||||
do_test fkey2-13.1.$tn.1 {
|
||||
catchsql $stmt
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-13.1.$tn.2 {
|
||||
execsql {
|
||||
SELECT * FROM pp;
|
||||
@ -911,7 +911,7 @@ foreach {tn stmt} {
|
||||
do_test fkey2-13.1.$tn.3 {
|
||||
execsql BEGIN;
|
||||
catchsql $stmt
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-13.1.$tn.4 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@ -1015,13 +1015,13 @@ ifcapable altertable {
|
||||
]
|
||||
do_test fkey2-14.2.2.3 {
|
||||
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2.2.4 {
|
||||
execsql { INSERT INTO t4 VALUES(1, NULL) }
|
||||
} {}
|
||||
do_test fkey2-14.2.2.5 {
|
||||
catchsql { UPDATE t4 SET b = 5 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2.2.6 {
|
||||
catchsql { UPDATE t4 SET b = 1 }
|
||||
} {0 {}}
|
||||
@ -1096,13 +1096,13 @@ ifcapable altertable {
|
||||
]
|
||||
do_test fkey2-14.2tmp.2.3 {
|
||||
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2tmp.2.4 {
|
||||
execsql { INSERT INTO t4 VALUES(1, NULL) }
|
||||
} {}
|
||||
do_test fkey2-14.2tmp.2.5 {
|
||||
catchsql { UPDATE t4 SET b = 5 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2tmp.2.6 {
|
||||
catchsql { UPDATE t4 SET b = 1 }
|
||||
} {0 {}}
|
||||
@ -1178,13 +1178,13 @@ ifcapable altertable {
|
||||
]
|
||||
do_test fkey2-14.2aux.2.3 {
|
||||
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2aux.2.4 {
|
||||
execsql { INSERT INTO t4 VALUES(1, NULL) }
|
||||
} {}
|
||||
do_test fkey2-14.2aux.2.5 {
|
||||
catchsql { UPDATE t4 SET b = 5 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-14.2aux.2.6 {
|
||||
catchsql { UPDATE t4 SET b = 1 }
|
||||
} {0 {}}
|
||||
@ -1210,7 +1210,7 @@ do_test fkey-2.14.3.2 {
|
||||
} {}
|
||||
do_test fkey-2.14.3.3 {
|
||||
catchsql { DROP TABLE t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey-2.14.3.4 {
|
||||
execsql {
|
||||
DELETE FROM t2;
|
||||
@ -1229,7 +1229,7 @@ do_test fkey-2.14.3.5 {
|
||||
} {}
|
||||
do_test fkey-2.14.3.6 {
|
||||
catchsql { DROP TABLE t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey-2.14.3.7 {
|
||||
execsql {
|
||||
DROP TABLE t2;
|
||||
@ -1387,15 +1387,15 @@ foreach {tn zSchema} {
|
||||
|
||||
do_test fkey2-16.1.$tn.3 {
|
||||
catchsql { UPDATE self SET b = 15 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey2-16.1.$tn.4 {
|
||||
catchsql { UPDATE self SET a = 15 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey2-16.1.$tn.5 {
|
||||
catchsql { UPDATE self SET a = 15, b = 16 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey2-16.1.$tn.6 {
|
||||
catchsql { UPDATE self SET a = 17, b = 17 }
|
||||
@ -1406,7 +1406,7 @@ foreach {tn zSchema} {
|
||||
} {}
|
||||
do_test fkey2-16.1.$tn.8 {
|
||||
catchsql { INSERT INTO self VALUES(20, 21) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -1463,7 +1463,7 @@ do_test fkey2-17.1.6 {
|
||||
INSERT INTO one VALUES(0, 0, 0);
|
||||
UPDATE two SET e=e+1, f=f+1;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-17.1.7 {
|
||||
execsql { SELECT * FROM one }
|
||||
} {1 2 3 2 3 4 3 4 5 0 0 0}
|
||||
@ -1619,7 +1619,7 @@ ifcapable auth {
|
||||
}
|
||||
do_test fkey2-18.8 {
|
||||
catchsql { INSERT INTO short VALUES(1, 3, 2) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-18.9 {
|
||||
execsql { INSERT INTO short VALUES(1, 3, NULL) }
|
||||
} {}
|
||||
@ -1628,7 +1628,7 @@ ifcapable auth {
|
||||
} {1 3 2 1 3 {}}
|
||||
do_test fkey2-18.11 {
|
||||
catchsql { UPDATE short SET f = 2 WHERE f IS NULL }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
db auth {}
|
||||
unset authargs
|
||||
@ -1680,7 +1680,7 @@ foreach {tn insert} {
|
||||
} {
|
||||
do_test fkey2-20.2.$tn.1 {
|
||||
catchsql "$insert INTO cc VALUES(1, 2)"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.2.$tn.2 {
|
||||
execsql { SELECT * FROM cc }
|
||||
} {}
|
||||
@ -1691,7 +1691,7 @@ foreach {tn insert} {
|
||||
INSERT INTO cc VALUES(1, 2);
|
||||
}
|
||||
catchsql "$insert INTO cc VALUES(3, 4)"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.2.$tn.4 {
|
||||
execsql { COMMIT ; SELECT * FROM cc }
|
||||
} {1 2}
|
||||
@ -1716,13 +1716,13 @@ foreach {tn update} {
|
||||
} {}
|
||||
do_test fkey2-20.3.$tn.2 {
|
||||
catchsql "$update pp SET a = 1"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.3.$tn.3 {
|
||||
execsql { SELECT * FROM pp }
|
||||
} {2 two}
|
||||
do_test fkey2-20.3.$tn.4 {
|
||||
catchsql "$update cc SET d = 1"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.3.$tn.5 {
|
||||
execsql { SELECT * FROM cc }
|
||||
} {1 2}
|
||||
@ -1732,7 +1732,7 @@ foreach {tn update} {
|
||||
INSERT INTO pp VALUES(3, 'three');
|
||||
}
|
||||
catchsql "$update pp SET a = 1 WHERE a = 2"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.3.$tn.7 {
|
||||
execsql { COMMIT ; SELECT * FROM pp }
|
||||
} {2 two 3 three}
|
||||
@ -1742,7 +1742,7 @@ foreach {tn update} {
|
||||
INSERT INTO cc VALUES(2, 2);
|
||||
}
|
||||
catchsql "$update cc SET d = 1 WHERE c = 1"
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-20.3.$tn.9 {
|
||||
execsql { COMMIT ; SELECT * FROM cc }
|
||||
} {1 2 2 2}
|
||||
@ -1768,7 +1768,7 @@ do_test fkey2-genfkey.1.1 {
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.2 {
|
||||
catchsql { INSERT INTO t2 VALUES(1, 2) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.3 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
@ -1780,7 +1780,7 @@ do_test fkey2-genfkey.1.4 {
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.5 {
|
||||
catchsql { UPDATE t2 SET e = 5 WHERE e IS NULL }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.6 {
|
||||
execsql { UPDATE t2 SET e = 1 WHERE e IS NULL }
|
||||
} {}
|
||||
@ -1789,13 +1789,13 @@ do_test fkey2-genfkey.1.7 {
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.8 {
|
||||
catchsql { UPDATE t1 SET a = 10 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.9 {
|
||||
catchsql { UPDATE t1 SET a = NULL }
|
||||
} {1 {datatype mismatch}}
|
||||
do_test fkey2-genfkey.1.10 {
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.11 {
|
||||
execsql { UPDATE t2 SET e = NULL }
|
||||
} {}
|
||||
@ -1815,7 +1815,7 @@ do_test fkey2-genfkey.1.13 {
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.14 {
|
||||
catchsql { INSERT INTO t3 VALUES(3, 1, 4) }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.15 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1, 1, 4);
|
||||
@ -1824,16 +1824,16 @@ do_test fkey2-genfkey.1.15 {
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.16 {
|
||||
catchsql { DELETE FROM t1 }
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.17 {
|
||||
catchsql { UPDATE t1 SET b = 10}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-genfkey.1.18 {
|
||||
execsql { UPDATE t1 SET a = 10}
|
||||
} {}
|
||||
do_test fkey2-genfkey.1.19 {
|
||||
catchsql { UPDATE t3 SET h = 'hello' WHERE i = 3}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
drop_all_tables
|
||||
do_test fkey2-genfkey.2.1 {
|
||||
@ -1946,7 +1946,7 @@ do_test fkey2-dd08e5.1.2 {
|
||||
catchsql {
|
||||
DELETE FROM tdd08;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-dd08e5.1.3 {
|
||||
execsql {
|
||||
SELECT * FROM tdd08;
|
||||
@ -1956,17 +1956,17 @@ do_test fkey2-dd08e5.1.4 {
|
||||
catchsql {
|
||||
INSERT INTO tdd08_b VALUES(400,500,300);
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-dd08e5.1.5 {
|
||||
catchsql {
|
||||
UPDATE tdd08_b SET x=x+1;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-dd08e5.1.6 {
|
||||
catchsql {
|
||||
UPDATE tdd08 SET a=a+1;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Verify that ticket ce7c133ea6cc9ccdc1a60d80441f80b6180f5eba
|
||||
@ -1987,12 +1987,12 @@ do_test fkey2-ce7c13.1.2 {
|
||||
catchsql {
|
||||
UPDATE tce71 set b = 201 where a = 100;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-ce7c13.1.3 {
|
||||
catchsql {
|
||||
UPDATE tce71 set a = 101 where a = 100;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-ce7c13.1.4 {
|
||||
execsql {
|
||||
CREATE TABLE tce73(a INTEGER PRIMARY KEY, b, UNIQUE(a,b));
|
||||
@ -2007,11 +2007,11 @@ do_test fkey2-ce7c13.1.5 {
|
||||
catchsql {
|
||||
UPDATE tce73 set b = 201 where a = 100;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey2-ce7c13.1.6 {
|
||||
catchsql {
|
||||
UPDATE tce73 set a = 101 where a = 100;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
finish_test
|
||||
|
@ -43,13 +43,13 @@ do_test fkey3-1.2 {
|
||||
catchsql {
|
||||
DELETE FROM t1 WHERE x=100;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey3-1.3 {
|
||||
catchsql {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_test fkey3-1.4 {
|
||||
execsql {
|
||||
@ -95,17 +95,17 @@ do_execsql_test 3.1.1 {
|
||||
} {}
|
||||
do_catchsql_test 3.1.2 {
|
||||
INSERT INTO t3 VALUES(NULL, 2, 5, 2);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_catchsql_test 3.1.3 {
|
||||
INSERT INTO t3 VALUES(NULL, 3, 5, 2);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
CREATE TABLE t4(a UNIQUE, b REFERENCES t4(a));
|
||||
}
|
||||
do_catchsql_test 3.2.2 {
|
||||
INSERT INTO t4 VALUES(NULL, 1);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_execsql_test 3.3.1 {
|
||||
CREATE TABLE t5(a INTEGER PRIMARY KEY, b REFERENCES t5(a));
|
||||
@ -113,7 +113,7 @@ do_execsql_test 3.3.1 {
|
||||
} {}
|
||||
do_catchsql_test 3.3.2 {
|
||||
INSERT INTO t5 VALUES(NULL, 3);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_execsql_test 3.4.1 {
|
||||
CREATE TABLE t6(a INTEGER PRIMARY KEY, b, c, d,
|
||||
@ -127,7 +127,7 @@ do_execsql_test 3.4.4 { INSERT INTO t6 VALUES(NULL, 'a', 1, 'a'); } {}
|
||||
do_execsql_test 3.4.5 { INSERT INTO t6 VALUES(5, 'a', 2, 'a'); } {}
|
||||
do_catchsql_test 3.4.6 {
|
||||
INSERT INTO t6 VALUES(NULL, 'a', 65, 'a');
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
do_execsql_test 3.4.7 {
|
||||
INSERT INTO t6 VALUES(100, 'one', 100, 'one');
|
||||
@ -149,10 +149,10 @@ do_execsql_test 3.5.2 { INSERT INTO t7 VALUES('x', 1, 'x', NULL) } {}
|
||||
do_execsql_test 3.5.3 { INSERT INTO t7 VALUES('x', 2, 'x', 2) } {}
|
||||
do_catchsql_test 3.5.4 {
|
||||
INSERT INTO t7 VALUES('x', 450, 'x', NULL);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_catchsql_test 3.5.5 {
|
||||
INSERT INTO t7 VALUES('x', 450, 'x', 451);
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
|
||||
do_execsql_test 3.6.1 {
|
||||
@ -163,7 +163,7 @@ do_execsql_test 3.6.1 {
|
||||
}
|
||||
do_catchsql_test 3.6.2 {
|
||||
UPDATE t8 SET d = 2;
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_execsql_test 3.6.3 { UPDATE t8 SET d = 1; }
|
||||
do_execsql_test 3.6.4 { UPDATE t8 SET e = 2; }
|
||||
|
||||
@ -181,6 +181,6 @@ do_catchsql_test 3.6.5 {
|
||||
INSERT INTO TestTable VALUES (1, 'parent', 1, null);
|
||||
INSERT INTO TestTable VALUES (2, 'child', 1, 1);
|
||||
UPDATE TestTable SET parent_id=1000 where id=2;
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
|
||||
finish_test
|
||||
|
@ -47,7 +47,7 @@ do_execsql_test fkey6-1.1 {
|
||||
} {}
|
||||
do_test fkey6-1.2 {
|
||||
catchsql {DELETE FROM t1 WHERE x=2;}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test fkey6-1.3 {
|
||||
sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
|
||||
} {0 0 0}
|
||||
@ -99,7 +99,7 @@ do_execsql_test fkey6-1.10.1 {
|
||||
} {1 0 1 0}
|
||||
do_test fkey6-1.10.2 {
|
||||
catchsql {DELETE FROM t1 WHERE x=3}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
db eval {ROLLBACK}
|
||||
|
||||
do_test fkey6-1.20 {
|
||||
@ -173,4 +173,3 @@ do_execsql_test fkey6-2.6 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -381,32 +381,32 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (NULL);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.3 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (NULL);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.4 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.5 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.6 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('1234bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.7 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('1234.56bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.8 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (1234);
|
||||
@ -416,7 +416,7 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (1234.56);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.10 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('1234');
|
||||
@ -426,32 +426,32 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('1234.56');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.12 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (ZEROBLOB(4));
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.13 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (X'');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.14 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (X'1234');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.15 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (X'12345678');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.16 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('1234.00');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
do_test func4-3.17 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (1234.00);
|
||||
@ -461,13 +461,13 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES ('-9223372036854775809');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
if {$highPrecision(1)} {
|
||||
do_test func4-3.19 {
|
||||
catchsql {
|
||||
INSERT INTO t1 (x) VALUES (9223372036854775808);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
}
|
||||
do_execsql_test func4-3.20 {
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
@ -483,32 +483,32 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (NULL);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.3 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (NULL);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.4 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES ('');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.5 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES ('bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.6 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES ('1234bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.7 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES ('1234.56bad');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.8 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (1234);
|
||||
@ -533,22 +533,22 @@ ifcapable check {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (ZEROBLOB(4));
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.13 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (X'');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.14 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (X'1234');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_test func4-4.15 {
|
||||
catchsql {
|
||||
INSERT INTO t2 (x) VALUES (X'12345678');
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t2}}
|
||||
do_execsql_test func4-4.16 {
|
||||
SELECT x FROM t2 ORDER BY x;
|
||||
} {1234.0 1234.0 1234.56 1234.56}
|
||||
|
@ -332,7 +332,7 @@ do_test in-10.2 {
|
||||
catchsql {
|
||||
INSERT INTO t5 VALUES(4);
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t5}}
|
||||
|
||||
# Ticket #1821
|
||||
#
|
||||
|
@ -397,16 +397,16 @@ do_test incrblob2-8.4 {
|
||||
} {cccccccccccccccccccc}
|
||||
do_test incrblob2-8.5 {
|
||||
catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test incrblob2-8.6 {
|
||||
catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test incrblob2-8.7 {
|
||||
sqlite3_blob_read $h 0 20
|
||||
} {cccccccccccccccccccc}
|
||||
do_test incrblob2-8.8 {
|
||||
catchsql {UPDATE t3 SET a = 6 WHERE a = 3 OR a = 5}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test incrblob2-8.9 {
|
||||
set rc [catch {sqlite3_blob_read $h 0 20} msg]
|
||||
list $rc $msg
|
||||
|
@ -666,7 +666,7 @@ ifcapable conflict {
|
||||
BEGIN;
|
||||
INSERT INTO t7 VALUES(1);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t7.a}}
|
||||
do_test index-19.3 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
@ -676,7 +676,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
INSERT INTO t8 VALUES(1);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t8.a}}
|
||||
do_test index-19.5 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
|
@ -34,7 +34,7 @@ do_test index3-1.2 {
|
||||
BEGIN;
|
||||
CREATE UNIQUE INDEX i1 ON t1(a);
|
||||
}
|
||||
} {1 {indexed columns are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test index3-1.3 {
|
||||
catchsql COMMIT;
|
||||
} {0 {}}
|
||||
|
@ -120,7 +120,7 @@ do_execsql_test 2.1 {
|
||||
}
|
||||
do_catchsql_test 2.2 {
|
||||
CREATE UNIQUE INDEX i3 ON t2(x);
|
||||
} {1 {indexed columns are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.x}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -220,7 +220,7 @@ do_test index6-3.2 {
|
||||
catchsql {
|
||||
INSERT INTO t3(a,b) VALUES(150, 'test1');
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test index6-3.3 {
|
||||
# can insert multiple rows with a==999 because such rows are not
|
||||
# part of the unique index.
|
||||
|
251
test/index7.test
Normal file
251
test/index7.test
Normal file
@ -0,0 +1,251 @@
|
||||
# 2013-11-04
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for partial indices in WITHOUT ROWID tables
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !vtab {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
load_static_extension db wholenumber;
|
||||
do_test index7-1.1 {
|
||||
# Able to parse and manage partial indices
|
||||
execsql {
|
||||
CREATE TABLE t1(a,b,c PRIMARY KEY) WITHOUT rowid;
|
||||
CREATE INDEX t1a ON t1(a) WHERE a IS NOT NULL;
|
||||
CREATE INDEX t1b ON t1(b) WHERE b>10;
|
||||
CREATE VIRTUAL TABLE nums USING wholenumber;
|
||||
INSERT INTO t1(a,b,c)
|
||||
SELECT CASE WHEN value%3!=0 THEN value END, value, value
|
||||
FROM nums WHERE value<=20;
|
||||
SELECT count(a), count(b) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {14 20 ok}
|
||||
|
||||
# Make sure the count(*) optimization works correctly with
|
||||
# partial indices. Ticket [a5c8ed66cae16243be6] 2013-10-03.
|
||||
#
|
||||
do_execsql_test index7-1.1.1 {
|
||||
SELECT count(*) FROM t1;
|
||||
} {20}
|
||||
|
||||
# Error conditions during parsing...
|
||||
#
|
||||
do_test index7-1.2 {
|
||||
catchsql {
|
||||
CREATE INDEX bad1 ON t1(a,b) WHERE x IS NOT NULL;
|
||||
}
|
||||
} {1 {no such column: x}}
|
||||
do_test index7-1.3 {
|
||||
catchsql {
|
||||
CREATE INDEX bad1 ON t1(a,b) WHERE EXISTS(SELECT * FROM t1);
|
||||
}
|
||||
} {1 {subqueries prohibited in partial index WHERE clauses}}
|
||||
do_test index7-1.4 {
|
||||
catchsql {
|
||||
CREATE INDEX bad1 ON t1(a,b) WHERE a!=?1;
|
||||
}
|
||||
} {1 {parameters prohibited in partial index WHERE clauses}}
|
||||
do_test index7-1.5 {
|
||||
catchsql {
|
||||
CREATE INDEX bad1 ON t1(a,b) WHERE a!=random();
|
||||
}
|
||||
} {1 {functions prohibited in partial index WHERE clauses}}
|
||||
do_test index7-1.6 {
|
||||
catchsql {
|
||||
CREATE INDEX bad1 ON t1(a,b) WHERE a NOT LIKE 'abc%';
|
||||
}
|
||||
} {1 {functions prohibited in partial index WHERE clauses}}
|
||||
|
||||
do_test index7-1.10 {
|
||||
execsql {
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {20 1} t1a {14 1} t1b {10 1} ok}
|
||||
|
||||
# STAT1 shows the partial indices have a reduced number of
|
||||
# rows.
|
||||
#
|
||||
do_test index7-1.11 {
|
||||
execsql {
|
||||
UPDATE t1 SET a=b;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {20 1} t1a {20 1} t1b {10 1} ok}
|
||||
|
||||
do_test index7-1.11b {
|
||||
execsql {
|
||||
UPDATE t1 SET a=NULL WHERE b%3!=0;
|
||||
UPDATE t1 SET b=b+100;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {20 1} t1a {6 1} t1b {20 1} ok}
|
||||
|
||||
do_test index7-1.12 {
|
||||
execsql {
|
||||
UPDATE t1 SET a=CASE WHEN b%3!=0 THEN b END;
|
||||
UPDATE t1 SET b=b-100;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {20 1} t1a {13 1} t1b {10 1} ok}
|
||||
|
||||
do_test index7-1.13 {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE b BETWEEN 8 AND 12;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {15 1} t1a {10 1} t1b {8 1} ok}
|
||||
|
||||
do_test index7-1.14 {
|
||||
execsql {
|
||||
REINDEX;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {15 1} t1a {10 1} t1b {8 1} ok}
|
||||
|
||||
do_test index7-1.15 {
|
||||
execsql {
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {t1 {15 1} t1a {10 1} t1b {8 1} t1c {15 1} ok}
|
||||
|
||||
# Queries use partial indices as appropriate times.
|
||||
#
|
||||
do_test index7-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a,b PRIMARY KEY) without rowid;
|
||||
INSERT INTO t2(a,b) SELECT value, value FROM nums WHERE value<1000;
|
||||
UPDATE t2 SET a=NULL WHERE b%5==0;
|
||||
CREATE INDEX t2a1 ON t2(a) WHERE a IS NOT NULL;
|
||||
SELECT count(*) FROM t2 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {800}
|
||||
do_test index7-2.2 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a=5;
|
||||
}
|
||||
} {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
|
||||
ifcapable stat4||stat3 {
|
||||
do_test index7-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
|
||||
} else {
|
||||
do_test index7-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL AND a>0;
|
||||
}
|
||||
} {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
|
||||
}
|
||||
do_test index7-2.4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NULL;
|
||||
}
|
||||
} {~/.*INDEX t2a1.*/}
|
||||
|
||||
do_execsql_test index7-2.101 {
|
||||
DROP INDEX t2a1;
|
||||
UPDATE t2 SET a=b, b=b+10000;
|
||||
SELECT b FROM t2 WHERE a=15;
|
||||
} {10015}
|
||||
do_execsql_test index7-2.102 {
|
||||
CREATE INDEX t2a2 ON t2(a) WHERE a<100 OR a>200;
|
||||
SELECT b FROM t2 WHERE a=15;
|
||||
PRAGMA integrity_check;
|
||||
} {10015 ok}
|
||||
do_execsql_test index7-2.102eqp {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t2 WHERE a=15;
|
||||
} {~/.*INDEX t2a2.*/}
|
||||
do_execsql_test index7-2.103 {
|
||||
SELECT b FROM t2 WHERE a=15 AND a<100;
|
||||
} {10015}
|
||||
do_execsql_test index7-2.103eqp {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t2 WHERE a=15 AND a<100;
|
||||
} {/.*INDEX t2a2.*/}
|
||||
do_execsql_test index7-2.104 {
|
||||
SELECT b FROM t2 WHERE a=515 AND a>200;
|
||||
} {10515}
|
||||
do_execsql_test index7-2.104eqp {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t2 WHERE a=515 AND a>200;
|
||||
} {/.*INDEX t2a2.*/}
|
||||
|
||||
# Partial UNIQUE indices
|
||||
#
|
||||
do_execsql_test index7-3.1 {
|
||||
CREATE TABLE t3(a,b PRIMARY KEY) without rowid;
|
||||
INSERT INTO t3 SELECT value, value FROM nums WHERE value<200;
|
||||
UPDATE t3 SET a=999 WHERE b%5!=0;
|
||||
CREATE UNIQUE INDEX t3a ON t3(a) WHERE a<>999;
|
||||
} {}
|
||||
do_test index7-3.2 {
|
||||
# unable to insert a duplicate row a-value that is not 999.
|
||||
catchsql {
|
||||
INSERT INTO t3(a,b) VALUES(150, 'test1');
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test index7-3.3 {
|
||||
# can insert multiple rows with a==999 because such rows are not
|
||||
# part of the unique index.
|
||||
catchsql {
|
||||
INSERT INTO t3(a,b) VALUES(999, 'test1'), (999, 'test2');
|
||||
}
|
||||
} {0 {}}
|
||||
do_execsql_test index7-3.4 {
|
||||
SELECT count(*) FROM t3 WHERE a=999;
|
||||
} {162}
|
||||
integrity_check index7-3.5
|
||||
|
||||
do_execsql_test index7-4.0 {
|
||||
VACUUM;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
# Silently ignore database name qualifiers in partial indices.
|
||||
#
|
||||
do_execsql_test index7-5.0 {
|
||||
CREATE INDEX t3b ON t3(b) WHERE xyzzy.t3.b BETWEEN 5 AND 10;
|
||||
/* ^^^^^-- ignored */
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM t3 WHERE t3.b BETWEEN 5 AND 10;
|
||||
SELECT stat+0 FROM sqlite_stat1 WHERE idx='t3b';
|
||||
} {6 6}
|
||||
|
||||
finish_test
|
@ -54,7 +54,7 @@ do_test insert4-1.1 {
|
||||
catchsql {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
xferopt_test insert4-1.2 0
|
||||
do_test insert4-1.3 {
|
||||
execsql {
|
||||
@ -101,7 +101,7 @@ do_test insert4-2.3.3 {
|
||||
INSERT INTO t1 SELECT * FROM t2 LIMIT 1;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
xferopt_test insert4-2.3.4 0
|
||||
|
||||
# Do not run the transfer optimization if there is a DISTINCT
|
||||
@ -119,7 +119,7 @@ do_test insert4-2.4.3 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 SELECT DISTINCT * FROM t2;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t1}}
|
||||
xferopt_test insert4-2.4.4 0
|
||||
|
||||
# The following procedure constructs two tables then tries to transfer
|
||||
@ -315,7 +315,7 @@ do_test insert4-6.6 {
|
||||
catchsql {
|
||||
INSERT INTO t6b SELECT * FROM t6a;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t6b}}
|
||||
do_test insert4-6.7 {
|
||||
execsql {
|
||||
DROP TABLE t6b;
|
||||
@ -324,7 +324,7 @@ do_test insert4-6.7 {
|
||||
catchsql {
|
||||
INSERT INTO t6b SELECT * FROM t6a;
|
||||
}
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: t6b}}
|
||||
|
||||
# Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
|
||||
# Disable the xfer optimization if the destination table contains
|
||||
@ -353,7 +353,7 @@ ifcapable foreignkey {
|
||||
catchsql {
|
||||
INSERT INTO t7b SELECT * FROM t7c;
|
||||
}
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_test insert4-7.4 {
|
||||
execsql {SELECT * FROM t7b}
|
||||
} {}
|
||||
@ -452,7 +452,7 @@ do_test insert4-8.5 {
|
||||
catchsql {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test insert4-8.6 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -472,7 +472,7 @@ do_test insert4-8.7 {
|
||||
catchsql {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test insert4-8.8 {
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
@ -494,7 +494,7 @@ do_test insert4-8.9 {
|
||||
INSERT INTO t1 VALUES(2,3);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test insert4-8.10 {
|
||||
catchsql {COMMIT}
|
||||
} {1 {cannot commit - no transaction is active}}
|
||||
|
@ -76,7 +76,7 @@ do_test intpkey-1.6 {
|
||||
INSERT INTO t1 VALUES(5,'second','entry');
|
||||
}} msg]
|
||||
lappend r $msg
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test intpkey-1.7 {
|
||||
execsql {
|
||||
SELECT rowid, * FROM t1;
|
||||
|
@ -240,7 +240,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} {
|
||||
if {$i>1} break
|
||||
}
|
||||
|
||||
if {$t0} {set t1 {column a is not unique}}
|
||||
if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test memdb-5.$i {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
|
||||
|
@ -235,7 +235,7 @@ do_test misc1-7.4 {
|
||||
catchsql {
|
||||
INSERT INTO t5 VALUES(1,2,4);
|
||||
}
|
||||
} {1 {columns a, b are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t5.a, t5.b}}
|
||||
do_test misc1-7.5 {
|
||||
catchsql {
|
||||
INSERT INTO t5 VALUES(0,2,4);
|
||||
|
@ -283,7 +283,7 @@ ifcapable {explain} {
|
||||
}]
|
||||
set y [regexp { 123456789012 } $x]
|
||||
lappend y [regexp { 4.5678 } $x]
|
||||
lappend y [regexp {,-BINARY} $x]
|
||||
lappend y [regexp {,-B} $x]
|
||||
} {1 1 1}
|
||||
} else {
|
||||
do_test misc3-6.11-utf8 {
|
||||
@ -293,7 +293,7 @@ ifcapable {explain} {
|
||||
set y [regexp { 123456789012 } $x]
|
||||
lappend y [regexp { 4.5678 } $x]
|
||||
lappend y [regexp { hello } $x]
|
||||
lappend y [regexp {,-BINARY} $x]
|
||||
lappend y [regexp {,-B} $x]
|
||||
} {1 1 1 1}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ do_test notnull-1.2 {
|
||||
INSERT INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-1.2b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.3 {
|
||||
catchsql {
|
||||
@ -62,7 +62,7 @@ do_test notnull-1.4 {
|
||||
INSERT OR REPLACE INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-1.4b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.5 {
|
||||
catchsql {
|
||||
@ -70,7 +70,7 @@ do_test notnull-1.5 {
|
||||
INSERT OR ABORT INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-1.5b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.6 {
|
||||
catchsql {
|
||||
@ -106,7 +106,7 @@ do_test notnull-1.10 {
|
||||
INSERT INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-1.10b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.11 {
|
||||
catchsql {
|
||||
@ -149,7 +149,7 @@ do_test notnull-1.16 {
|
||||
INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.c may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.c}}
|
||||
verify_ex_errcode notnull-1.16b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.17 {
|
||||
catchsql {
|
||||
@ -157,7 +157,7 @@ do_test notnull-1.17 {
|
||||
INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,3,null,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.d may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.d}}
|
||||
verify_ex_errcode notnull-1.17b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.18 {
|
||||
catchsql {
|
||||
@ -179,7 +179,7 @@ do_test notnull-1.20 {
|
||||
INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,null);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.e may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.e}}
|
||||
verify_ex_errcode notnull-1.20b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-1.21 {
|
||||
catchsql {
|
||||
@ -196,7 +196,7 @@ do_test notnull-2.1 {
|
||||
UPDATE t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-2.1b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-2.2 {
|
||||
catchsql {
|
||||
@ -205,7 +205,7 @@ do_test notnull-2.2 {
|
||||
UPDATE OR REPLACE t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-2.2b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-2.3 {
|
||||
catchsql {
|
||||
@ -222,7 +222,7 @@ do_test notnull-2.4 {
|
||||
UPDATE OR ABORT t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-2.4b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-2.5 {
|
||||
catchsql {
|
||||
@ -231,7 +231,7 @@ do_test notnull-2.5 {
|
||||
UPDATE t1 SET b=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-2.6b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-2.6 {
|
||||
catchsql {
|
||||
@ -272,7 +272,7 @@ do_test notnull-2.10 {
|
||||
UPDATE t1 SET e=null, a=b, b=a;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.e may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.e}}
|
||||
verify_ex_errcode notnull-2.10b SQLITE_CONSTRAINT_NOTNULL
|
||||
|
||||
do_test notnull-3.0 {
|
||||
@ -298,7 +298,7 @@ do_test notnull-3.2 {
|
||||
INSERT INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-3.2b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.3 {
|
||||
catchsql {
|
||||
@ -313,7 +313,7 @@ do_test notnull-3.4 {
|
||||
INSERT OR REPLACE INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-3.4b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.5 {
|
||||
catchsql {
|
||||
@ -321,7 +321,7 @@ do_test notnull-3.5 {
|
||||
INSERT OR ABORT INTO t1(b,c,d,e) VALUES(2,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-3.5b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.6 {
|
||||
catchsql {
|
||||
@ -357,7 +357,7 @@ do_test notnull-3.10 {
|
||||
INSERT INTO t1(a,b,c,d,e) VALUES(1,null,3,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-3.10b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.11 {
|
||||
catchsql {
|
||||
@ -400,7 +400,7 @@ do_test notnull-3.16 {
|
||||
INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,null,4,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.c may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.c}}
|
||||
verify_ex_errcode notnull-3.16b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.17 {
|
||||
catchsql {
|
||||
@ -408,7 +408,7 @@ do_test notnull-3.17 {
|
||||
INSERT OR ABORT INTO t1(a,b,c,d,e) VALUES(1,2,3,null,5);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.d may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.d}}
|
||||
verify_ex_errcode notnull-3.17b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.18 {
|
||||
catchsql {
|
||||
@ -430,7 +430,7 @@ do_test notnull-3.20 {
|
||||
INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,null);
|
||||
SELECT * FROM t1 order by a;
|
||||
}
|
||||
} {1 {t1.e may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.e}}
|
||||
verify_ex_errcode notnull-3.20b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-3.21 {
|
||||
catchsql {
|
||||
@ -447,7 +447,7 @@ do_test notnull-4.1 {
|
||||
UPDATE t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-4.1b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-4.2 {
|
||||
catchsql {
|
||||
@ -456,7 +456,7 @@ do_test notnull-4.2 {
|
||||
UPDATE OR REPLACE t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-4.2b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-4.3 {
|
||||
catchsql {
|
||||
@ -473,7 +473,7 @@ do_test notnull-4.4 {
|
||||
UPDATE OR ABORT t1 SET a=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.a}}
|
||||
verify_ex_errcode notnull-4.4b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-4.5 {
|
||||
catchsql {
|
||||
@ -482,7 +482,7 @@ do_test notnull-4.5 {
|
||||
UPDATE t1 SET b=null;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-4.5b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-4.6 {
|
||||
catchsql {
|
||||
@ -523,7 +523,7 @@ do_test notnull-4.10 {
|
||||
UPDATE t1 SET e=null, a=b, b=a;
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
}
|
||||
} {1 {t1.e may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.e}}
|
||||
verify_ex_errcode notnull-4.10b SQLITE_CONSTRAINT_NOTNULL
|
||||
|
||||
# Test that bug 29ab7be99f is fixed.
|
||||
@ -542,7 +542,7 @@ do_test notnull-5.2 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-5.2b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-5.3 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
@ -555,7 +555,7 @@ do_test notnull-5.4 {
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
COMMIT;
|
||||
}
|
||||
} {1 {t1.b may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t1.b}}
|
||||
verify_ex_errcode notnull-5.4b SQLITE_CONSTRAINT_NOTNULL
|
||||
do_test notnull-5.5 {
|
||||
execsql { SELECT * FROM t1 }
|
||||
|
@ -94,4 +94,19 @@ do_execsql_test 2.7 {
|
||||
} {/B-TREE/}
|
||||
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c, d, e, f);
|
||||
CREATE INDEX t3bcde ON t3(b, c, d, e);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT a FROM t3 WHERE b=2 AND c=3 ORDER BY d DESC, e DESC, b, c, a DESC;
|
||||
} {~/B-TREE/}
|
||||
do_execsql_test 3.1 {
|
||||
DROP TABLE t3;
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c, d, e, f) WITHOUT rowid;
|
||||
CREATE INDEX t3bcde ON t3(b, c, d, e);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT a FROM t3 WHERE b=2 AND c=3 ORDER BY d DESC, e DESC, b, c, a DESC;
|
||||
} {~/B-TREE/}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -271,7 +271,7 @@ do_execsql_test pager1-3.1.2 {
|
||||
} {3 0}
|
||||
do_catchsql_test pager1-3.1.3 {
|
||||
INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
|
||||
} {1 {constraint failed}}
|
||||
} {1 {CHECK constraint failed: counter}}
|
||||
do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
|
||||
do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
|
||||
do_execsql_test pager1-3.6 { COMMIT } {}
|
||||
@ -1703,7 +1703,7 @@ do_catchsql_test pager1-14.1.4 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
|
||||
INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.rowid}}
|
||||
do_execsql_test pager1-14.1.5 {
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
|
@ -285,31 +285,31 @@ ifcapable attach {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {PRAGMA integrity_check}
|
||||
} {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.3 {
|
||||
execsql {PRAGMA integrity_check=1}
|
||||
} {{rowid 1 missing from index i2}}
|
||||
} {{row 1 missing from index i2}}
|
||||
do_test pragma-3.4 {
|
||||
execsql {
|
||||
ATTACH DATABASE 'test.db' AS t2;
|
||||
PRAGMA integrity_check
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.5 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=4
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2}}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2}}
|
||||
do_test pragma-3.6 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=xyz
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.7 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=0
|
||||
}
|
||||
} {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
|
||||
# Add additional corruption by appending unused pages to the end of
|
||||
# the database file testerr.db
|
||||
@ -344,7 +344,7 @@ ifcapable attach {
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.10 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=1
|
||||
@ -358,7 +358,7 @@ Page 4 is never used}}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2}}
|
||||
do_test pragma-3.12 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=4
|
||||
@ -366,7 +366,7 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2}}
|
||||
do_test pragma-3.13 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=3
|
||||
@ -390,10 +390,10 @@ Page 5 is never used}}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_test pragma-3.16 {
|
||||
execsql {
|
||||
PRAGMA integrity_check(10)
|
||||
@ -401,10 +401,10 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2}}
|
||||
do_test pragma-3.17 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=8
|
||||
@ -412,7 +412,7 @@ Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used}}
|
||||
do_test pragma-3.18 {
|
||||
@ -422,7 +422,7 @@ Page 5 is never used}}
|
||||
} {{*** in database t2 ***
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {rowid 1 missing from index i2}}
|
||||
Page 6 is never used} {row 1 missing from index i2}}
|
||||
}
|
||||
do_test pragma-3.19 {
|
||||
catch {db close}
|
||||
|
@ -54,7 +54,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
INSERT INTO t3 SELECT a FROM t1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
|
||||
# Try to continue with the SELECT statement
|
||||
#
|
||||
|
@ -858,7 +858,7 @@ do_test savepoint-12.2 {
|
||||
SAVEPOINT sp2;
|
||||
INSERT OR ROLLBACK INTO t4 VALUES(1, 'one');
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t4.a}}
|
||||
do_test savepoint-12.3 {
|
||||
sqlite3_get_autocommit db
|
||||
} {1}
|
||||
|
@ -30,7 +30,7 @@ do_test schema5-1.1 {
|
||||
} {1 2 3}
|
||||
do_test schema5-1.2 {
|
||||
catchsql {INSERT INTO t1 VALUES(1,3,4);}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test schema5-1.3 {
|
||||
db eval {
|
||||
DROP TABLE t1;
|
||||
@ -44,7 +44,7 @@ do_test schema5-1.3 {
|
||||
} {1 2 3}
|
||||
do_test schema5-1.4 {
|
||||
catchsql {INSERT INTO t1 VALUES(10,11,12);}
|
||||
} {1 {constraint two failed}}
|
||||
} {1 {CHECK constraint failed: two}}
|
||||
do_test schema5-1.5 {
|
||||
db eval {
|
||||
DROP TABLE t1;
|
||||
@ -57,10 +57,10 @@ do_test schema5-1.5 {
|
||||
} {}
|
||||
do_test schema5-1.6 {
|
||||
catchsql {INSERT INTO t1 VALUES(1,3,4)}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test schema5-1.7 {
|
||||
catchsql {INSERT INTO t1 VALUES(10,2,3)}
|
||||
} {1 {columns b, c are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.b, t1.c}}
|
||||
|
||||
|
||||
|
||||
|
@ -464,7 +464,7 @@ do_test table-10.1 {
|
||||
CREATE TABLE t6(a REFERENCES t4(a) NOT NULL);
|
||||
INSERT INTO t6 VALUES(NULL);
|
||||
}
|
||||
} {1 {t6.a may not be NULL}}
|
||||
} {1 {NOT NULL constraint failed: t6.a}}
|
||||
do_test table-10.2 {
|
||||
catchsql {
|
||||
DROP TABLE t6;
|
||||
|
71
test/tableopts.test
Normal file
71
test/tableopts.test
Normal file
@ -0,0 +1,71 @@
|
||||
# 2013-10-19
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test the operation of table-options in the WITH clause of the
|
||||
# CREATE TABLE statement.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test tableopt-1.1 {
|
||||
catchsql {
|
||||
CREATE TABLE t1(a,b) WITHOUT rowid;
|
||||
}
|
||||
} {1 {no PRIMARY KEY for table t1}}
|
||||
do_test tableopt-1.2 {
|
||||
catchsql {
|
||||
CREATE TABLE t1(a,b) WITHOUT unknown2;
|
||||
}
|
||||
} {1 {unknown table option: unknown2}}
|
||||
|
||||
do_execsql_test tableopt-2.1 {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
INSERT INTO t1 VALUES(1,2,3),(2,3,4);
|
||||
SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;
|
||||
} {3 4}
|
||||
do_test tableopt-2.1.1 {
|
||||
catchsql {
|
||||
SELECT rowid, * FROM t1;
|
||||
}
|
||||
} {1 {no such column: rowid}}
|
||||
do_test tableopt-2.1.2 {
|
||||
catchsql {
|
||||
SELECT _rowid_, * FROM t1;
|
||||
}
|
||||
} {1 {no such column: _rowid_}}
|
||||
do_test tableopt-2.1.3 {
|
||||
catchsql {
|
||||
SELECT oid, * FROM t1;
|
||||
}
|
||||
} {1 {no such column: oid}}
|
||||
do_execsql_test tableopt-2.2 {
|
||||
VACUUM;
|
||||
SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;
|
||||
} {3 4}
|
||||
do_test tableopt-2.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;}
|
||||
} {3 4}
|
||||
db2 close
|
||||
|
||||
# Make sure the "without" keyword is still usable as a table or
|
||||
# column name.
|
||||
#
|
||||
do_execsql_test tableopt-3.1 {
|
||||
CREATE TABLE without(x INTEGER PRIMARY KEY, without TEXT);
|
||||
INSERT INTO without VALUES(1, 'xyzzy'), (2, 'fizzle');
|
||||
SELECT * FROM without WHERE without='xyzzy';
|
||||
} {1 xyzzy}
|
||||
|
||||
|
||||
finish_test
|
@ -1023,6 +1023,78 @@ proc explain {sql {db db}} {
|
||||
}
|
||||
}
|
||||
|
||||
proc explain_i {sql {db db}} {
|
||||
puts ""
|
||||
puts "addr opcode p1 p2 p3 p4 p5 #"
|
||||
puts "---- ------------ ------ ------ ------ ---------------- -- -"
|
||||
|
||||
|
||||
# Set up colors for the different opcodes. Scheme is as follows:
|
||||
#
|
||||
# Red: Opcodes that write to a b-tree.
|
||||
# Blue: Opcodes that reposition or seek a cursor.
|
||||
# Green: The ResultRow opcode.
|
||||
#
|
||||
set R "\033\[31;1m" ;# Red fg
|
||||
set G "\033\[32;1m" ;# Green fg
|
||||
set B "\033\[34;1m" ;# Red fg
|
||||
set D "\033\[39;0m" ;# Default fg
|
||||
foreach opcode {
|
||||
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
|
||||
NoConflict Next Prev
|
||||
} {
|
||||
set color($opcode) $B
|
||||
}
|
||||
foreach opcode {ResultRow} {
|
||||
set color($opcode) $G
|
||||
}
|
||||
foreach opcode {IdxInsert Insert Delete IdxDelete} {
|
||||
set color($opcode) $R
|
||||
}
|
||||
|
||||
set bSeenGoto 0
|
||||
$db eval "explain $sql" {} {
|
||||
set x($addr) 0
|
||||
set op($addr) $opcode
|
||||
|
||||
if {$opcode == "Goto" && ($bSeenGoto==0 || ($p2 > $addr+10))} {
|
||||
set linebreak($p2) 1
|
||||
set bSeenGoto 1
|
||||
}
|
||||
|
||||
if {$opcode == "Next" || $opcode=="Prev"} {
|
||||
for {set i $p2} {$i<$addr} {incr i} {
|
||||
incr x($i) 2
|
||||
}
|
||||
}
|
||||
|
||||
if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} {
|
||||
for {set i [expr $p2+1]} {$i<$addr} {incr i} {
|
||||
incr x($i) 2
|
||||
}
|
||||
}
|
||||
|
||||
if {$opcode == "Halt" && $comment == "End of coroutine"} {
|
||||
set linebreak([expr $addr+1]) 1
|
||||
}
|
||||
}
|
||||
|
||||
$db eval "explain $sql" {} {
|
||||
if {[info exists linebreak($addr)]} {
|
||||
puts ""
|
||||
}
|
||||
set I [string repeat " " $x($addr)]
|
||||
|
||||
set col ""
|
||||
catch { set col $color($opcode) }
|
||||
|
||||
puts [format {%-4d %s%s%-12.12s%s %-6d %-6d %-6d % -17s %s %s} \
|
||||
$addr $I $col $opcode $D $p1 $p2 $p3 $p4 $p5 $comment
|
||||
]
|
||||
}
|
||||
puts "---- ------------ ------ ------ ------ ---------------- -- -"
|
||||
}
|
||||
|
||||
# Show the VDBE program for an SQL statement but omit the Trace
|
||||
# opcode at the beginning. This procedure can be used to prove
|
||||
# that different SQL statements generate exactly the same VDBE code.
|
||||
|
@ -31,7 +31,7 @@ do_test tkt-4a03ed-1.1 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
COMMIT;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.b}}
|
||||
do_test tkt-4a03ed-1.2 {
|
||||
db eval {
|
||||
PRAGMA integrity_check;
|
||||
|
@ -29,7 +29,7 @@ do_test tkt-78e04-1.1 {
|
||||
catchsql {
|
||||
INSERT INTO ""("") VALUES(1);
|
||||
}
|
||||
} {1 {column is not unique}}
|
||||
} {1 {UNIQUE constraint failed: .}}
|
||||
do_test tkt-78e04-1.2 {
|
||||
execsql {
|
||||
PRAGMA table_info("");
|
||||
|
@ -98,7 +98,7 @@ do_catchsql_test 3.2 {
|
||||
DROP TABLE pp1;
|
||||
DROP TABLE cc1;
|
||||
COMMIT;
|
||||
} {1 {foreign key constraint failed}}
|
||||
} {1 {FOREIGN KEY constraint failed}}
|
||||
do_catchsql_test 3.3 {
|
||||
DROP TABLE cc2;
|
||||
COMMIT;
|
||||
|
@ -40,7 +40,7 @@ do_test tkt1567-1.4 {
|
||||
catchsql {
|
||||
UPDATE t1 SET a = CASE WHEN rowid<90 THEN substr(a,1,10) ELSE '9999' END;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test tkt1567-1.5 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
@ -48,4 +48,35 @@ do_test tkt1567-1.5 {
|
||||
} {}
|
||||
integrity_check tkt1567-1.6
|
||||
|
||||
do_test tkt1567-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a TEXT PRIMARY KEY, rowid INT) WITHOUT rowid;
|
||||
}
|
||||
set bigstr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
for {set i 0} {$i<100} {incr i} {
|
||||
set x [format %5d [expr $i*2]]
|
||||
set sql "INSERT INTO t2 VALUES('$x-$bigstr', $i+1)"
|
||||
execsql $sql
|
||||
}
|
||||
} {}
|
||||
integrity_check tkt1567-2.2
|
||||
|
||||
do_test tkt1567-2.3 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
UPDATE t2 SET a = a||'x' WHERE rowid%2==0;
|
||||
}
|
||||
} {}
|
||||
do_test tkt1567-2.4 {
|
||||
catchsql {
|
||||
UPDATE t2 SET a = CASE WHEN rowid<90 THEN substr(a,1,10) ELSE '9999' END;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t2.a}}
|
||||
do_test tkt1567-2.5 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
integrity_check tkt1567-2.6
|
||||
|
||||
finish_test
|
||||
|
@ -74,7 +74,7 @@ do_test tkt35xx-1.2.2 {
|
||||
DROP TABLE t5;
|
||||
INSERT INTO t3(a, b) SELECT c, d FROM t4;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a}}
|
||||
do_test tkt35xx-1.2.3 {
|
||||
# Show that the transaction has not been rolled back.
|
||||
catchsql BEGIN
|
||||
|
@ -497,7 +497,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
INSERT OR ABORT INTO tbl values (2, 2, 3);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.1c {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
@ -507,7 +507,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
INSERT OR FAIL INTO tbl values (2, 2, 3);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.1e {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
@ -523,7 +523,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.1h {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
@ -551,7 +551,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.2c {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
@ -561,7 +561,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.2e {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
@ -583,7 +583,7 @@ ifcapable conflict {
|
||||
catchsql {
|
||||
UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test trigger2-6.2h {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
|
@ -157,7 +157,7 @@ do_test triggerC-1.14 {
|
||||
} {}
|
||||
do_test triggerC-1.15 {
|
||||
catchsql { UPDATE OR ROLLBACK t1 SET a=100 }
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
@ -47,8 +47,8 @@ do_test unique-1.3 {
|
||||
catchsql {
|
||||
INSERT INTO t1(a,b,c) VALUES(1,3,4)
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_UNIQUE
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_PRIMARYKEY
|
||||
do_test unique-1.4 {
|
||||
execsql {
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
@ -58,7 +58,7 @@ do_test unique-1.5 {
|
||||
catchsql {
|
||||
INSERT INTO t1(a,b,c) VALUES(3,2,4)
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.b}}
|
||||
verify_ex_errcode unique-1.5b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test unique-1.6 {
|
||||
execsql {
|
||||
@ -100,7 +100,7 @@ do_test unique-2.3 {
|
||||
catchsql {
|
||||
INSERT INTO t2 VALUES(1,5);
|
||||
}
|
||||
} {1 {column a is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.a}}
|
||||
verify_ex_errcode unique-2.3b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test unique-2.4 {
|
||||
catchsql {
|
||||
@ -127,7 +127,7 @@ do_test unique-2.8 {
|
||||
catchsql {
|
||||
CREATE UNIQUE INDEX i2 ON t2(a);
|
||||
}
|
||||
} {1 {indexed columns are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t2.a}}
|
||||
verify_ex_errcode unique-2.8b SQLITE_CONSTRAINT_UNIQUE
|
||||
do_test unique-2.9 {
|
||||
catchsql {
|
||||
@ -166,7 +166,7 @@ do_test unique-3.4 {
|
||||
INSERT INTO t3(a,b,c,d) VALUES(1,4,3,5);
|
||||
SELECT * FROM t3 ORDER BY a,b,c,d;
|
||||
}
|
||||
} {1 {columns a, c, d are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.a, t3.c, t3.d}}
|
||||
verify_ex_errcode unique-3.4b SQLITE_CONSTRAINT_UNIQUE
|
||||
integrity_check unique-3.5
|
||||
|
||||
@ -221,7 +221,7 @@ do_test unique-4.9 {
|
||||
} {0 {}}
|
||||
do_test unique-4.10 {
|
||||
catchsql {CREATE UNIQUE INDEX i4c ON t4(b)}
|
||||
} {1 {indexed columns are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t4.b}}
|
||||
verify_ex_errcode unique-4.10b SQLITE_CONSTRAINT_UNIQUE
|
||||
integrity_check unique-4.99
|
||||
|
||||
@ -254,7 +254,7 @@ do_test unique-5.2 {
|
||||
catchsql {
|
||||
INSERT INTO t5 VALUES(1,2,3,4,5,6);
|
||||
}
|
||||
} {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, sixth_column_with_long_name are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t5.first_column_with_long_name, t5.second_column_with_long_name, t5.third_column_with_long_name, t5.fourth_column_with_long_name, t5.fifth_column_with_long_name, t5.sixth_column_with_long_name}}
|
||||
verify_ex_errcode unique-5.2b SQLITE_CONSTRAINT_UNIQUE
|
||||
|
||||
|
||||
|
@ -452,7 +452,7 @@ do_test update-10.3 {
|
||||
UPDATE t1 SET a=1, e=10 WHERE f=7;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 {PRIMARY KEY must be unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test update-10.4 {
|
||||
catchsql {
|
||||
SELECT * FROM t1;
|
||||
@ -469,7 +469,7 @@ do_test update-10.6 {
|
||||
UPDATE t1 SET b=2, e=12 WHERE f=7;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 {column b is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.b}}
|
||||
do_test update-10.7 {
|
||||
catchsql {
|
||||
SELECT * FROM t1;
|
||||
@ -486,7 +486,7 @@ do_test update-10.9 {
|
||||
UPDATE t1 SET c=3, d=4, e=14 WHERE f=7;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 {columns c, d are not unique}}
|
||||
} {1 {UNIQUE constraint failed: t1.c, t1.d}}
|
||||
do_test update-10.10 {
|
||||
catchsql {
|
||||
SELECT * FROM t1;
|
||||
|
@ -1040,10 +1040,10 @@ do_test vtab1.12-1 {
|
||||
# First test outside of a transaction.
|
||||
do_test vtab1.12-2 {
|
||||
catchsql { INSERT INTO echo_c SELECT * FROM b; }
|
||||
} {1 {echo-vtab-error: column a is not unique}}
|
||||
} {1 {echo-vtab-error: UNIQUE constraint failed: c.a}}
|
||||
do_test vtab1.12-2.1 {
|
||||
sqlite3_errmsg db
|
||||
} {echo-vtab-error: column a is not unique}
|
||||
} {echo-vtab-error: UNIQUE constraint failed: c.a}
|
||||
do_test vtab1.12-3 {
|
||||
execsql { SELECT * FROM c }
|
||||
} {3 G H}
|
||||
@ -1052,7 +1052,7 @@ do_test vtab1.12-3 {
|
||||
do_test vtab1.12-4 {
|
||||
execsql {BEGIN}
|
||||
catchsql { INSERT INTO echo_c SELECT * FROM b; }
|
||||
} {1 {echo-vtab-error: column a is not unique}}
|
||||
} {1 {echo-vtab-error: UNIQUE constraint failed: c.a}}
|
||||
do_test vtab1.12-5 {
|
||||
execsql { SELECT * FROM c }
|
||||
} {3 G H}
|
||||
|
@ -322,7 +322,7 @@ do_test wal-5.4 {
|
||||
INSERT INTO t3 VALUES('abc');
|
||||
}
|
||||
catchsql { INSERT INTO t3 VALUES('abc') }
|
||||
} {1 {column x is not unique}}
|
||||
} {1 {UNIQUE constraint failed: t3.x}}
|
||||
do_test wal-5.5 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
|
@ -12,7 +12,6 @@
|
||||
# is testing of where.c. More specifically, the focus is the optimization
|
||||
# of WHERE clauses that feature the OR operator.
|
||||
#
|
||||
# $Id: where8.test,v 1.9 2009/07/31 06:14:52 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -749,4 +748,13 @@ do_test where8-5.3 {
|
||||
}
|
||||
} {1 {}}
|
||||
|
||||
# The OR optimization and WITHOUT ROWID
|
||||
#
|
||||
do_execsql_test where8-6.1 {
|
||||
CREATE TABLE t600(a PRIMARY KEY, b) WITHOUT rowid;
|
||||
CREATE INDEX t600b ON t600(b);
|
||||
INSERT INTO t600 VALUES('state','screen'),('exact','dolphin'),('green','mercury');
|
||||
SELECT a, b, '|' FROM t600 WHERE a=='state' OR b='mercury' ORDER BY +a;
|
||||
} {green mercury | state screen |}
|
||||
|
||||
finish_test
|
||||
|
203
test/without_rowid1.test
Normal file
203
test/without_rowid1.test
Normal file
@ -0,0 +1,203 @@
|
||||
# 2013-10-30
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing WITHOUT ROWID tables.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix without_rowid1
|
||||
|
||||
# Create and query a WITHOUT ROWID table.
|
||||
#
|
||||
do_execsql_test without_rowid1-1.0 {
|
||||
CREATE TABLE t1(a,b,c,d, PRIMARY KEY(c,a)) WITHOUT ROWID;
|
||||
CREATE INDEX t1bd ON t1(b, d);
|
||||
INSERT INTO t1 VALUES('journal','sherman','ammonia','helena');
|
||||
INSERT INTO t1 VALUES('dynamic','juliet','flipper','command');
|
||||
INSERT INTO t1 VALUES('journal','sherman','gamma','patriot');
|
||||
INSERT INTO t1 VALUES('arctic','sleep','ammonia','helena');
|
||||
SELECT *, '|' FROM t1 ORDER BY c, a;
|
||||
} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
|
||||
|
||||
integrity_check without_rowid1-1.0ic
|
||||
|
||||
do_execsql_test without_rowid1-1.1 {
|
||||
SELECT *, '|' FROM t1 ORDER BY +c, a;
|
||||
} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
|
||||
|
||||
do_execsql_test without_rowid1-1.2 {
|
||||
SELECT *, '|' FROM t1 ORDER BY c DESC, a DESC;
|
||||
} {journal sherman gamma patriot | dynamic juliet flipper command | journal sherman ammonia helena | arctic sleep ammonia helena |}
|
||||
|
||||
do_execsql_test without_rowid1-1.11 {
|
||||
SELECT *, '|' FROM t1 ORDER BY b, d;
|
||||
} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
|
||||
|
||||
do_execsql_test without_rowid1-1.12 {
|
||||
SELECT *, '|' FROM t1 ORDER BY +b, d;
|
||||
} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
|
||||
|
||||
# Trying to insert a duplicate PRIMARY KEY fails.
|
||||
#
|
||||
do_test without_rowid1-1.21 {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES('dynamic','phone','flipper','harvard');
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: t1.c, t1.a}}
|
||||
|
||||
# REPLACE INTO works, however.
|
||||
#
|
||||
do_execsql_test without_rowid1-1.22 {
|
||||
REPLACE INTO t1 VALUES('dynamic','phone','flipper','harvard');
|
||||
SELECT *, '|' FROM t1 ORDER BY c, a;
|
||||
} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic phone flipper harvard | journal sherman gamma patriot |}
|
||||
|
||||
do_execsql_test without_rowid1-1.23 {
|
||||
SELECT *, '|' FROM t1 ORDER BY b, d;
|
||||
} {dynamic phone flipper harvard | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
|
||||
|
||||
# UPDATE statements.
|
||||
#
|
||||
do_execsql_test without_rowid1-1.31 {
|
||||
UPDATE t1 SET d=3.1415926 WHERE a='journal';
|
||||
SELECT *, '|' FROM t1 ORDER BY c, a;
|
||||
} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | dynamic phone flipper harvard | journal sherman gamma 3.1415926 |}
|
||||
do_execsql_test without_rowid1-1.32 {
|
||||
SELECT *, '|' FROM t1 ORDER BY b, d;
|
||||
} {dynamic phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
|
||||
|
||||
do_execsql_test without_rowid1-1.35 {
|
||||
UPDATE t1 SET a=1250 WHERE b='phone';
|
||||
SELECT *, '|' FROM t1 ORDER BY c, a;
|
||||
} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | 1250 phone flipper harvard | journal sherman gamma 3.1415926 |}
|
||||
integrity_check without_rowid1-1.36
|
||||
|
||||
do_execsql_test without_rowid1-1.37 {
|
||||
SELECT *, '|' FROM t1 ORDER BY b, d;
|
||||
} {1250 phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
|
||||
|
||||
do_execsql_test without_rowid1-1.40 {
|
||||
VACUUM;
|
||||
SELECT *, '|' FROM t1 ORDER BY b, d;
|
||||
} {1250 phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
|
||||
integrity_check without_rowid1-1.41
|
||||
|
||||
# Verify that ANALYZE works
|
||||
#
|
||||
do_execsql_test without_rowid1-1.50 {
|
||||
ANALYZE;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
} {t1 t1 {4 2 1} t1 t1bd {4 2 2}}
|
||||
ifcapable stat3 {
|
||||
do_execsql_test without_rowid1-1.51 {
|
||||
SELECT DISTINCT tbl, idx FROM sqlite_stat3 ORDER BY idx;
|
||||
} {t1 t1 t1 t1bd}
|
||||
}
|
||||
ifcapable stat4 {
|
||||
do_execsql_test without_rowid1-1.52 {
|
||||
SELECT DISTINCT tbl, idx FROM sqlite_stat4 ORDER BY idx;
|
||||
} {t1 t1 t1 t1bd}
|
||||
}
|
||||
|
||||
#----------
|
||||
|
||||
do_execsql_test 2.1.1 {
|
||||
CREATE TABLE t4 (a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID;
|
||||
INSERT INTO t4 VALUES('abc', 'def');
|
||||
SELECT * FROM t4;
|
||||
} {abc def}
|
||||
do_execsql_test 2.1.2 {
|
||||
UPDATE t4 SET a = 'ABC';
|
||||
SELECT * FROM t4;
|
||||
} {ABC def}
|
||||
|
||||
do_execsql_test 2.2.1 {
|
||||
DROP TABLE t4;
|
||||
CREATE TABLE t4 (b, a COLLATE nocase PRIMARY KEY) WITHOUT ROWID;
|
||||
INSERT INTO t4(a, b) VALUES('abc', 'def');
|
||||
SELECT * FROM t4;
|
||||
} {def abc}
|
||||
|
||||
do_execsql_test 2.2.2 {
|
||||
UPDATE t4 SET a = 'ABC', b = 'xyz';
|
||||
SELECT * FROM t4;
|
||||
} {xyz ABC}
|
||||
|
||||
do_execsql_test 2.3.1 {
|
||||
CREATE TABLE t5 (a, b, PRIMARY KEY(b, a)) WITHOUT ROWID;
|
||||
INSERT INTO t5(a, b) VALUES('abc', 'def');
|
||||
UPDATE t5 SET a='abc', b='def';
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.4.1 {
|
||||
CREATE TABLE t6 (
|
||||
a COLLATE nocase, b, c UNIQUE, PRIMARY KEY(b, a)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
INSERT INTO t6(a, b, c) VALUES('abc', 'def', 'ghi');
|
||||
UPDATE t6 SET a='ABC', c='ghi';
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.4.2 {
|
||||
SELECT * FROM t6 ORDER BY b, a;
|
||||
SELECT * FROM t6 ORDER BY c;
|
||||
} {ABC def ghi ABC def ghi}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Unless the destination table is completely empty, the xfer optimization
|
||||
# is disabled for WITHOUT ROWID tables. The following tests check for
|
||||
# some problems that might occur if this were not the case.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1.1 {
|
||||
CREATE TABLE t1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||
CREATE UNIQUE INDEX i1 ON t1(b);
|
||||
|
||||
CREATE TABLE t2(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||
CREATE UNIQUE INDEX i2 ON t2(b);
|
||||
|
||||
INSERT INTO t1 VALUES('one', 'two');
|
||||
INSERT INTO t2 VALUES('three', 'two');
|
||||
}
|
||||
|
||||
do_execsql_test 3.1.2 {
|
||||
INSERT OR REPLACE INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
} {three two}
|
||||
|
||||
do_execsql_test 3.1.3 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
} {three two}
|
||||
|
||||
do_catchsql_test 3.1.4 {
|
||||
INSERT INTO t2 VALUES('four', 'four');
|
||||
INSERT INTO t2 VALUES('six', 'two');
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
} {1 {UNIQUE constraint failed: t2.b}}
|
||||
|
||||
do_execsql_test 3.1.5 {
|
||||
CREATE TABLE t3(a PRIMARY KEY);
|
||||
CREATE TABLE t4(a PRIMARY KEY);
|
||||
|
||||
INSERT INTO t4 VALUES('i');
|
||||
INSERT INTO t4 VALUES('ii');
|
||||
INSERT INTO t4 VALUES('iii');
|
||||
|
||||
INSERT INTO t3 SELECT * FROM t4;
|
||||
SELECT * FROM t3;
|
||||
} {i ii iii}
|
||||
|
||||
finish_test
|
||||
|
125
test/without_rowid2.test
Normal file
125
test/without_rowid2.test
Normal file
@ -0,0 +1,125 @@
|
||||
# 2013-11-02
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing WITHOUT ROWID tables, and especially
|
||||
# FOREIGN KEY constraints.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable {!foreignkey} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Create a table and some data to work with.
|
||||
#
|
||||
do_test without_rowid2-1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(
|
||||
a INT PRIMARY KEY,
|
||||
b INT
|
||||
REFERENCES t1 ON DELETE CASCADE
|
||||
REFERENCES t2,
|
||||
c TEXT,
|
||||
FOREIGN KEY (b,c) REFERENCES t2(x,y) ON UPDATE CASCADE
|
||||
) WITHOUT rowid;
|
||||
}
|
||||
} {}
|
||||
do_test without_rowid2-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(
|
||||
x INT PRIMARY KEY,
|
||||
y TEXT
|
||||
) WITHOUT rowid;
|
||||
}
|
||||
} {}
|
||||
do_test without_rowid2-1.2 {
|
||||
execsql {
|
||||
CREATE TABLE t3(
|
||||
a INT REFERENCES t2,
|
||||
b INT REFERENCES t1,
|
||||
FOREIGN KEY (a,b) REFERENCES t2(x,y)
|
||||
);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test without_rowid2-2.1 {
|
||||
execsql {
|
||||
CREATE TABLE t4(a int primary key) WITHOUT rowid;
|
||||
CREATE TABLE t5(x references t4);
|
||||
CREATE TABLE t6(x references t4);
|
||||
CREATE TABLE t7(x references t4);
|
||||
CREATE TABLE t8(x references t4);
|
||||
CREATE TABLE t9(x references t4);
|
||||
CREATE TABLE t10(x references t4);
|
||||
DROP TABLE t7;
|
||||
DROP TABLE t9;
|
||||
DROP TABLE t5;
|
||||
DROP TABLE t8;
|
||||
DROP TABLE t6;
|
||||
DROP TABLE t10;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test without_rowid2-3.1 {
|
||||
execsql {
|
||||
CREATE TABLE t5(a PRIMARY KEY, b, c) WITHOUT rowid;
|
||||
CREATE TABLE t6(
|
||||
d REFERENCES t5,
|
||||
e REFERENCES t5(c)
|
||||
);
|
||||
PRAGMA foreign_key_list(t6);
|
||||
}
|
||||
} [concat \
|
||||
{0 0 t5 e c {NO ACTION} {NO ACTION} NONE} \
|
||||
{1 0 t5 d {} {NO ACTION} {NO ACTION} NONE} \
|
||||
]
|
||||
do_test without_rowid2-3.2 {
|
||||
execsql {
|
||||
CREATE TABLE t7(d, e, f,
|
||||
FOREIGN KEY (d, e) REFERENCES t5(a, b)
|
||||
);
|
||||
PRAGMA foreign_key_list(t7);
|
||||
}
|
||||
} [concat \
|
||||
{0 0 t5 d a {NO ACTION} {NO ACTION} NONE} \
|
||||
{0 1 t5 e b {NO ACTION} {NO ACTION} NONE} \
|
||||
]
|
||||
do_test without_rowid2-3.3 {
|
||||
execsql {
|
||||
CREATE TABLE t8(d, e, f,
|
||||
FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET NULL
|
||||
);
|
||||
PRAGMA foreign_key_list(t8);
|
||||
}
|
||||
} [concat \
|
||||
{0 0 t5 d {} {SET NULL} CASCADE NONE} \
|
||||
{0 1 t5 e {} {SET NULL} CASCADE NONE} \
|
||||
]
|
||||
do_test without_rowid2-3.4 {
|
||||
execsql {
|
||||
CREATE TABLE t9(d, e, f,
|
||||
FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET DEFAULT
|
||||
);
|
||||
PRAGMA foreign_key_list(t9);
|
||||
}
|
||||
} [concat \
|
||||
{0 0 t5 d {} {SET DEFAULT} CASCADE NONE} \
|
||||
{0 1 t5 e {} {SET DEFAULT} CASCADE NONE} \
|
||||
]
|
||||
do_test without_rowid2-3.5 {
|
||||
sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
|
||||
} {0 0 0}
|
||||
|
||||
finish_test
|
2084
test/without_rowid3.test
Normal file
2084
test/without_rowid3.test
Normal file
File diff suppressed because it is too large
Load Diff
764
test/without_rowid4.test
Normal file
764
test/without_rowid4.test
Normal file
@ -0,0 +1,764 @@
|
||||
# 2013-11-04
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Regression testing of FOR EACH ROW table triggers on WITHOUT ROWID
|
||||
# tables.
|
||||
#
|
||||
# 1. Trigger execution order tests.
|
||||
# These tests ensure that BEFORE and AFTER triggers are fired at the correct
|
||||
# times relative to each other and the triggering statement.
|
||||
#
|
||||
# without_rowid4-1.1.*: ON UPDATE trigger execution model.
|
||||
# without_rowid4-1.2.*: DELETE trigger execution model.
|
||||
# without_rowid4-1.3.*: INSERT trigger execution model.
|
||||
#
|
||||
# 2. Trigger program execution tests.
|
||||
# These tests ensure that trigger programs execute correctly (ie. that a
|
||||
# trigger program can correctly execute INSERT, UPDATE, DELETE * SELECT
|
||||
# statements, and combinations thereof).
|
||||
#
|
||||
# 3. Selective trigger execution
|
||||
# This tests that conditional triggers (ie. UPDATE OF triggers and triggers
|
||||
# with WHEN clauses) are fired only fired when they are supposed to be.
|
||||
#
|
||||
# without_rowid4-3.1: UPDATE OF triggers
|
||||
# without_rowid4-3.2: WHEN clause
|
||||
#
|
||||
# 4. Cascaded trigger execution
|
||||
# Tests that trigger-programs may cause other triggers to fire. Also that a
|
||||
# trigger-program is never executed recursively.
|
||||
#
|
||||
# without_rowid4-4.1: Trivial cascading trigger
|
||||
# without_rowid4-4.2: Trivial recursive trigger handling
|
||||
#
|
||||
# 5. Count changes behaviour.
|
||||
# Verify that rows altered by triggers are not included in the return value
|
||||
# of the "count changes" interface.
|
||||
#
|
||||
# 6. ON CONFLICT clause handling
|
||||
# without_rowid4-6.1[a-f]: INSERT statements
|
||||
# without_rowid4-6.2[a-f]: UPDATE statements
|
||||
#
|
||||
# 7. & 8. Triggers on views fire correctly.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable {!trigger} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# The tests in this file were written before SQLite supported recursive
|
||||
# trigger invocation, and some tests depend on that to pass. So disable
|
||||
# recursive triggers for this file.
|
||||
catchsql { pragma recursive_triggers = off }
|
||||
|
||||
# 1.
|
||||
ifcapable subquery {
|
||||
set ii 0
|
||||
set tbl_definitions [list \
|
||||
{CREATE TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;} \
|
||||
{CREATE TABLE tbl (a, b PRIMARY KEY) WITHOUT rowid;} \
|
||||
{CREATE TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
|
||||
CREATE INDEX tbl_idx ON tbl(b);} \
|
||||
]
|
||||
ifcapable tempdb {
|
||||
lappend tbl_definitions \
|
||||
{CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
|
||||
CREATE INDEX tbl_idx ON tbl(b);}
|
||||
lappend tbl_definitions \
|
||||
{CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid}
|
||||
lappend tbl_definitions \
|
||||
{CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;}
|
||||
}
|
||||
foreach tbl_defn $tbl_definitions {
|
||||
incr ii
|
||||
catchsql { DROP INDEX tbl_idx; }
|
||||
catchsql {
|
||||
DROP TABLE rlog;
|
||||
DROP TABLE clog;
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE other_tbl;
|
||||
}
|
||||
|
||||
execsql $tbl_defn
|
||||
|
||||
execsql {
|
||||
INSERT INTO tbl VALUES(1, 2);
|
||||
INSERT INTO tbl VALUES(3, 4);
|
||||
|
||||
CREATE TABLE rlog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
|
||||
CREATE TABLE clog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
|
||||
|
||||
CREATE TRIGGER before_update_row BEFORE UPDATE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
new.a, new.b);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER after_update_row AFTER UPDATE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
new.a, new.b);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER conditional_update_row AFTER UPDATE ON tbl FOR EACH ROW
|
||||
WHEN old.a = 1
|
||||
BEGIN
|
||||
INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog),
|
||||
old.a, old.b,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
new.a, new.b);
|
||||
END;
|
||||
}
|
||||
|
||||
do_test without_rowid4-1.$ii.1 {
|
||||
set r {}
|
||||
foreach v [execsql {
|
||||
UPDATE tbl SET a = a * 10, b = b * 10;
|
||||
SELECT * FROM rlog ORDER BY idx;
|
||||
SELECT * FROM clog ORDER BY idx;
|
||||
}] {
|
||||
lappend r [expr {int($v)}]
|
||||
}
|
||||
set r
|
||||
} [list 1 1 2 4 6 10 20 \
|
||||
2 1 2 13 24 10 20 \
|
||||
3 3 4 13 24 30 40 \
|
||||
4 3 4 40 60 30 40 \
|
||||
1 1 2 13 24 10 20 ]
|
||||
|
||||
execsql {
|
||||
DELETE FROM rlog;
|
||||
DELETE FROM tbl;
|
||||
INSERT INTO tbl VALUES (100, 100);
|
||||
INSERT INTO tbl VALUES (300, 200);
|
||||
CREATE TRIGGER delete_before_row BEFORE DELETE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
0, 0);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER delete_after_row AFTER DELETE ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
old.a, old.b,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
0, 0);
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-1.$ii.2 {
|
||||
set r {}
|
||||
foreach v [execsql {
|
||||
DELETE FROM tbl;
|
||||
SELECT * FROM rlog;
|
||||
}] {
|
||||
lappend r [expr {int($v)}]
|
||||
}
|
||||
set r
|
||||
} [list 1 100 100 400 300 0 0 \
|
||||
2 100 100 300 200 0 0 \
|
||||
3 300 200 300 200 0 0 \
|
||||
4 300 200 0 0 0 0 ]
|
||||
|
||||
execsql {
|
||||
DELETE FROM rlog;
|
||||
CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
0, 0,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
new.a, new.b);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER insert_after_row AFTER INSERT ON tbl FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog),
|
||||
0, 0,
|
||||
(SELECT coalesce(sum(a),0) FROM tbl),
|
||||
(SELECT coalesce(sum(b),0) FROM tbl),
|
||||
new.a, new.b);
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-1.$ii.3 {
|
||||
execsql {
|
||||
|
||||
CREATE TABLE other_tbl(a, b);
|
||||
INSERT INTO other_tbl VALUES(1, 2);
|
||||
INSERT INTO other_tbl VALUES(3, 4);
|
||||
-- INSERT INTO tbl SELECT * FROM other_tbl;
|
||||
INSERT INTO tbl VALUES(5, 6);
|
||||
DROP TABLE other_tbl;
|
||||
|
||||
SELECT * FROM rlog;
|
||||
}
|
||||
} [list 1 0 0 0 0 5 6 \
|
||||
2 0 0 5 6 5 6 ]
|
||||
|
||||
integrity_check without_rowid4-1.$ii.4
|
||||
}
|
||||
catchsql {
|
||||
DROP TABLE rlog;
|
||||
DROP TABLE clog;
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE other_tbl;
|
||||
}
|
||||
}
|
||||
|
||||
# 2.
|
||||
set ii 0
|
||||
foreach tr_program {
|
||||
{UPDATE tbl SET b = old.b;}
|
||||
{INSERT INTO log VALUES(new.c, 2, 3);}
|
||||
{DELETE FROM log WHERE a = 1;}
|
||||
{INSERT INTO tbl VALUES(500, new.b * 10, 700);
|
||||
UPDATE tbl SET c = old.c;
|
||||
DELETE FROM log;}
|
||||
{INSERT INTO log select * from tbl;}
|
||||
} {
|
||||
foreach test_varset [ list \
|
||||
{
|
||||
set statement {UPDATE tbl SET c = 10 WHERE a = 1;}
|
||||
set prep {INSERT INTO tbl VALUES(1, 2, 3);}
|
||||
set newC 10
|
||||
set newB 2
|
||||
set newA 1
|
||||
set oldA 1
|
||||
set oldB 2
|
||||
set oldC 3
|
||||
} \
|
||||
{
|
||||
set statement {DELETE FROM tbl WHERE a = 1;}
|
||||
set prep {INSERT INTO tbl VALUES(1, 2, 3);}
|
||||
set oldA 1
|
||||
set oldB 2
|
||||
set oldC 3
|
||||
} \
|
||||
{
|
||||
set statement {INSERT INTO tbl VALUES(1, 2, 3);}
|
||||
set newA 1
|
||||
set newB 2
|
||||
set newC 3
|
||||
}
|
||||
] \
|
||||
{
|
||||
set statement {}
|
||||
set prep {}
|
||||
set newA {''}
|
||||
set newB {''}
|
||||
set newC {''}
|
||||
set oldA {''}
|
||||
set oldB {''}
|
||||
set oldC {''}
|
||||
|
||||
incr ii
|
||||
|
||||
eval $test_varset
|
||||
|
||||
set statement_type [string range $statement 0 5]
|
||||
set tr_program_fixed $tr_program
|
||||
if {$statement_type == "DELETE"} {
|
||||
regsub -all new\.a $tr_program_fixed {''} tr_program_fixed
|
||||
regsub -all new\.b $tr_program_fixed {''} tr_program_fixed
|
||||
regsub -all new\.c $tr_program_fixed {''} tr_program_fixed
|
||||
}
|
||||
if {$statement_type == "INSERT"} {
|
||||
regsub -all old\.a $tr_program_fixed {''} tr_program_fixed
|
||||
regsub -all old\.b $tr_program_fixed {''} tr_program_fixed
|
||||
regsub -all old\.c $tr_program_fixed {''} tr_program_fixed
|
||||
}
|
||||
|
||||
|
||||
set tr_program_cooked $tr_program
|
||||
regsub -all new\.a $tr_program_cooked $newA tr_program_cooked
|
||||
regsub -all new\.b $tr_program_cooked $newB tr_program_cooked
|
||||
regsub -all new\.c $tr_program_cooked $newC tr_program_cooked
|
||||
regsub -all old\.a $tr_program_cooked $oldA tr_program_cooked
|
||||
regsub -all old\.b $tr_program_cooked $oldB tr_program_cooked
|
||||
regsub -all old\.c $tr_program_cooked $oldC tr_program_cooked
|
||||
|
||||
catchsql {
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE log;
|
||||
}
|
||||
|
||||
execsql {
|
||||
CREATE TABLE tbl(a PRIMARY KEY, b, c) WITHOUT rowid;
|
||||
CREATE TABLE log(a, b, c);
|
||||
}
|
||||
|
||||
set query {SELECT * FROM tbl; SELECT * FROM log;}
|
||||
set prep "$prep; INSERT INTO log VALUES(1, 2, 3);\
|
||||
INSERT INTO log VALUES(10, 20, 30);"
|
||||
|
||||
# Check execution of BEFORE programs:
|
||||
|
||||
set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]
|
||||
|
||||
execsql "DELETE FROM tbl; DELETE FROM log; $prep";
|
||||
execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6]\
|
||||
ON tbl BEGIN $tr_program_fixed END;"
|
||||
|
||||
do_test without_rowid4-2.$ii-before "execsql {$statement $query}" $before_data
|
||||
|
||||
execsql "DROP TRIGGER the_trigger;"
|
||||
execsql "DELETE FROM tbl; DELETE FROM log;"
|
||||
|
||||
# Check execution of AFTER programs
|
||||
set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]
|
||||
|
||||
execsql "DELETE FROM tbl; DELETE FROM log; $prep";
|
||||
execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\
|
||||
ON tbl BEGIN $tr_program_fixed END;"
|
||||
|
||||
do_test without_rowid4-2.$ii-after "execsql {$statement $query}" $after_data
|
||||
execsql "DROP TRIGGER the_trigger;"
|
||||
|
||||
integrity_check without_rowid4-2.$ii-integrity
|
||||
}
|
||||
}
|
||||
catchsql {
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE log;
|
||||
}
|
||||
|
||||
# 3.
|
||||
|
||||
# without_rowid4-3.1: UPDATE OF triggers
|
||||
execsql {
|
||||
CREATE TABLE tbl (a, b, c, d, PRIMARY KEY(a,b,c,d)) WITHOUT rowid;
|
||||
CREATE TABLE log (a);
|
||||
INSERT INTO log VALUES (0);
|
||||
INSERT INTO tbl VALUES (0, 0, 0, 0);
|
||||
INSERT INTO tbl VALUES (1, 0, 0, 0);
|
||||
CREATE TRIGGER tbl_after_update_cd BEFORE UPDATE OF c, d ON tbl
|
||||
BEGIN
|
||||
UPDATE log SET a = a + 1;
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-3.1 {
|
||||
execsql {
|
||||
UPDATE tbl SET b = 1, c = 10; -- 2
|
||||
UPDATE tbl SET b = 10; -- 0
|
||||
UPDATE tbl SET d = 4 WHERE a = 0; --1
|
||||
UPDATE tbl SET a = 4, b = 10; --0
|
||||
SELECT * FROM log;
|
||||
}
|
||||
} {3}
|
||||
execsql {
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE log;
|
||||
}
|
||||
|
||||
# without_rowid4-3.2: WHEN clause
|
||||
set when_triggers [list {t1 BEFORE INSERT ON tbl WHEN new.a > 20}]
|
||||
ifcapable subquery {
|
||||
lappend when_triggers \
|
||||
{t2 BEFORE INSERT ON tbl WHEN (SELECT count(*) FROM tbl) = 0}
|
||||
}
|
||||
|
||||
execsql {
|
||||
CREATE TABLE tbl (a, b, c, d);
|
||||
CREATE TABLE log (a);
|
||||
INSERT INTO log VALUES (0);
|
||||
}
|
||||
|
||||
foreach trig $when_triggers {
|
||||
execsql "CREATE TRIGGER $trig BEGIN UPDATE log set a = a + 1; END;"
|
||||
}
|
||||
|
||||
ifcapable subquery {
|
||||
set t232 {1 0 1}
|
||||
} else {
|
||||
set t232 {0 0 1}
|
||||
}
|
||||
do_test without_rowid4-3.2 {
|
||||
execsql {
|
||||
|
||||
INSERT INTO tbl VALUES(0, 0, 0, 0); -- 1 (ifcapable subquery)
|
||||
SELECT * FROM log;
|
||||
UPDATE log SET a = 0;
|
||||
|
||||
INSERT INTO tbl VALUES(0, 0, 0, 0); -- 0
|
||||
SELECT * FROM log;
|
||||
UPDATE log SET a = 0;
|
||||
|
||||
INSERT INTO tbl VALUES(200, 0, 0, 0); -- 1
|
||||
SELECT * FROM log;
|
||||
UPDATE log SET a = 0;
|
||||
}
|
||||
} $t232
|
||||
execsql {
|
||||
DROP TABLE tbl;
|
||||
DROP TABLE log;
|
||||
}
|
||||
integrity_check without_rowid4-3.3
|
||||
|
||||
# Simple cascaded trigger
|
||||
execsql {
|
||||
CREATE TABLE tblA(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE tblB(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE tblC(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
|
||||
CREATE TRIGGER tr1 BEFORE INSERT ON tblA BEGIN
|
||||
INSERT INTO tblB values(new.a, new.b);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER tr2 BEFORE INSERT ON tblB BEGIN
|
||||
INSERT INTO tblC values(new.a, new.b);
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-4.1 {
|
||||
execsql {
|
||||
INSERT INTO tblA values(1, 2);
|
||||
SELECT * FROM tblA;
|
||||
SELECT * FROM tblB;
|
||||
SELECT * FROM tblC;
|
||||
}
|
||||
} {1 2 1 2 1 2}
|
||||
execsql {
|
||||
DROP TABLE tblA;
|
||||
DROP TABLE tblB;
|
||||
DROP TABLE tblC;
|
||||
}
|
||||
|
||||
# Simple recursive trigger
|
||||
execsql {
|
||||
CREATE TABLE tbl(a, b, c, PRIMARY KEY(c,a,b)) WITHOUT rowid;
|
||||
CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl
|
||||
BEGIN
|
||||
INSERT INTO tbl VALUES (new.a, new.b, new.c+1);
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-4.2 {
|
||||
execsql {
|
||||
INSERT INTO tbl VALUES (1, 2, 3);
|
||||
select * from tbl;
|
||||
}
|
||||
} {1 2 3 1 2 4}
|
||||
execsql {
|
||||
DROP TABLE tbl;
|
||||
}
|
||||
|
||||
# 5.
|
||||
execsql {
|
||||
CREATE TABLE tbl(a, b, c, PRIMARY KEY(c,a,b)) WITHOUT rowid;
|
||||
CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl
|
||||
BEGIN
|
||||
INSERT INTO tbl VALUES (1, 2, 3);
|
||||
INSERT INTO tbl VALUES (2, 2, 3);
|
||||
UPDATE tbl set b = 10 WHERE a = 1;
|
||||
DELETE FROM tbl WHERE a = 1;
|
||||
DELETE FROM tbl;
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-5 {
|
||||
execsql {
|
||||
INSERT INTO tbl VALUES(100, 200, 300);
|
||||
}
|
||||
db changes
|
||||
} {1}
|
||||
execsql {
|
||||
DROP TABLE tbl;
|
||||
}
|
||||
|
||||
ifcapable conflict {
|
||||
# Handling of ON CONFLICT by INSERT statements inside triggers
|
||||
execsql {
|
||||
CREATE TABLE tbl (a PRIMARY KEY, b, c) WITHOUT rowid;
|
||||
CREATE TRIGGER ai_tbl AFTER INSERT ON tbl BEGIN
|
||||
INSERT OR IGNORE INTO tbl values (new.a, 0, 0);
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-6.1a {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO tbl values (1, 2, 3);
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test without_rowid4-6.1b {
|
||||
catchsql {
|
||||
INSERT OR ABORT INTO tbl values (2, 2, 3);
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.1c {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test without_rowid4-6.1d {
|
||||
catchsql {
|
||||
INSERT OR FAIL INTO tbl values (2, 2, 3);
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.1e {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 3 2 2 3}
|
||||
do_test without_rowid4-6.1f {
|
||||
execsql {
|
||||
INSERT OR REPLACE INTO tbl values (2, 2, 3);
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 3 2 0 0}
|
||||
do_test without_rowid4-6.1g {
|
||||
catchsql {
|
||||
INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.1h {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {}
|
||||
execsql {DELETE FROM tbl}
|
||||
|
||||
|
||||
# Handling of ON CONFLICT by UPDATE statements inside triggers
|
||||
execsql {
|
||||
INSERT INTO tbl values (4, 2, 3);
|
||||
INSERT INTO tbl values (6, 3, 4);
|
||||
CREATE TRIGGER au_tbl AFTER UPDATE ON tbl BEGIN
|
||||
UPDATE OR IGNORE tbl SET a = new.a, c = 10;
|
||||
END;
|
||||
}
|
||||
do_test without_rowid4-6.2a {
|
||||
execsql {
|
||||
BEGIN;
|
||||
UPDATE tbl SET a = 1 WHERE a = 4;
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 10 6 3 4}
|
||||
do_test without_rowid4-6.2b {
|
||||
catchsql {
|
||||
UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.2c {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 2 10 6 3 4}
|
||||
do_test without_rowid4-6.2d {
|
||||
catchsql {
|
||||
UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.2e {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {4 2 10 6 3 4}
|
||||
do_test without_rowid4-6.2f.1 {
|
||||
execsql {
|
||||
UPDATE OR REPLACE tbl SET a = 1 WHERE a = 4;
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {1 3 10}
|
||||
do_test without_rowid4-6.2f.2 {
|
||||
execsql {
|
||||
INSERT INTO tbl VALUES (2, 3, 4);
|
||||
SELECT * FROM tbl;
|
||||
}
|
||||
} {1 3 10 2 3 4}
|
||||
do_test without_rowid4-6.2g {
|
||||
catchsql {
|
||||
UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
|
||||
}
|
||||
} {1 {UNIQUE constraint failed: tbl.a}}
|
||||
do_test without_rowid4-6.2h {
|
||||
execsql {
|
||||
SELECT * from tbl;
|
||||
}
|
||||
} {4 2 3 6 3 4}
|
||||
execsql {
|
||||
DROP TABLE tbl;
|
||||
}
|
||||
} ; # ifcapable conflict
|
||||
|
||||
# 7. Triggers on views
|
||||
ifcapable view {
|
||||
|
||||
do_test without_rowid4-7.1 {
|
||||
execsql {
|
||||
CREATE TABLE ab(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
|
||||
CREATE TABLE cd(c, d, PRIMARY KEY(c,d)) WITHOUT rowid;
|
||||
INSERT INTO ab VALUES (1, 2);
|
||||
INSERT INTO ab VALUES (0, 0);
|
||||
INSERT INTO cd VALUES (3, 4);
|
||||
|
||||
CREATE TABLE tlog(ii INTEGER PRIMARY KEY,
|
||||
olda, oldb, oldc, oldd, newa, newb, newc, newd);
|
||||
|
||||
CREATE VIEW abcd AS SELECT a, b, c, d FROM ab, cd;
|
||||
|
||||
CREATE TRIGGER before_update INSTEAD OF UPDATE ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
|
||||
END;
|
||||
CREATE TRIGGER after_update INSTEAD OF UPDATE ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER before_delete INSTEAD OF DELETE ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
old.a, old.b, old.c, old.d, 0, 0, 0, 0);
|
||||
END;
|
||||
CREATE TRIGGER after_delete INSTEAD OF DELETE ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
old.a, old.b, old.c, old.d, 0, 0, 0, 0);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER before_insert INSTEAD OF INSERT ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
0, 0, 0, 0, new.a, new.b, new.c, new.d);
|
||||
END;
|
||||
CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
|
||||
INSERT INTO tlog VALUES(NULL,
|
||||
0, 0, 0, 0, new.a, new.b, new.c, new.d);
|
||||
END;
|
||||
}
|
||||
} {};
|
||||
|
||||
do_test without_rowid4-7.2 {
|
||||
execsql {
|
||||
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||
DELETE FROM abcd WHERE a = 1;
|
||||
INSERT INTO abcd VALUES(10, 20, 30, 40);
|
||||
SELECT * FROM tlog;
|
||||
}
|
||||
} [ list 1 1 2 3 4 100 25 3 4 \
|
||||
2 1 2 3 4 100 25 3 4 \
|
||||
3 1 2 3 4 0 0 0 0 \
|
||||
4 1 2 3 4 0 0 0 0 \
|
||||
5 0 0 0 0 10 20 30 40 \
|
||||
6 0 0 0 0 10 20 30 40 ]
|
||||
|
||||
do_test without_rowid4-7.3 {
|
||||
execsql {
|
||||
DELETE FROM tlog;
|
||||
INSERT INTO abcd VALUES(10, 20, 30, 40);
|
||||
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||
DELETE FROM abcd WHERE a = 1;
|
||||
SELECT * FROM tlog;
|
||||
}
|
||||
} [ list \
|
||||
1 0 0 0 0 10 20 30 40 \
|
||||
2 0 0 0 0 10 20 30 40 \
|
||||
3 1 2 3 4 100 25 3 4 \
|
||||
4 1 2 3 4 100 25 3 4 \
|
||||
5 1 2 3 4 0 0 0 0 \
|
||||
6 1 2 3 4 0 0 0 0 \
|
||||
]
|
||||
do_test without_rowid4-7.4 {
|
||||
execsql {
|
||||
DELETE FROM tlog;
|
||||
DELETE FROM abcd WHERE a = 1;
|
||||
INSERT INTO abcd VALUES(10, 20, 30, 40);
|
||||
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||
SELECT * FROM tlog;
|
||||
}
|
||||
} [ list \
|
||||
1 1 2 3 4 0 0 0 0 \
|
||||
2 1 2 3 4 0 0 0 0 \
|
||||
3 0 0 0 0 10 20 30 40 \
|
||||
4 0 0 0 0 10 20 30 40 \
|
||||
5 1 2 3 4 100 25 3 4 \
|
||||
6 1 2 3 4 100 25 3 4 \
|
||||
]
|
||||
|
||||
do_test without_rowid4-8.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b,c)) WITHOUT rowid;
|
||||
INSERT INTO t1 VALUES(1,2,3);
|
||||
CREATE VIEW v1 AS
|
||||
SELECT a+b AS x, b+c AS y, a+c AS z FROM t1;
|
||||
SELECT * FROM v1;
|
||||
}
|
||||
} {3 5 4}
|
||||
do_test without_rowid4-8.2 {
|
||||
execsql {
|
||||
CREATE TABLE v1log(a,b,c,d,e,f);
|
||||
CREATE TRIGGER r1 INSTEAD OF DELETE ON v1 BEGIN
|
||||
INSERT INTO v1log VALUES(OLD.x,NULL,OLD.y,NULL,OLD.z,NULL);
|
||||
END;
|
||||
DELETE FROM v1 WHERE x=1;
|
||||
SELECT * FROM v1log;
|
||||
}
|
||||
} {}
|
||||
do_test without_rowid4-8.3 {
|
||||
execsql {
|
||||
DELETE FROM v1 WHERE x=3;
|
||||
SELECT * FROM v1log;
|
||||
}
|
||||
} {3 {} 5 {} 4 {}}
|
||||
do_test without_rowid4-8.4 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(4,5,6);
|
||||
DELETE FROM v1log;
|
||||
DELETE FROM v1 WHERE y=11;
|
||||
SELECT * FROM v1log;
|
||||
}
|
||||
} {9 {} 11 {} 10 {}}
|
||||
do_test without_rowid4-8.5 {
|
||||
execsql {
|
||||
CREATE TRIGGER r2 INSTEAD OF INSERT ON v1 BEGIN
|
||||
INSERT INTO v1log VALUES(NULL,NEW.x,NULL,NEW.y,NULL,NEW.z);
|
||||
END;
|
||||
DELETE FROM v1log;
|
||||
INSERT INTO v1 VALUES(1,2,3);
|
||||
SELECT * FROM v1log;
|
||||
}
|
||||
} {{} 1 {} 2 {} 3}
|
||||
do_test without_rowid4-8.6 {
|
||||
execsql {
|
||||
CREATE TRIGGER r3 INSTEAD OF UPDATE ON v1 BEGIN
|
||||
INSERT INTO v1log VALUES(OLD.x,NEW.x,OLD.y,NEW.y,OLD.z,NEW.z);
|
||||
END;
|
||||
DELETE FROM v1log;
|
||||
UPDATE v1 SET x=x+100, y=y+200, z=z+300;
|
||||
SELECT * FROM v1log;
|
||||
}
|
||||
} {3 103 5 205 4 304 9 109 11 211 10 310}
|
||||
|
||||
# At one point the following was causing a segfault.
|
||||
do_test without_rowid4-9.1 {
|
||||
execsql {
|
||||
CREATE TABLE t3(a TEXT, b TEXT);
|
||||
CREATE VIEW v3 AS SELECT t3.a FROM t3;
|
||||
CREATE TRIGGER trig1 INSTEAD OF DELETE ON v3 BEGIN
|
||||
SELECT 1;
|
||||
END;
|
||||
DELETE FROM v3 WHERE a = 1;
|
||||
}
|
||||
} {}
|
||||
|
||||
} ;# ifcapable view
|
||||
|
||||
integrity_check without_rowid4-9.9
|
||||
|
||||
finish_test
|
@ -262,6 +262,7 @@ static Keyword aKeywordTable[] = {
|
||||
{ "VALUES", "TK_VALUES", ALWAYS },
|
||||
{ "VIEW", "TK_VIEW", VIEW },
|
||||
{ "VIRTUAL", "TK_VIRTUAL", VTAB },
|
||||
{ "WITHOUT", "TK_WITHOUT", ALWAYS },
|
||||
{ "WHEN", "TK_WHEN", ALWAYS },
|
||||
{ "WHERE", "TK_WHERE", ALWAYS },
|
||||
};
|
||||
|
@ -571,7 +571,7 @@ subreport {All tables} {NOT is_index} 0
|
||||
if {$nindex>0} {
|
||||
subreport {All indices} {is_index} 0
|
||||
}
|
||||
foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index
|
||||
foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
|
||||
ORDER BY name}] {
|
||||
set qn [quote $tbl]
|
||||
set name [string toupper $tbl]
|
||||
|
Loading…
x
Reference in New Issue
Block a user