mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
Allow a FROM clause in UPDATE statements.
FossilOrigin-Name: f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec
This commit is contained in:
32
manifest
32
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Yet\sanother\sattempt\sto\senhance\ssqlite3_load_extension()\sso\sthat\sit\sworks\nwith\sWindow-style\spathnames\susing\sa\sbackslash\sseparator\scharacter.
|
C Allow\sa\sFROM\sclause\sin\sUPDATE\sstatements.
|
||||||
D 2020-04-26T22:04:48.458
|
D 2020-04-27T20:55:33.061
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@@ -476,14 +476,14 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
|||||||
F src/btree.c 02376eb7d49ccf31b53c2504f045ad74687c142a5c15ca837516e59e737867dc
|
F src/btree.c 02376eb7d49ccf31b53c2504f045ad74687c142a5c15ca837516e59e737867dc
|
||||||
F src/btree.h 32672fa1aa74a7e9ab3aae822f94ffc8e732b1eb005988dc2283f91dc7573398
|
F src/btree.h 32672fa1aa74a7e9ab3aae822f94ffc8e732b1eb005988dc2283f91dc7573398
|
||||||
F src/btreeInt.h 887cdd2ea7f4a65143074a8a7c8928b0546f8c18dda3c06a408ce7992cbab0c0
|
F src/btreeInt.h 887cdd2ea7f4a65143074a8a7c8928b0546f8c18dda3c06a408ce7992cbab0c0
|
||||||
F src/build.c ec6c0bda1e43ef55e5f5121a77ba19fac51fc6585f95ce2da795bcedcf6e8f36
|
F src/build.c 8debc951e3f7e5152bbb7e6b2f26cad7b00a1db068c69af7db4aab136486e541
|
||||||
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
||||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||||
F src/ctime.c 6a77ec9e0eb87aea929e002c816298907e337094a7b556898ae2d1e6be209f90
|
F src/ctime.c 6a77ec9e0eb87aea929e002c816298907e337094a7b556898ae2d1e6be209f90
|
||||||
F src/date.c b29b349d277e3d579dcc295b24c0a2caed83fd8f090a9f7cbe6070c0fd662384
|
F src/date.c b29b349d277e3d579dcc295b24c0a2caed83fd8f090a9f7cbe6070c0fd662384
|
||||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||||
F src/dbstat.c 793deaf88a0904f88285d93d6713c636d55ede0ffd9f08d10f4ea825531d367f
|
F src/dbstat.c 793deaf88a0904f88285d93d6713c636d55ede0ffd9f08d10f4ea825531d367f
|
||||||
F src/delete.c 11000121c4281c0bce4e41db29addfaea0038eaa127ece02557c9207bc3e541d
|
F src/delete.c 6a4cbe008e8885eac5a0e0f9228ad716db92e3f9f2f0cb91a8ae276658d1f909
|
||||||
F src/expr.c d1e1d42cbdec08bb867a1ab43a59b401d82ff2bc88bdcb4af20e479a5facb6d8
|
F src/expr.c d1e1d42cbdec08bb867a1ab43a59b401d82ff2bc88bdcb4af20e479a5facb6d8
|
||||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||||
F src/fkey.c 4b575423b0a5d4898b1a7868ce985cf1a8ad91c741c9abbb108ff02536d20f41
|
F src/fkey.c 4b575423b0a5d4898b1a7868ce985cf1a8ad91c741c9abbb108ff02536d20f41
|
||||||
@@ -521,7 +521,7 @@ F src/os_win.c 035a813cbd17f355bdcad7ab894af214a9c13a1db8aeac902365350b98cd45a7
|
|||||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||||
F src/pager.c 52cee2f72710be47b5b13ff66b339ca3855e5bc48e92a94114d2affedc70041f
|
F src/pager.c 52cee2f72710be47b5b13ff66b339ca3855e5bc48e92a94114d2affedc70041f
|
||||||
F src/pager.h 3b33619a90180e0874c7eca31d6f6ceb464d9322c6fb4e9a7bbb318c8a17bdb3
|
F src/pager.h 3b33619a90180e0874c7eca31d6f6ceb464d9322c6fb4e9a7bbb318c8a17bdb3
|
||||||
F src/parse.y c8eff38606f443d5ba245263fa7abc05e4116d95656e050c4b78e9bfbf931add
|
F src/parse.y 5f2150bb4974e440924dfcc2e33cea7cf1895492b917464572efd258b0eab267
|
||||||
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
||||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||||
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
||||||
@@ -532,12 +532,12 @@ F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
|
|||||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||||
F src/resolve.c d36a2b1639e1c33d7b508abfd3452a63e7fd81737f6f3940bfef085fca6f21f4
|
F src/resolve.c d36a2b1639e1c33d7b508abfd3452a63e7fd81737f6f3940bfef085fca6f21f4
|
||||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||||
F src/select.c c310de94bf67315054587c18a16e7a3e3dc3a98dc79168f0c2b776548d43f6cd
|
F src/select.c 7e56a58673d027ab7951559adfda752192baff7c6083a88e4dd8db3c84e465e8
|
||||||
F src/shell.c.in 1fc834b80c72dd37587ea87a4f4167cf5e6d98d12d143184ed2e732f529c0950
|
F src/shell.c.in 1fc834b80c72dd37587ea87a4f4167cf5e6d98d12d143184ed2e732f529c0950
|
||||||
F src/sqlite.h.in fd6fcfe173accab8d9cb9a843856d9e9fb475f893b60a455e01d8739b5076f0e
|
F src/sqlite.h.in fd6fcfe173accab8d9cb9a843856d9e9fb475f893b60a455e01d8739b5076f0e
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
|
F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
|
||||||
F src/sqliteInt.h 0f3848c46310d197246003f052985b72d1cdbfc0b31e069db76cb5231062fa1d
|
F src/sqliteInt.h bfed03b21bfa8fade8887a12d5bc0f5a349e98105aec675b9c1e027e9fd66f67
|
||||||
F src/sqliteLimit.h 95cb8479ca459496d9c1c6a9f76b38aee12203a56ce1092fe13e50ae2454c032
|
F src/sqliteLimit.h 95cb8479ca459496d9c1c6a9f76b38aee12203a56ce1092fe13e50ae2454c032
|
||||||
F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
|
F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
|
||||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||||
@@ -599,7 +599,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
|||||||
F src/tokenize.c eee7bae3ec0bc4abee951554bf46a8ba567c0f7752ac90c820ed8afff4c612dc
|
F src/tokenize.c eee7bae3ec0bc4abee951554bf46a8ba567c0f7752ac90c820ed8afff4c612dc
|
||||||
F src/treeview.c 82c6391a3ba76215d4185fd4719a56ec4caf186a40c8a7b6e6ba4ae4467c2742
|
F src/treeview.c 82c6391a3ba76215d4185fd4719a56ec4caf186a40c8a7b6e6ba4ae4467c2742
|
||||||
F src/trigger.c 4ada1037cc99777f647a882cdacbd1a4deb6567b69daf02946286401b88cdc04
|
F src/trigger.c 4ada1037cc99777f647a882cdacbd1a4deb6567b69daf02946286401b88cdc04
|
||||||
F src/update.c 3eb778c42155d944377a4ee5e440b04520f07094804ed6ce63d2528f619614d9
|
F src/update.c 72aae4f6198aca8290c1368f26f6f8b7d29e23d0d2bfbd4f773eaa8d9a9380a4
|
||||||
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
||||||
F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
|
F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
|
||||||
F src/util.c 3b6cedf7a0c69bd6e1acce832873952d416212d6293b18d03064e07d7a9b5118
|
F src/util.c 3b6cedf7a0c69bd6e1acce832873952d416212d6293b18d03064e07d7a9b5118
|
||||||
@@ -1003,6 +1003,7 @@ F test/fts4record.test a48508f69a84c9287c8019d3a1ae712f5730d8335ffaf8e2101e691d0
|
|||||||
F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
|
F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
|
||||||
F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
|
F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
|
||||||
F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95
|
F test/fts4unicode.test ceca76422abc251818cb25dabe33d3c3970da5f7c90e1540f190824e6b3a7c95
|
||||||
|
F test/fts4upfrom.test 04ef3b150370e0083cf7c721928b79163b70d930f74ebc4b91bc382b8123f659
|
||||||
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
||||||
F test/func.test f673822636fb8ed618dd2b80230d16e495d19c8f2e2e7d6c22e93e2b3de097ad
|
F test/func.test f673822636fb8ed618dd2b80230d16e495d19c8f2e2e7d6c22e93e2b3de097ad
|
||||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||||
@@ -1234,7 +1235,7 @@ F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
|||||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||||
F test/permutations.test c83339862d72b6272f957905205f874e6eefdbad2823380452c4f0128fd3d906
|
F test/permutations.test c83339862d72b6272f957905205f874e6eefdbad2823380452c4f0128fd3d906
|
||||||
F test/pg_common.tcl 222a1bad1c41c308fa366313cd7b51b3be7e9b21c8736a421b974ac941693b54
|
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
|
||||||
F test/pragma.test 59becdfd720b80d463ab750f69f7118fde10dfd556aa5d554f3bf6b7e5ea7533
|
F test/pragma.test 59becdfd720b80d463ab750f69f7118fde10dfd556aa5d554f3bf6b7e5ea7533
|
||||||
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
|
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
|
||||||
F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31
|
F test/pragma3.test 92a46bbea12322dd94a404f49edcfbfc913a2c98115f0d030a7459bb4712ef31
|
||||||
@@ -1615,6 +1616,8 @@ F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
|
|||||||
F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
|
F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
|
||||||
F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f847310d
|
F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f847310d
|
||||||
F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
|
F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
|
||||||
|
F test/upfrom1.tcl 62efddee869b3a6f3e076b2816793fec9422e38d10ea42b63da733cdd2b1ad8e
|
||||||
|
F test/upfrom1.test 543389b4eef43c7a21079df018cf95e29d7c2a4efd09b2597e54a03bbdbd30b9
|
||||||
F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
|
F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
|
||||||
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
|
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
|
||||||
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
|
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
|
||||||
@@ -1861,7 +1864,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 57b16d8ca3d1ede3b411389256bec6686433aae716f47bca309ee7c8e5fe3128
|
P b73d9a7d6f7fec0ffc9640902a849289c305f8651e891388c01255c4da7a6c4b
|
||||||
R cae58a18aebbf7b95fb40ba841d77f19
|
R bbc1e04364e396384c0537dbe66bc52f
|
||||||
U drh
|
T *branch * update-from
|
||||||
Z a013006b51e7e5f259533e9a3800742b
|
T *sym-update-from *
|
||||||
|
T -sym-trunk *
|
||||||
|
U dan
|
||||||
|
Z 657e2dd1dadbea443cdb52f63d6f9883
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
b73d9a7d6f7fec0ffc9640902a849289c305f8651e891388c01255c4da7a6c4b
|
f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec
|
||||||
20
src/build.c
20
src/build.c
@@ -4495,6 +4495,26 @@ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Append the contents of SrcList p2 to SrcList p1 and return the resulting
|
||||||
|
** SrcList. Or, if an error occurs, return NULL. In all cases, p1 and p2
|
||||||
|
** are deleted by this function.
|
||||||
|
*/
|
||||||
|
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
|
||||||
|
if( p2 && p1 ){
|
||||||
|
assert( p1->nSrc==1 );
|
||||||
|
p1 = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, p1->nSrc);
|
||||||
|
if( p1 ){
|
||||||
|
assert( p1->nSrc==1+p2->nSrc );
|
||||||
|
memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(struct SrcList_item));
|
||||||
|
sqlite3_free(p2);
|
||||||
|
}else{
|
||||||
|
sqlite3SrcListDelete(pParse->db, p2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Add the list of function arguments to the SrcList entry for a
|
** Add the list of function arguments to the SrcList entry for a
|
||||||
** table-valued-function.
|
** table-valued-function.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||||
struct SrcList_item *pItem = pSrc->a;
|
struct SrcList_item *pItem = pSrc->a;
|
||||||
Table *pTab;
|
Table *pTab;
|
||||||
assert( pItem && pSrc->nSrc==1 );
|
assert( pItem && pSrc->nSrc>=1 );
|
||||||
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
|
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
|
||||||
sqlite3DeleteTable(pParse->db, pItem->pTab);
|
sqlite3DeleteTable(pParse->db, pItem->pTab);
|
||||||
pItem->pTab = pTab;
|
pItem->pTab = pTab;
|
||||||
|
|||||||
@@ -637,7 +637,7 @@ as(X) ::= . {X.n = 0; X.z = 0;}
|
|||||||
|
|
||||||
// A complete FROM clause.
|
// A complete FROM clause.
|
||||||
//
|
//
|
||||||
from(A) ::= . {A = sqlite3DbMallocZero(pParse->db, sizeof(*A));}
|
from(A) ::= . {A = 0;}
|
||||||
from(A) ::= FROM seltablist(X). {
|
from(A) ::= FROM seltablist(X). {
|
||||||
A = X;
|
A = X;
|
||||||
sqlite3SrcListShiftJoinType(A);
|
sqlite3SrcListShiftJoinType(A);
|
||||||
@@ -867,18 +867,20 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
|
|||||||
////////////////////////// The UPDATE command ////////////////////////////////
|
////////////////////////// The UPDATE command ////////////////////////////////
|
||||||
//
|
//
|
||||||
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y)
|
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||||
where_opt(W) orderby_opt(O) limit_opt(L). {
|
where_opt(W) orderby_opt(O) limit_opt(L). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
|
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||||
sqlite3Update(pParse,X,Y,W,R,O,L,0);
|
sqlite3Update(pParse,X,Y,W,R,O,L,0);
|
||||||
}
|
}
|
||||||
%endif
|
%endif
|
||||||
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y)
|
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||||
where_opt(W). {
|
where_opt(W). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||||
|
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||||
sqlite3Update(pParse,X,Y,W,R,0,0,0);
|
sqlite3Update(pParse,X,Y,W,R,0,0,0);
|
||||||
}
|
}
|
||||||
%endif
|
%endif
|
||||||
|
|||||||
20
src/select.c
20
src/select.c
@@ -1154,11 +1154,11 @@ static void selectInnerLoop(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_SUBQUERY
|
|
||||||
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
|
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
|
||||||
** then there should be a single item on the stack. Write this
|
** then there should be a single item on the stack. Write this
|
||||||
** item into the set table with bogus data.
|
** item into the set table with bogus data.
|
||||||
*/
|
*/
|
||||||
|
case SRT_ISet:
|
||||||
case SRT_Set: {
|
case SRT_Set: {
|
||||||
if( pSort ){
|
if( pSort ){
|
||||||
/* At first glance you would think we could optimize out the
|
/* At first glance you would think we could optimize out the
|
||||||
@@ -1168,16 +1168,22 @@ static void selectInnerLoop(
|
|||||||
pushOntoSorter(
|
pushOntoSorter(
|
||||||
pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
|
pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
|
||||||
}else{
|
}else{
|
||||||
|
int bITab = (eDest==SRT_ISet);
|
||||||
int r1 = sqlite3GetTempReg(pParse);
|
int r1 = sqlite3GetTempReg(pParse);
|
||||||
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult+bITab, nResultCol-bITab,
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
|
r1, pDest->zAffSdst, 0
|
||||||
r1, pDest->zAffSdst, nResultCol);
|
);
|
||||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol);
|
if( bITab ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1,regResult,nResultCol);
|
||||||
|
}
|
||||||
sqlite3ReleaseTempReg(pParse, r1);
|
sqlite3ReleaseTempReg(pParse, r1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
/* If any row exist in the result set, record that fact and abort.
|
/* If any row exist in the result set, record that fact and abort.
|
||||||
*/
|
*/
|
||||||
case SRT_Exists: {
|
case SRT_Exists: {
|
||||||
@@ -4981,8 +4987,8 @@ static int selectExpander(Walker *pWalker, Select *p){
|
|||||||
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
|
||||||
Table *pTab;
|
Table *pTab;
|
||||||
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
|
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
|
||||||
if( pFrom->fg.isRecursive ) continue;
|
if( pFrom->pTab ) continue;
|
||||||
assert( pFrom->pTab==0 );
|
assert( pFrom->fg.isRecursive==0 );
|
||||||
#ifndef SQLITE_OMIT_CTE
|
#ifndef SQLITE_OMIT_CTE
|
||||||
if( withExpand(pWalker, pFrom) ) return WRC_Abort;
|
if( withExpand(pWalker, pFrom) ) return WRC_Abort;
|
||||||
if( pFrom->pTab ) {} else
|
if( pFrom->pTab ) {} else
|
||||||
|
|||||||
@@ -3187,6 +3187,7 @@ struct Select {
|
|||||||
#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
|
#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
|
||||||
#define SRT_Coroutine 13 /* Generate a single row of result */
|
#define SRT_Coroutine 13 /* Generate a single row of result */
|
||||||
#define SRT_Table 14 /* Store result as data with an automatic rowid */
|
#define SRT_Table 14 /* Store result as data with an automatic rowid */
|
||||||
|
#define SRT_ISet 15 /* Store result as data with rowid */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** An instance of this object describes where to put of the results of
|
** An instance of this object describes where to put of the results of
|
||||||
@@ -4204,6 +4205,7 @@ void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
|
|||||||
IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
|
IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
|
||||||
int sqlite3IdListIndex(IdList*,const char*);
|
int sqlite3IdListIndex(IdList*,const char*);
|
||||||
SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int);
|
SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int);
|
||||||
|
SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2);
|
||||||
SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*);
|
SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*);
|
||||||
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
|
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
|
||||||
Token*, Select*, Expr*, IdList*);
|
Token*, Select*, Expr*, IdList*);
|
||||||
|
|||||||
514
src/update.c
514
src/update.c
@@ -130,6 +130,93 @@ static int indexWhereClauseMightChange(
|
|||||||
aXRef, chngRowid);
|
aXRef, chngRowid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function generates VM code to run the query:
|
||||||
|
**
|
||||||
|
** SELECT <other-columns>, pChanges FROM pTabList WHERE pWhere
|
||||||
|
**
|
||||||
|
** and write the results to the ephemeral table already opened as cursor
|
||||||
|
** iEph. None of pChanges, pTabList or pWhere are modified or consumed by
|
||||||
|
** this function, they must be deleted by the caller.
|
||||||
|
**
|
||||||
|
** Exactly how results are written to table iEph, and exactly what
|
||||||
|
** the <other-columns> in the query above are is determined by the type
|
||||||
|
** of table pTabList->a[0].pTab.
|
||||||
|
**
|
||||||
|
** If the table is a WITHOUT ROWID table, then argument pPk must be its
|
||||||
|
** PRIMARY KEY. In this case <other-columns> are the primary key columns
|
||||||
|
** of the table, in order. The results of the query are written to ephemeral
|
||||||
|
** table iEph as index keys, using OP_IdxInsert.
|
||||||
|
**
|
||||||
|
** If the table is actually a view, then <other-columns> are all columns of
|
||||||
|
** the view. The results are written to the ephemeral table iEph as records
|
||||||
|
** with automatically assigned integer keys.
|
||||||
|
**
|
||||||
|
** If the table is a virtual or ordinary intkey table, then <other-columns>
|
||||||
|
** is its rowid. For a virtual table, the results are written to iEph as
|
||||||
|
** records with automatically assigned integer keys For intkey tables, the
|
||||||
|
** rowid value in <other-columns> is used as the integer key, and the
|
||||||
|
** remaining fields make up the table record.
|
||||||
|
*/
|
||||||
|
static void updatePopulateEphTable(
|
||||||
|
Parse *pParse, /* Parse context */
|
||||||
|
int iEph, /* Cursor for open eph. table */
|
||||||
|
Index *pPk, /* PK if table 0 is WITHOUT ROWID */
|
||||||
|
ExprList *pChanges, /* List of expressions to return */
|
||||||
|
SrcList *pTabList, /* List of tables to select from */
|
||||||
|
Expr *pWhere /* WHERE clause for query */
|
||||||
|
){
|
||||||
|
int i;
|
||||||
|
sqlite3 *db = pParse->db;
|
||||||
|
SelectDest dest;
|
||||||
|
Select *pSelect = 0;
|
||||||
|
ExprList *pList = 0;
|
||||||
|
Table *pTab = pTabList->a[0].pTab;
|
||||||
|
SrcList *pSrc = sqlite3SrcListDup(db, pTabList, 0);
|
||||||
|
Expr *pWhere2 = sqlite3ExprDup(db, pWhere, 0);
|
||||||
|
int eDest;
|
||||||
|
|
||||||
|
assert( pTabList->nSrc>1 );
|
||||||
|
if( pSrc ){
|
||||||
|
pSrc->a[0].iCursor = -1;
|
||||||
|
pSrc->a[0].pTab->nTabRef--;
|
||||||
|
pSrc->a[0].pTab = 0;
|
||||||
|
}
|
||||||
|
if( pPk ){
|
||||||
|
for(i=0; i<pPk->nKeyCol; i++){
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3PExpr(pParse, TK_DOT,
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
eDest = SRT_Set;
|
||||||
|
}else if( pTab->pSelect ){
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3PExpr(pParse, TK_DOT,
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||||
|
sqlite3PExpr(pParse, TK_ASTERISK, 0, 0)
|
||||||
|
));
|
||||||
|
eDest = SRT_Table;
|
||||||
|
}else{
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3PExpr(pParse, TK_DOT,
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||||
|
sqlite3Expr(db, TK_ID, "_rowid_")
|
||||||
|
));
|
||||||
|
eDest = IsVirtual(pTab) ? SRT_Table : SRT_ISet;
|
||||||
|
}
|
||||||
|
for(i=0; i<pChanges->nExpr; i++){
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3ExprDup(db, pChanges->a[i].pExpr, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pSelect = sqlite3SelectNew(pParse, pList, pSrc, pWhere2, 0, 0, 0, 0, 0);
|
||||||
|
sqlite3SelectDestInit(&dest, eDest, iEph);
|
||||||
|
sqlite3Select(pParse, pSelect, &dest);
|
||||||
|
sqlite3SelectDelete(db, pSelect);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Process an UPDATE statement.
|
** Process an UPDATE statement.
|
||||||
**
|
**
|
||||||
@@ -192,6 +279,7 @@ void sqlite3Update(
|
|||||||
i16 nPk = 0; /* Number of components of the PRIMARY KEY */
|
i16 nPk = 0; /* Number of components of the PRIMARY KEY */
|
||||||
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
||||||
int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */
|
int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */
|
||||||
|
int nChangeFrom = 0;
|
||||||
|
|
||||||
/* Register Allocations */
|
/* Register Allocations */
|
||||||
int regRowCount = 0; /* A count of rows changed */
|
int regRowCount = 0; /* A count of rows changed */
|
||||||
@@ -207,7 +295,6 @@ void sqlite3Update(
|
|||||||
if( pParse->nErr || db->mallocFailed ){
|
if( pParse->nErr || db->mallocFailed ){
|
||||||
goto update_cleanup;
|
goto update_cleanup;
|
||||||
}
|
}
|
||||||
assert( pTabList->nSrc==1 );
|
|
||||||
|
|
||||||
/* Locate the table which we want to update.
|
/* Locate the table which we want to update.
|
||||||
*/
|
*/
|
||||||
@@ -231,6 +318,8 @@ void sqlite3Update(
|
|||||||
# undef isView
|
# undef isView
|
||||||
# define isView 0
|
# define isView 0
|
||||||
#endif
|
#endif
|
||||||
|
nChangeFrom = (pTabList->nSrc>1) ? pChanges->nExpr : 0;
|
||||||
|
assert( nChangeFrom==0 || pUpsert==0 );
|
||||||
|
|
||||||
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||||
if( !isView ){
|
if( !isView ){
|
||||||
@@ -302,7 +391,9 @@ void sqlite3Update(
|
|||||||
*/
|
*/
|
||||||
chngRowid = chngPk = 0;
|
chngRowid = chngPk = 0;
|
||||||
for(i=0; i<pChanges->nExpr; i++){
|
for(i=0; i<pChanges->nExpr; i++){
|
||||||
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
|
/* If this is an UPDATE with a FROM clause, do not resolve expressions
|
||||||
|
** here. The call to sqlite3Select() below will do that. */
|
||||||
|
if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
|
||||||
goto update_cleanup;
|
goto update_cleanup;
|
||||||
}
|
}
|
||||||
for(j=0; j<pTab->nCol; j++){
|
for(j=0; j<pTab->nCol; j++){
|
||||||
@@ -461,7 +552,7 @@ void sqlite3Update(
|
|||||||
** an ephemeral table.
|
** an ephemeral table.
|
||||||
*/
|
*/
|
||||||
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
|
||||||
if( isView ){
|
if( nChangeFrom==0 && isView ){
|
||||||
sqlite3MaterializeView(pParse, pTab,
|
sqlite3MaterializeView(pParse, pTab,
|
||||||
pWhere, pOrderBy, pLimit, iDataCur
|
pWhere, pOrderBy, pLimit, iDataCur
|
||||||
);
|
);
|
||||||
@@ -473,7 +564,7 @@ void sqlite3Update(
|
|||||||
/* Resolve the column names in all the expressions in the
|
/* Resolve the column names in all the expressions in the
|
||||||
** WHERE clause.
|
** WHERE clause.
|
||||||
*/
|
*/
|
||||||
if( sqlite3ResolveExprNames(&sNC, pWhere) ){
|
if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pWhere) ){
|
||||||
goto update_cleanup;
|
goto update_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,105 +591,118 @@ void sqlite3Update(
|
|||||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( HasRowid(pTab) ){
|
if( nChangeFrom==0 && HasRowid(pTab) ){
|
||||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||||
}else{
|
}else{
|
||||||
assert( pPk!=0 );
|
assert( pPk!=0 || HasRowid(pTab) );
|
||||||
nPk = pPk->nKeyCol;
|
nPk = pPk ? pPk->nKeyCol : 0;
|
||||||
iPk = pParse->nMem+1;
|
iPk = pParse->nMem+1;
|
||||||
pParse->nMem += nPk;
|
pParse->nMem += nPk;
|
||||||
|
pParse->nMem += nChangeFrom;
|
||||||
regKey = ++pParse->nMem;
|
regKey = ++pParse->nMem;
|
||||||
if( pUpsert==0 ){
|
if( pUpsert==0 ){
|
||||||
|
int nEphCol = nPk + nChangeFrom + (isView ? pTab->nCol : 0);
|
||||||
iEph = pParse->nTab++;
|
iEph = pParse->nTab++;
|
||||||
sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
|
if( pPk ) sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
|
||||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nEphCol);
|
||||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
if( pPk ) sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||||
}
|
if( nChangeFrom ){
|
||||||
}
|
updatePopulateEphTable(pParse, iEph, pPk, pChanges, pTabList, pWhere);
|
||||||
|
#ifndef SQLITE_OMIT_SUBQUERY
|
||||||
if( pUpsert ){
|
if( isView ) iDataCur = iEph;
|
||||||
/* If this is an UPSERT, then all cursors have already been opened by
|
#endif
|
||||||
** the outer INSERT and the data cursor should be pointing at the row
|
|
||||||
** that is to be updated. So bypass the code that searches for the
|
|
||||||
** row(s) to be updated.
|
|
||||||
*/
|
|
||||||
pWInfo = 0;
|
|
||||||
eOnePass = ONEPASS_SINGLE;
|
|
||||||
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
|
|
||||||
bFinishSeek = 0;
|
|
||||||
}else{
|
|
||||||
/* Begin the database scan.
|
|
||||||
**
|
|
||||||
** Do not consider a single-pass strategy for a multi-row update if
|
|
||||||
** there are any triggers or foreign keys to process, or rows may
|
|
||||||
** be deleted as a result of REPLACE conflict handling. Any of these
|
|
||||||
** things might disturb a cursor being used to scan through the table
|
|
||||||
** or index, causing a single-pass approach to malfunction. */
|
|
||||||
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
|
||||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
|
||||||
flags |= WHERE_ONEPASS_MULTIROW;
|
|
||||||
}
|
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
|
||||||
if( pWInfo==0 ) goto update_cleanup;
|
|
||||||
|
|
||||||
/* A one-pass strategy that might update more than one row may not
|
|
||||||
** be used if any column of the index used for the scan is being
|
|
||||||
** updated. Otherwise, if there is an index on "b", statements like
|
|
||||||
** the following could create an infinite loop:
|
|
||||||
**
|
|
||||||
** UPDATE t1 SET b=b+1 WHERE b>?
|
|
||||||
**
|
|
||||||
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
|
||||||
** strategy that uses an index for which one or more columns are being
|
|
||||||
** updated. */
|
|
||||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
|
||||||
bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo);
|
|
||||||
if( eOnePass!=ONEPASS_SINGLE ){
|
|
||||||
sqlite3MultiWrite(pParse);
|
|
||||||
if( eOnePass==ONEPASS_MULTI ){
|
|
||||||
int iCur = aiCurOnePass[1];
|
|
||||||
if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
|
||||||
eOnePass = ONEPASS_OFF;
|
|
||||||
}
|
|
||||||
assert( iCur!=iDataCur || !HasRowid(pTab) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( HasRowid(pTab) ){
|
if( nChangeFrom ){
|
||||||
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
sqlite3MultiWrite(pParse);
|
||||||
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
eOnePass = ONEPASS_OFF;
|
||||||
** leave it in register regOldRowid. */
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
|
||||||
if( eOnePass==ONEPASS_OFF ){
|
|
||||||
/* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */
|
|
||||||
aRegIdx[nAllIdx] = ++pParse->nMem;
|
|
||||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
/* Read the PK of the current row into an array of registers. In
|
if( pUpsert ){
|
||||||
** ONEPASS_OFF mode, serialize the array into a record and store it in
|
/* If this is an UPSERT, then all cursors have already been opened by
|
||||||
** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
|
** the outer INSERT and the data cursor should be pointing at the row
|
||||||
** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
|
** that is to be updated. So bypass the code that searches for the
|
||||||
** is not required) and leave the PK fields in the array of registers. */
|
** row(s) to be updated.
|
||||||
for(i=0; i<nPk; i++){
|
*/
|
||||||
assert( pPk->aiColumn[i]>=0 );
|
pWInfo = 0;
|
||||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,
|
eOnePass = ONEPASS_SINGLE;
|
||||||
pPk->aiColumn[i], iPk+i);
|
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
|
||||||
}
|
bFinishSeek = 0;
|
||||||
if( eOnePass ){
|
|
||||||
if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
|
|
||||||
nKey = nPk;
|
|
||||||
regKey = iPk;
|
|
||||||
}else{
|
}else{
|
||||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
|
/* Begin the database scan.
|
||||||
sqlite3IndexAffinityStr(db, pPk), nPk);
|
**
|
||||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
|
** Do not consider a single-pass strategy for a multi-row update if
|
||||||
|
** there are any triggers or foreign keys to process, or rows may
|
||||||
|
** be deleted as a result of REPLACE conflict handling. Any of these
|
||||||
|
** things might disturb a cursor being used to scan through the table
|
||||||
|
** or index, causing a single-pass approach to malfunction. */
|
||||||
|
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
||||||
|
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||||
|
flags |= WHERE_ONEPASS_MULTIROW;
|
||||||
|
}
|
||||||
|
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags,iIdxCur);
|
||||||
|
if( pWInfo==0 ) goto update_cleanup;
|
||||||
|
|
||||||
|
/* A one-pass strategy that might update more than one row may not
|
||||||
|
** be used if any column of the index used for the scan is being
|
||||||
|
** updated. Otherwise, if there is an index on "b", statements like
|
||||||
|
** the following could create an infinite loop:
|
||||||
|
**
|
||||||
|
** UPDATE t1 SET b=b+1 WHERE b>?
|
||||||
|
**
|
||||||
|
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
||||||
|
** strategy that uses an index for which one or more columns are being
|
||||||
|
** updated. */
|
||||||
|
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||||
|
bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo);
|
||||||
|
if( eOnePass!=ONEPASS_SINGLE ){
|
||||||
|
sqlite3MultiWrite(pParse);
|
||||||
|
if( eOnePass==ONEPASS_MULTI ){
|
||||||
|
int iCur = aiCurOnePass[1];
|
||||||
|
if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
||||||
|
eOnePass = ONEPASS_OFF;
|
||||||
|
}
|
||||||
|
assert( iCur!=iDataCur || !HasRowid(pTab) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( HasRowid(pTab) ){
|
||||||
|
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
||||||
|
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
||||||
|
** leave it in register regOldRowid. */
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||||
|
if( eOnePass==ONEPASS_OFF ){
|
||||||
|
/* We need to use regRowSet, so reallocate aRegIdx[nAllIdx] */
|
||||||
|
aRegIdx[nAllIdx] = ++pParse->nMem;
|
||||||
|
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
/* Read the PK of the current row into an array of registers. In
|
||||||
|
** ONEPASS_OFF mode, serialize the array into a record and store it in
|
||||||
|
** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
|
||||||
|
** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
|
||||||
|
** is not required) and leave the PK fields in the array of registers. */
|
||||||
|
for(i=0; i<nPk; i++){
|
||||||
|
assert( pPk->aiColumn[i]>=0 );
|
||||||
|
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,
|
||||||
|
pPk->aiColumn[i], iPk+i);
|
||||||
|
}
|
||||||
|
if( eOnePass ){
|
||||||
|
if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||||
|
nKey = nPk;
|
||||||
|
regKey = iPk;
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
|
||||||
|
sqlite3IndexAffinityStr(db, pPk), nPk);
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( pUpsert==0 ){
|
if( pUpsert==0 ){
|
||||||
if( eOnePass!=ONEPASS_MULTI ){
|
if( nChangeFrom==0 && eOnePass!=ONEPASS_MULTI ){
|
||||||
sqlite3WhereEnd(pWInfo);
|
sqlite3WhereEnd(pWInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,12 +738,32 @@ void sqlite3Update(
|
|||||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||||
VdbeCoverageIf(v, pPk==0);
|
VdbeCoverageIf(v, pPk==0);
|
||||||
VdbeCoverageIf(v, pPk!=0);
|
VdbeCoverageIf(v, pPk!=0);
|
||||||
}else if( pPk ){
|
}else if( pPk || nChangeFrom ){
|
||||||
labelContinue = sqlite3VdbeMakeLabel(pParse);
|
labelContinue = sqlite3VdbeMakeLabel(pParse);
|
||||||
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
|
||||||
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
addrTop = sqlite3VdbeCurrentAddr(v);
|
||||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
|
if( nChangeFrom ){
|
||||||
VdbeCoverage(v);
|
if( !isView ){
|
||||||
|
if( pPk ){
|
||||||
|
for(i=0; i<nPk; i++){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, iEph, i, iPk+i);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp4Int(
|
||||||
|
v, OP_NotFound, iDataCur, labelContinue, iPk, nPk
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
|
||||||
|
sqlite3VdbeAddOp3(
|
||||||
|
v, OP_NotExists, iDataCur, labelContinue, regOldRowid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VdbeCoverage(v);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
|
||||||
|
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0);
|
||||||
|
VdbeCoverage(v);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
|
labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
|
||||||
regOldRowid);
|
regOldRowid);
|
||||||
@@ -708,7 +832,13 @@ void sqlite3Update(
|
|||||||
}else{
|
}else{
|
||||||
j = aXRef[i];
|
j = aXRef[i];
|
||||||
if( j>=0 ){
|
if( j>=0 ){
|
||||||
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, k);
|
if( nChangeFrom ){
|
||||||
|
assert( eOnePass==ONEPASS_OFF );
|
||||||
|
int nOff = (isView ? pTab->nCol : nPk);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, iEph, nOff+j, k);
|
||||||
|
}else{
|
||||||
|
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, k);
|
||||||
|
}
|
||||||
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
|
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
|
||||||
/* This branch loads the value of a column that will not be changed
|
/* This branch loads the value of a column that will not be changed
|
||||||
** into a register. This is done if there are no BEFORE triggers, or
|
** into a register. This is done if there are no BEFORE triggers, or
|
||||||
@@ -740,43 +870,45 @@ void sqlite3Update(
|
|||||||
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
|
||||||
TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
|
TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
|
||||||
|
|
||||||
/* The row-trigger may have deleted the row being updated. In this
|
if( !isView ){
|
||||||
** case, jump to the next row. No updates or AFTER triggers are
|
/* The row-trigger may have deleted the row being updated. In this
|
||||||
** required. This behavior - what happens when the row being updated
|
** case, jump to the next row. No updates or AFTER triggers are
|
||||||
** is deleted or renamed by a BEFORE trigger - is left undefined in the
|
** required. This behavior - what happens when the row being updated
|
||||||
** documentation.
|
** is deleted or renamed by a BEFORE trigger - is left undefined in the
|
||||||
*/
|
** documentation.
|
||||||
if( pPk ){
|
*/
|
||||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey);
|
if( pPk ){
|
||||||
VdbeCoverage(v);
|
sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
|
||||||
}else{
|
VdbeCoverage(v);
|
||||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
|
}else{
|
||||||
VdbeCoverage(v);
|
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
|
||||||
}
|
VdbeCoverage(v);
|
||||||
|
}
|
||||||
/* After-BEFORE-trigger-reload-loop:
|
|
||||||
** If it did not delete it, the BEFORE trigger may still have modified
|
/* After-BEFORE-trigger-reload-loop:
|
||||||
** some of the columns of the row being updated. Load the values for
|
** If it did not delete it, the BEFORE trigger may still have modified
|
||||||
** all columns not modified by the update statement into their registers
|
** some of the columns of the row being updated. Load the values for
|
||||||
** in case this has happened. Only unmodified columns are reloaded.
|
** all columns not modified by the update statement into their registers
|
||||||
** The values computed for modified columns use the values before the
|
** in case this has happened. Only unmodified columns are reloaded.
|
||||||
** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
|
** The values computed for modified columns use the values before the
|
||||||
** for an example.
|
** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
|
||||||
*/
|
** for an example.
|
||||||
for(i=0, k=regNew; i<pTab->nCol; i++, k++){
|
*/
|
||||||
if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
|
for(i=0, k=regNew; i<pTab->nCol; i++, k++){
|
||||||
if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--;
|
if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
|
||||||
}else if( aXRef[i]<0 && i!=pTab->iPKey ){
|
if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--;
|
||||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
|
}else if( aXRef[i]<0 && i!=pTab->iPKey ){
|
||||||
|
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
|
||||||
if( pTab->tabFlags & TF_HasGenerated ){
|
if( pTab->tabFlags & TF_HasGenerated ){
|
||||||
testcase( pTab->tabFlags & TF_HasVirtual );
|
testcase( pTab->tabFlags & TF_HasVirtual );
|
||||||
testcase( pTab->tabFlags & TF_HasStored );
|
testcase( pTab->tabFlags & TF_HasStored );
|
||||||
sqlite3ComputeGeneratedColumns(pParse, regNew, pTab);
|
sqlite3ComputeGeneratedColumns(pParse, regNew, pTab);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !isView ){
|
if( !isView ){
|
||||||
@@ -879,7 +1011,7 @@ void sqlite3Update(
|
|||||||
}else if( eOnePass==ONEPASS_MULTI ){
|
}else if( eOnePass==ONEPASS_MULTI ){
|
||||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||||
sqlite3WhereEnd(pWInfo);
|
sqlite3WhereEnd(pWInfo);
|
||||||
}else if( pPk ){
|
}else if( pPk || nChangeFrom ){
|
||||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
||||||
}else{
|
}else{
|
||||||
@@ -982,69 +1114,101 @@ static void updateVirtualTable(
|
|||||||
addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
|
addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
|
||||||
regArg = pParse->nMem + 1;
|
regArg = pParse->nMem + 1;
|
||||||
pParse->nMem += nArg;
|
pParse->nMem += nArg;
|
||||||
regRec = ++pParse->nMem;
|
if( pSrc->nSrc>1 ){
|
||||||
regRowid = ++pParse->nMem;
|
ExprList *pList = 0;
|
||||||
|
|
||||||
/* Start scanning the virtual table */
|
|
||||||
pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
|
|
||||||
if( pWInfo==0 ) return;
|
|
||||||
|
|
||||||
/* Populate the argument registers. */
|
|
||||||
for(i=0; i<pTab->nCol; i++){
|
|
||||||
assert( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 );
|
|
||||||
if( aXRef[i]>=0 ){
|
|
||||||
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
|
||||||
}else{
|
|
||||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
|
||||||
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( HasRowid(pTab) ){
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
|
||||||
if( pRowid ){
|
if( pRowid ){
|
||||||
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
pList = sqlite3ExprListAppend(pParse, pList, sqlite3ExprDup(db,pRowid,0));
|
||||||
}else{
|
}else{
|
||||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3PExpr(pParse, TK_DOT,
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||||
|
sqlite3Expr(db, TK_ID, "_rowid_")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
for(i=0; i<pTab->nCol; i++){
|
||||||
|
if( aXRef[i]>=0 ){
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0)
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
pList = sqlite3ExprListAppend(pParse, pList,
|
||||||
|
sqlite3PExpr(pParse, TK_DOT,
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||||
|
sqlite3Expr(db, TK_ID, pTab->aCol[i].zName)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePopulateEphTable(pParse, ephemTab, 0, pList, pSrc, pWhere);
|
||||||
|
sqlite3ExprListDelete(db, pList);
|
||||||
|
eOnePass = ONEPASS_OFF;
|
||||||
}else{
|
}else{
|
||||||
Index *pPk; /* PRIMARY KEY index */
|
regRec = ++pParse->nMem;
|
||||||
i16 iPk; /* PRIMARY KEY column */
|
regRowid = ++pParse->nMem;
|
||||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
|
||||||
assert( pPk!=0 );
|
|
||||||
assert( pPk->nKeyCol==1 );
|
|
||||||
iPk = pPk->aiColumn[0];
|
|
||||||
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
/* Start scanning the virtual table */
|
||||||
|
pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,WHERE_ONEPASS_DESIRED,0);
|
||||||
|
if( pWInfo==0 ) return;
|
||||||
|
|
||||||
/* There is no ONEPASS_MULTI on virtual tables */
|
/* Populate the argument registers. */
|
||||||
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
for(i=0; i<pTab->nCol; i++){
|
||||||
|
assert( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 );
|
||||||
|
if( aXRef[i]>=0 ){
|
||||||
|
sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
||||||
|
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* For sqlite3_vtab_nochange() */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( HasRowid(pTab) ){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
||||||
|
if( pRowid ){
|
||||||
|
sqlite3ExprCode(pParse, pRowid, regArg+1);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Index *pPk; /* PRIMARY KEY index */
|
||||||
|
i16 iPk; /* PRIMARY KEY column */
|
||||||
|
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||||
|
assert( pPk!=0 );
|
||||||
|
assert( pPk->nKeyCol==1 );
|
||||||
|
iPk = pPk->aiColumn[0];
|
||||||
|
sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
|
||||||
|
}
|
||||||
|
|
||||||
if( eOnePass ){
|
eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
||||||
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
|
||||||
** above. */
|
/* There is no ONEPASS_MULTI on virtual tables */
|
||||||
sqlite3VdbeChangeToNoop(v, addr);
|
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
|
||||||
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
|
||||||
}else{
|
if( eOnePass ){
|
||||||
/* Create a record from the argument register contents and insert it into
|
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
||||||
** the ephemeral table. */
|
** above. */
|
||||||
sqlite3MultiWrite(pParse);
|
sqlite3VdbeChangeToNoop(v, addr);
|
||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
|
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
|
||||||
|
}else{
|
||||||
|
/* Create a record from the argument register contents and insert it into
|
||||||
|
** the ephemeral table. */
|
||||||
|
sqlite3MultiWrite(pParse);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
|
||||||
#ifdef SQLITE_DEBUG
|
#ifdef SQLITE_DEBUG
|
||||||
/* Signal an assert() within OP_MakeRecord that it is allowed to
|
/* Signal an assert() within OP_MakeRecord that it is allowed to
|
||||||
** accept no-change records with serial_type 10 */
|
** accept no-change records with serial_type 10 */
|
||||||
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
|
sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
|
||||||
#endif
|
#endif
|
||||||
sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
|
sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
|
||||||
sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
|
sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if( eOnePass==ONEPASS_OFF ){
|
if( eOnePass==ONEPASS_OFF ){
|
||||||
/* End the virtual table scan */
|
/* End the virtual table scan */
|
||||||
sqlite3WhereEnd(pWInfo);
|
if( pSrc->nSrc==1 ){
|
||||||
|
sqlite3WhereEnd(pWInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/* Begin scannning through the ephemeral table. */
|
/* Begin scannning through the ephemeral table. */
|
||||||
addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
|
addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
|
||||||
|
|||||||
105
test/fts4upfrom.test
Normal file
105
test/fts4upfrom.test
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# 2020 February 24
|
||||||
|
#
|
||||||
|
# The author disclaims copyright to this source code. In place of
|
||||||
|
# a legal notice, here is a blessing:
|
||||||
|
#
|
||||||
|
# May you do good and not evil.
|
||||||
|
# May you find forgiveness for yourself and forgive others.
|
||||||
|
# May you share freely, never taking more than you give.
|
||||||
|
#
|
||||||
|
#*************************************************************************
|
||||||
|
# This file implements regression tests for SQLite library. The
|
||||||
|
# focus of this script is testing UPDATE statements with FROM clauses
|
||||||
|
# against FTS4 tables.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set testprefix fts4upfrom
|
||||||
|
|
||||||
|
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||||
|
ifcapable !fts3 {
|
||||||
|
finish_test
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach {tn create_table} {
|
||||||
|
1 { CREATE VIRTUAL TABLE ft USING fts3(a, b, c) }
|
||||||
|
2 { CREATE TABLE ft(a, b, c) }
|
||||||
|
3 {
|
||||||
|
CREATE TABLE real(a, b, c);
|
||||||
|
CREATE INDEX i1 ON real(a);
|
||||||
|
CREATE VIEW ft AS SELECT rowid, a, b, c FROM real;
|
||||||
|
CREATE TRIGGER tr1 INSTEAD OF INSERT ON ft BEGIN
|
||||||
|
INSERT INTO real(rowid, a, b, c) VALUES(new.rowid, new.a, new.b, new.c);
|
||||||
|
END;
|
||||||
|
CREATE TRIGGER tr2 INSTEAD OF UPDATE ON ft BEGIN
|
||||||
|
UPDATE real SET rowid=new.rowid, a=new.a, b=new.b, c=new.c
|
||||||
|
WHERE rowid=old.rowid;
|
||||||
|
END;
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
catchsql { DROP VIEW IF EXISTS changes }
|
||||||
|
catchsql { DROP TABLE IF EXISTS ft }
|
||||||
|
catchsql { DROP VIEW IF EXISTS ft }
|
||||||
|
execsql $create_table
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.0 {
|
||||||
|
INSERT INTO ft(a, b, c) VALUES('a', NULL, 'apple');
|
||||||
|
INSERT INTO ft(a, b, c) VALUES('b', NULL, 'banana');
|
||||||
|
INSERT INTO ft(a, b, c) VALUES('c', NULL, 'cherry');
|
||||||
|
INSERT INTO ft(a, b, c) VALUES('d', NULL, 'damson plum');
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.1 {
|
||||||
|
SELECT a, b, c FROM ft ORDER BY rowid;
|
||||||
|
} {
|
||||||
|
a {} apple
|
||||||
|
b {} banana
|
||||||
|
c {} cherry
|
||||||
|
d {} {damson plum}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.2 {
|
||||||
|
UPDATE ft SET b=o.c FROM ft AS o WHERE (ft.a == char(unicode(o.a)+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.3 {
|
||||||
|
SELECT a, b, c FROM ft ORDER BY rowid;
|
||||||
|
} {
|
||||||
|
a {} apple
|
||||||
|
b apple banana
|
||||||
|
c banana cherry
|
||||||
|
d cherry {damson plum}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_catchsql_test 1.$tn.4 {
|
||||||
|
UPDATE ft SET c=v FROM changes WHERE a=k;
|
||||||
|
} {1 {no such table: changes}}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.5 {
|
||||||
|
create view changes(k, v) AS
|
||||||
|
VALUES( 'd', 'dewberry' ) UNION ALL
|
||||||
|
VALUES( 'c', 'clementine' ) UNION ALL
|
||||||
|
VALUES( 'b', 'blueberry' ) UNION ALL
|
||||||
|
VALUES( 'a', 'apricot' )
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.6 {
|
||||||
|
UPDATE ft SET c=v FROM changes WHERE a=k;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_execsql_test 1.$tn.7 {
|
||||||
|
SELECT a, b, c FROM ft ORDER BY rowid;
|
||||||
|
} {
|
||||||
|
a {} apricot
|
||||||
|
b apple blueberry
|
||||||
|
c banana clementine
|
||||||
|
d cherry dewberry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
||||||
@@ -18,6 +18,8 @@ sqlite3 sqlite ""
|
|||||||
|
|
||||||
proc execsql {sql} {
|
proc execsql {sql} {
|
||||||
|
|
||||||
|
set sql [string map {{WITHOUT ROWID} {}} $sql]
|
||||||
|
|
||||||
set lSql [list]
|
set lSql [list]
|
||||||
set frag ""
|
set frag ""
|
||||||
while {[string length $sql]>0} {
|
while {[string length $sql]>0} {
|
||||||
|
|||||||
80
test/upfrom1.tcl
Normal file
80
test/upfrom1.tcl
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 2020 April 22
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#***********************************************************************
|
||||||
|
#
|
||||||
|
|
||||||
|
source [file join [file dirname $argv0] pg_common.tcl]
|
||||||
|
|
||||||
|
#=========================================================================
|
||||||
|
|
||||||
|
start_test upfrom1 "2020 April 22"
|
||||||
|
|
||||||
|
foreach {tn wo} {
|
||||||
|
1 "WITHOUT ROWID"
|
||||||
|
2 ""
|
||||||
|
} {
|
||||||
|
eval [string map [list %TN% $tn %WITHOUT_ROWID% $wo] {
|
||||||
|
execsql_test 1.%TN%.0 {
|
||||||
|
DROP TABLE IF EXISTS t2;
|
||||||
|
CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) %WITHOUT_ROWID%;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS chng;
|
||||||
|
CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER);
|
||||||
|
INSERT INTO chng VALUES(1, 100, 1000);
|
||||||
|
INSERT INTO chng VALUES(7, 700, 7000);
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.1 {
|
||||||
|
SELECT * FROM t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.2 {
|
||||||
|
UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a;
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.3 {
|
||||||
|
DELETE FROM t2;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.4 {
|
||||||
|
UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a)
|
||||||
|
WHERE a IN (SELECT a FROM chng);
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.5 {
|
||||||
|
DROP TABLE IF EXISTS t3;
|
||||||
|
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) %WITHOUT_ROWID%;
|
||||||
|
INSERT INTO t3 VALUES(1, 1, 'one');
|
||||||
|
INSERT INTO t3 VALUES(2, 2, 'two');
|
||||||
|
INSERT INTO t3 VALUES(3, 3, 'three');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS t4;
|
||||||
|
CREATE TABLE t4(x TEXT);
|
||||||
|
INSERT INTO t4 VALUES('five');
|
||||||
|
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 1.%TN%.6 {
|
||||||
|
UPDATE t3 SET c=x FROM t4;
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
}
|
||||||
|
}]}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
||||||
130
test/upfrom1.test
Normal file
130
test/upfrom1.test
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# 2020 April 22
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED!
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set testprefix upfrom1
|
||||||
|
|
||||||
|
do_execsql_test 1.1.0 {
|
||||||
|
DROP TABLE IF EXISTS t2;
|
||||||
|
CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) WITHOUT ROWID;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS chng;
|
||||||
|
CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER);
|
||||||
|
INSERT INTO chng VALUES(1, 100, 1000);
|
||||||
|
INSERT INTO chng VALUES(7, 700, 7000);
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.1 {
|
||||||
|
SELECT * FROM t2;
|
||||||
|
} {1 2 3 4 5 6 7 8 9}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.2 {
|
||||||
|
UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a;
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
} {1 100 1000 4 5 6 7 700 7000}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.3 {
|
||||||
|
DELETE FROM t2;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.4 {
|
||||||
|
UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a)
|
||||||
|
WHERE a IN (SELECT a FROM chng);
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
} {1 100 1000 4 5 6 7 700 7000}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.5 {
|
||||||
|
DROP TABLE IF EXISTS t3;
|
||||||
|
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) WITHOUT ROWID;
|
||||||
|
INSERT INTO t3 VALUES(1, 1, 'one');
|
||||||
|
INSERT INTO t3 VALUES(2, 2, 'two');
|
||||||
|
INSERT INTO t3 VALUES(3, 3, 'three');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS t4;
|
||||||
|
CREATE TABLE t4(x TEXT);
|
||||||
|
INSERT INTO t4 VALUES('five');
|
||||||
|
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
} {1 1 one 2 2 two 3 3 three}
|
||||||
|
|
||||||
|
do_execsql_test 1.1.6 {
|
||||||
|
UPDATE t3 SET c=x FROM t4;
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
} {1 1 five 2 2 five 3 3 five}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.0 {
|
||||||
|
DROP TABLE IF EXISTS t2;
|
||||||
|
CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER) ;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS chng;
|
||||||
|
CREATE TABLE chng(a INTEGER, b INTEGER, c INTEGER);
|
||||||
|
INSERT INTO chng VALUES(1, 100, 1000);
|
||||||
|
INSERT INTO chng VALUES(7, 700, 7000);
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.1 {
|
||||||
|
SELECT * FROM t2;
|
||||||
|
} {1 2 3 4 5 6 7 8 9}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.2 {
|
||||||
|
UPDATE t2 SET b = chng.b, c = chng.c FROM chng WHERE chng.a = t2.a;
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
} {1 100 1000 4 5 6 7 700 7000}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.3 {
|
||||||
|
DELETE FROM t2;
|
||||||
|
INSERT INTO t2 VALUES(1, 2, 3);
|
||||||
|
INSERT INTO t2 VALUES(4, 5, 6);
|
||||||
|
INSERT INTO t2 VALUES(7, 8, 9);
|
||||||
|
} {}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.4 {
|
||||||
|
UPDATE t2 SET (b, c) = (SELECT b, c FROM chng WHERE a=t2.a)
|
||||||
|
WHERE a IN (SELECT a FROM chng);
|
||||||
|
SELECT * FROM t2 ORDER BY a;
|
||||||
|
} {1 100 1000 4 5 6 7 700 7000}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.5 {
|
||||||
|
DROP TABLE IF EXISTS t3;
|
||||||
|
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INTEGER, c TEXT) ;
|
||||||
|
INSERT INTO t3 VALUES(1, 1, 'one');
|
||||||
|
INSERT INTO t3 VALUES(2, 2, 'two');
|
||||||
|
INSERT INTO t3 VALUES(3, 3, 'three');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS t4;
|
||||||
|
CREATE TABLE t4(x TEXT);
|
||||||
|
INSERT INTO t4 VALUES('five');
|
||||||
|
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
} {1 1 one 2 2 two 3 3 three}
|
||||||
|
|
||||||
|
do_execsql_test 1.2.6 {
|
||||||
|
UPDATE t3 SET c=x FROM t4;
|
||||||
|
SELECT * FROM t3 ORDER BY a;
|
||||||
|
} {1 1 five 2 2 five 3 3 five}
|
||||||
|
|
||||||
|
finish_test
|
||||||
Reference in New Issue
Block a user