mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Add support for the RETURNING clause following PostgreSQL syntax.
FossilOrigin-Name: 416c898bfb8ff9639ffbaefcfb47fce3782763af1fc67969fa91c5f01a336676
This commit is contained in:
42
manifest
42
manifest
@ -1,5 +1,5 @@
|
|||||||
C Fix\san\sassert()\sthat\smight\sbe\soff-by-one\sin\sthe\scase\sof\sa\sprior\nerrors\sin\sthe\sparse.
|
C Add\ssupport\sfor\sthe\sRETURNING\sclause\sfollowing\sPostgreSQL\ssyntax.
|
||||||
D 2021-02-03T12:35:51.366
|
D 2021-02-03T13:08:09.152
|
||||||
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
|
||||||
@ -485,24 +485,24 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
|||||||
F src/btree.c 4da25694985ac8f5f714bfa58a6cd453f9161d7da9394a95605aaa4db2752757
|
F src/btree.c 4da25694985ac8f5f714bfa58a6cd453f9161d7da9394a95605aaa4db2752757
|
||||||
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
|
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
|
||||||
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
|
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
|
||||||
F src/build.c d4c06261b0e532523ede58dc511381a7a9c155132e4b65a6bb2ff76fe657793a
|
F src/build.c 118e1076282415229420d04f9cc25bb148a2c412d82ea3c319136d2122c842e5
|
||||||
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
||||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||||
F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
|
F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
|
||||||
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
|
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
|
||||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||||
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
|
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
|
||||||
F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe
|
F src/delete.c 352ea931218c45a3daf17472d4141b9c7fc026d85da3f1ade404ea5bb6d67f77
|
||||||
F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74
|
F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74
|
||||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||||
F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
|
F src/fkey.c 02e4a3311885cd2b31eb17fd58dc2fc738cd2c823d0d39e4dd5595169c6f8bc3
|
||||||
F src/func.c 2ea99e9e0531b7f020d5e8e167d25344d618afc718ddc94dd91fa8fef1c85a91
|
F src/func.c 2ea99e9e0531b7f020d5e8e167d25344d618afc718ddc94dd91fa8fef1c85a91
|
||||||
F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
|
F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
|
||||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||||
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
|
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
|
||||||
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
|
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
|
||||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||||
F src/insert.c 9b970eff058a858fbd9f2db71425ef195942c2610855daa66ae23024432d52f5
|
F src/insert.c 97be36c52c667a64aacbba76398544d224268d62444b67d011a077c486e375bb
|
||||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||||
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
|
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
|
||||||
F src/main.c 1c5de7b3fabcdf05f4fe563aab5d81d175b89c67a8678a12ba86629356afa356
|
F src/main.c 1c5de7b3fabcdf05f4fe563aab5d81d175b89c67a8678a12ba86629356afa356
|
||||||
@ -530,7 +530,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
|
|||||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||||
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
|
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
|
||||||
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
|
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
|
||||||
F src/parse.y 6c8aa09a7fa6e0867c3a3d67ef61b911aa392c9b084a61dc632cd93732aef8ad
|
F src/parse.y 67ba503780de64b967ae195b7e14c33531329228e1bc0b83d63324beb733680b
|
||||||
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
|
||||||
@ -539,14 +539,14 @@ F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
|
|||||||
F src/prepare.c f288cbc35f79eb32e162de7e80a63ebe00d80e639dcfac071bee11570cbdb16f
|
F src/prepare.c f288cbc35f79eb32e162de7e80a63ebe00d80e639dcfac071bee11570cbdb16f
|
||||||
F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
|
F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
|
||||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||||
F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c
|
F src/resolve.c f6761473ea4b51190fc52f8f2121498b78717266e106e7bff12849ea2d52165f
|
||||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||||
F src/select.c 738cb746189f721f59972993c13085fa2975c4cbfd04ba26445f3b42c81237dc
|
F src/select.c 738cb746189f721f59972993c13085fa2975c4cbfd04ba26445f3b42c81237dc
|
||||||
F src/shell.c.in 9ebc74e4f05cfbd0f4a36060fdaeff1da4e9af4458358722bc08c5a1ab9a0879
|
F src/shell.c.in 9ebc74e4f05cfbd0f4a36060fdaeff1da4e9af4458358722bc08c5a1ab9a0879
|
||||||
F src/sqlite.h.in 0af968a1fa3c717261e1df0ed105fa7bddb4d82de7e0adb3eab49e6aa81b4de7
|
F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||||
F src/sqliteInt.h 3e5bc0446611e272b93754265e3265f36249d0458da25e32991fce241d69dbcf
|
F src/sqliteInt.h 0fda3b2c05b1559135aa2c4ecb8e75bd2085ba4433310bbb5427d97c2d81315d
|
||||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||||
@ -607,17 +607,17 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
|||||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||||
F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
|
F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
|
||||||
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
|
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
|
||||||
F src/trigger.c 731ea5ed6b308574b7dc2a5d2a9187ef5510a3692cc1ea06a34608a084b8f376
|
F src/trigger.c 0a242d65dd9b9822d4e990653eb4ece3557dcda01374934aa3cc1f9718d8dee3
|
||||||
F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
|
F src/update.c 0f5a61f0787199983530a33f6fffe4f52742f35fcdf6ccfad1078b1a8bc17723
|
||||||
F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
|
F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
|
||||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||||
F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
|
F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
|
||||||
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
||||||
F src/vdbe.c 102d21260bddbb43c845603c3a2d6b4f3762e72f836ccda12991f291485d2539
|
F src/vdbe.c 9b9a714318e49b59a282b4e175080dc53a07b8099895fa21613e6b80fbad3727
|
||||||
F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
|
F src/vdbe.h a71bf43572d3de57923d1928ac01ae8d355cd67e94462ba4f7462265cedbef9a
|
||||||
F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
|
F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
|
||||||
F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
|
F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
|
||||||
F src/vdbeaux.c e91d74e24babcf61969279b193e228cf4f8bc724a9cc59ed287db064326876f8
|
F src/vdbeaux.c 2be30e4918126122fa358ef8303206cad0feffe17d320077c77ff5c2a34f3626
|
||||||
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
||||||
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
|
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
|
||||||
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
|
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
|
||||||
@ -1286,6 +1286,7 @@ F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b
|
|||||||
F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
|
F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
|
||||||
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
|
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
|
||||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||||
|
F test/returning1.test 684e1c73d961422a7376c932fcdd6dacf02bad21d12f749cfe8c19991ef379f6
|
||||||
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
||||||
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
|
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
|
||||||
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||||
@ -1751,7 +1752,7 @@ F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
|
|||||||
F test/whereL.test 1afe47227f093dc0547236491fb37529b7be9724b8575925a321001b80e6a23a
|
F test/whereL.test 1afe47227f093dc0547236491fb37529b7be9724b8575925a321001b80e6a23a
|
||||||
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
|
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
|
||||||
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
|
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
|
||||||
F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
|
F test/wherelimit.test daa0fd9122c5745cc459ec40b8d3c16ce13ce8382b5b847e7cfff4b871260cbf
|
||||||
F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
|
F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
|
||||||
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
|
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
|
||||||
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
|
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
|
||||||
@ -1826,7 +1827,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
|
|||||||
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
|
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
|
||||||
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
|
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
|
||||||
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
|
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
|
||||||
F tool/mkkeywordhash.c 24e4396ae665d985fed9e040e8b748129c1a12d77eeeae7ad4609821c41ba7bf
|
F tool/mkkeywordhash.c 750f25aef0e23f8e3367af6d824fbf5ed7d3e285f27cea91aa2dd72c367630eb
|
||||||
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
|
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
|
||||||
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
|
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
|
||||||
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
|
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
|
||||||
@ -1898,7 +1899,8 @@ 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 e4ccfac09b6fe8cc3aec29d10f4e4c83097964f29882343db52ed91f6f0dde1c
|
P 06b15b17be38c804dd2641d8616a2a7bd396d2eb9901a0fbf94edd8bd508cf9c a10c5a2503ff2998f6ee40f721aab8c9579052e535dc141bd57d10551eaea387
|
||||||
R 30096bbeccf9e03a69ea838444df44cb
|
R 10c9b96c07db17433ca188cab65447b4
|
||||||
|
T +closed a10c5a2503ff2998f6ee40f721aab8c9579052e535dc141bd57d10551eaea387
|
||||||
U drh
|
U drh
|
||||||
Z 17a66c8e0166569653feae3b32e8dbae
|
Z 40f463993ad19f6f3483715fa2a6abfe
|
||||||
|
@ -1 +1 @@
|
|||||||
06b15b17be38c804dd2641d8616a2a7bd396d2eb9901a0fbf94edd8bd508cf9c
|
416c898bfb8ff9639ffbaefcfb47fce3782763af1fc67969fa91c5f01a336676
|
69
src/build.c
69
src/build.c
@ -1243,6 +1243,75 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Name of the special TEMP trigger used to implement RETURNING. The
|
||||||
|
** name begins with "sqlite_" so that it is guaranteed not to collide
|
||||||
|
** with any application-generated triggers.
|
||||||
|
*/
|
||||||
|
#define RETURNING_TRIGGER_NAME "sqlite_returning"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Clean up the data structures associated with the RETURNING clause.
|
||||||
|
*/
|
||||||
|
static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
|
||||||
|
Hash *pHash;
|
||||||
|
pHash = &(db->aDb[1].pSchema->trigHash);
|
||||||
|
sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
|
||||||
|
sqlite3ExprListDelete(db, pRet->pReturnEL);
|
||||||
|
sqlite3DbFree(db, pRet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Add the RETURNING clause to the parse currently underway.
|
||||||
|
**
|
||||||
|
** This routine creates a special TEMP trigger that will fire for each row
|
||||||
|
** of the DML statement. That TEMP trigger contains a single SELECT
|
||||||
|
** statement with a result set that is the argument of the RETURNING clause.
|
||||||
|
** The trigger has the Trigger.bReturning flag and an opcode of
|
||||||
|
** TK_RETURNING instead of TK_SELECT, so that the trigger code generator
|
||||||
|
** knows to handle it specially. The TEMP trigger is automatically
|
||||||
|
** removed at the end of the parse.
|
||||||
|
**
|
||||||
|
** When this routine is called, we do not yet know if the RETURNING clause
|
||||||
|
** is attached to a DELETE, INSERT, or UPDATE, so construct it as a
|
||||||
|
** RETURNING trigger instead. It will then be converted into the appropriate
|
||||||
|
** type on the first call to sqlite3TriggersExist().
|
||||||
|
*/
|
||||||
|
void sqlite3AddReturning(Parse *pParse, ExprList *pList){
|
||||||
|
Returning *pRet;
|
||||||
|
Hash *pHash;
|
||||||
|
sqlite3 *db = pParse->db;
|
||||||
|
assert( !pParse->bReturning );
|
||||||
|
pParse->bReturning = 1;
|
||||||
|
pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
|
||||||
|
if( pRet==0 ){
|
||||||
|
sqlite3ExprListDelete(db, pList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pRet->pParse = pParse;
|
||||||
|
pRet->pReturnEL = pList;
|
||||||
|
sqlite3ParserAddCleanup(pParse,
|
||||||
|
(void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
|
||||||
|
if( db->mallocFailed ) return;
|
||||||
|
pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
|
||||||
|
pRet->retTrig.op = TK_RETURNING;
|
||||||
|
pRet->retTrig.tr_tm = TRIGGER_AFTER;
|
||||||
|
pRet->retTrig.bReturning = 1;
|
||||||
|
pRet->retTrig.pSchema = db->aDb[1].pSchema;
|
||||||
|
pRet->retTrig.step_list = &pRet->retTStep;
|
||||||
|
pRet->retTStep.op = TK_RETURNING;
|
||||||
|
pRet->retTStep.pTrig = &pRet->retTrig;
|
||||||
|
pRet->retTStep.pSelect = &pRet->retSel;
|
||||||
|
pRet->retSel.op = TK_ALL;
|
||||||
|
pRet->retSel.pEList = pList;
|
||||||
|
pRet->retSel.pSrc = (SrcList*)&pRet->retSrcList;
|
||||||
|
pHash = &(db->aDb[1].pSchema->trigHash);
|
||||||
|
assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 );
|
||||||
|
if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
|
||||||
|
==&pRet->retTrig ){
|
||||||
|
sqlite3OomFault(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Add a new column to the table currently being constructed.
|
** Add a new column to the table currently being constructed.
|
||||||
|
@ -387,6 +387,7 @@ void sqlite3DeleteFrom(
|
|||||||
if( (db->flags & SQLITE_CountRows)!=0
|
if( (db->flags & SQLITE_CountRows)!=0
|
||||||
&& !pParse->nested
|
&& !pParse->nested
|
||||||
&& !pParse->pTriggerTab
|
&& !pParse->pTriggerTab
|
||||||
|
&& !pParse->bReturning
|
||||||
){
|
){
|
||||||
memCnt = ++pParse->nMem;
|
memCnt = ++pParse->nMem;
|
||||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
||||||
@ -608,7 +609,7 @@ void sqlite3DeleteFrom(
|
|||||||
** invoke the callback function.
|
** invoke the callback function.
|
||||||
*/
|
*/
|
||||||
if( memCnt ){
|
if( memCnt ){
|
||||||
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
|
sqlite3VdbeAddOp2(v, OP_ChngCntRow, memCnt, 1);
|
||||||
sqlite3VdbeSetNumCols(v, 1);
|
sqlite3VdbeSetNumCols(v, 1);
|
||||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1352,7 @@ static Trigger *fkActionTrigger(
|
|||||||
|
|
||||||
switch( action ){
|
switch( action ){
|
||||||
case OE_Restrict:
|
case OE_Restrict:
|
||||||
pStep->op = TK_SELECT;
|
pStep->op = TK_SELECT;
|
||||||
break;
|
break;
|
||||||
case OE_Cascade:
|
case OE_Cascade:
|
||||||
if( !pChanges ){
|
if( !pChanges ){
|
||||||
|
@ -954,6 +954,7 @@ void sqlite3Insert(
|
|||||||
if( (db->flags & SQLITE_CountRows)!=0
|
if( (db->flags & SQLITE_CountRows)!=0
|
||||||
&& !pParse->nested
|
&& !pParse->nested
|
||||||
&& !pParse->pTriggerTab
|
&& !pParse->pTriggerTab
|
||||||
|
&& !pParse->bReturning
|
||||||
){
|
){
|
||||||
regRowCount = ++pParse->nMem;
|
regRowCount = ++pParse->nMem;
|
||||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||||
@ -1318,7 +1319,7 @@ insert_end:
|
|||||||
** invoke the callback function.
|
** invoke the callback function.
|
||||||
*/
|
*/
|
||||||
if( regRowCount ){
|
if( regRowCount ){
|
||||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
|
||||||
sqlite3VdbeSetNumCols(v, 1);
|
sqlite3VdbeSetNumCols(v, 1);
|
||||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
||||||
}
|
}
|
||||||
|
26
src/parse.y
26
src/parse.y
@ -868,7 +868,7 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
|||||||
/////////////////////////// The DELETE statement /////////////////////////////
|
/////////////////////////// The DELETE statement /////////////////////////////
|
||||||
//
|
//
|
||||||
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
||||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
|
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W)
|
||||||
orderby_opt(O) limit_opt(L). {
|
orderby_opt(O) limit_opt(L). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||||
@ -881,7 +881,7 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
|
|||||||
sqlite3DeleteFrom(pParse,X,W,O,L);
|
sqlite3DeleteFrom(pParse,X,W,O,L);
|
||||||
}
|
}
|
||||||
%else
|
%else
|
||||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
|
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
sqlite3DeleteFrom(pParse,X,W,0,0);
|
sqlite3DeleteFrom(pParse,X,W,0,0);
|
||||||
}
|
}
|
||||||
@ -889,15 +889,23 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
|
|||||||
|
|
||||||
%type where_opt {Expr*}
|
%type where_opt {Expr*}
|
||||||
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
|
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||||
|
%type where_opt_ret {Expr*}
|
||||||
|
%destructor where_opt_ret {sqlite3ExprDelete(pParse->db, $$);}
|
||||||
|
|
||||||
where_opt(A) ::= . {A = 0;}
|
where_opt(A) ::= . {A = 0;}
|
||||||
where_opt(A) ::= WHERE expr(X). {A = X;}
|
where_opt(A) ::= WHERE expr(X). {A = X;}
|
||||||
|
where_opt_ret(A) ::= . {A = 0;}
|
||||||
|
where_opt_ret(A) ::= WHERE expr(X). {A = X;}
|
||||||
|
where_opt_ret(A) ::= RETURNING selcollist(X).
|
||||||
|
{sqlite3AddReturning(pParse,X); A = 0;}
|
||||||
|
where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
|
||||||
|
{sqlite3AddReturning(pParse,Y); A = X;}
|
||||||
|
|
||||||
////////////////////////// The UPDATE command ////////////////////////////////
|
////////////////////////// The UPDATE command ////////////////////////////////
|
||||||
//
|
//
|
||||||
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
||||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
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_ret(W) orderby_opt(O) limit_opt(L). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
X = sqlite3SrcListAppendList(pParse, X, F);
|
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||||
@ -912,7 +920,7 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
|||||||
}
|
}
|
||||||
%else
|
%else
|
||||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||||
where_opt(W). {
|
where_opt_ret(W). {
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||||
X = sqlite3SrcListAppendList(pParse, X, F);
|
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||||
@ -946,7 +954,7 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
|
|||||||
upsert(U). {
|
upsert(U). {
|
||||||
sqlite3Insert(pParse, X, S, F, R, U);
|
sqlite3Insert(pParse, X, S, F, R, U);
|
||||||
}
|
}
|
||||||
cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
|
cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning.
|
||||||
{
|
{
|
||||||
sqlite3Insert(pParse, X, 0, F, R, 0);
|
sqlite3Insert(pParse, X, 0, F, R, 0);
|
||||||
}
|
}
|
||||||
@ -959,16 +967,20 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
|
|||||||
// avoid unreachable code.
|
// avoid unreachable code.
|
||||||
//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
|
//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
|
||||||
upsert(A) ::= . { A = 0; }
|
upsert(A) ::= . { A = 0; }
|
||||||
|
upsert(A) ::= RETURNING selcollist(X). { A = 0; sqlite3AddReturning(pParse,X); }
|
||||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
|
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
|
||||||
DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
|
DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
|
||||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
|
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
|
||||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
|
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
|
||||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
|
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
|
||||||
upsert(A) ::= ON CONFLICT DO NOTHING.
|
upsert(A) ::= ON CONFLICT DO NOTHING returning.
|
||||||
{ A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
|
{ A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
|
||||||
upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W).
|
upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W) returning.
|
||||||
{ A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);}
|
{ A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);}
|
||||||
|
|
||||||
|
returning ::= RETURNING selcollist(X). {sqlite3AddReturning(pParse,X);}
|
||||||
|
returning ::= .
|
||||||
|
|
||||||
%type insert_cmd {int}
|
%type insert_cmd {int}
|
||||||
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
||||||
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
|
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
|
||||||
|
@ -371,23 +371,26 @@ static int lookupName(
|
|||||||
** it is a new.* or old.* trigger argument reference. Or
|
** it is a new.* or old.* trigger argument reference. Or
|
||||||
** maybe it is an excluded.* from an upsert.
|
** maybe it is an excluded.* from an upsert.
|
||||||
*/
|
*/
|
||||||
if( zDb==0 && zTab!=0 && cntTab==0 ){
|
if( zDb==0 && cntTab==0 ){
|
||||||
pTab = 0;
|
pTab = 0;
|
||||||
#ifndef SQLITE_OMIT_TRIGGER
|
#ifndef SQLITE_OMIT_TRIGGER
|
||||||
if( pParse->pTriggerTab!=0 ){
|
if( pParse->pTriggerTab!=0 ){
|
||||||
int op = pParse->eTriggerOp;
|
int op = pParse->eTriggerOp;
|
||||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
|
||||||
pExpr->iTable = 1;
|
pExpr->iTable = 1;
|
||||||
pTab = pParse->pTriggerTab;
|
pTab = pParse->pTriggerTab;
|
||||||
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
}else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){
|
||||||
pExpr->iTable = 0;
|
pExpr->iTable = 0;
|
||||||
pTab = pParse->pTriggerTab;
|
pTab = pParse->pTriggerTab;
|
||||||
|
}else if( pParse->bReturning ){
|
||||||
|
pExpr->iTable = op!=TK_DELETE;
|
||||||
|
pTab = pParse->pTriggerTab;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_TRIGGER */
|
#endif /* SQLITE_OMIT_TRIGGER */
|
||||||
#ifndef SQLITE_OMIT_UPSERT
|
#ifndef SQLITE_OMIT_UPSERT
|
||||||
if( (pNC->ncFlags & NC_UUpsert)!=0 ){
|
if( (pNC->ncFlags & NC_UUpsert)!=0 && ALWAYS(zTab) ){
|
||||||
Upsert *pUpsert = pNC->uNC.pUpsert;
|
Upsert *pUpsert = pNC->uNC.pUpsert;
|
||||||
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
||||||
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
||||||
|
@ -2115,7 +2115,13 @@ struct sqlite3_mem_methods {
|
|||||||
** The second parameter is a pointer to an integer into which
|
** The second parameter is a pointer to an integer into which
|
||||||
** is written 0 or 1 to indicate whether triggers are disabled or enabled
|
** is written 0 or 1 to indicate whether triggers are disabled or enabled
|
||||||
** following this call. The second parameter may be a NULL pointer, in
|
** following this call. The second parameter may be a NULL pointer, in
|
||||||
** which case the trigger setting is not reported back. </dd>
|
** which case the trigger setting is not reported back.
|
||||||
|
**
|
||||||
|
** <p>Originally this option disabled all triggers. ^(However, since
|
||||||
|
** SQLite version 3.35.0, TEMP triggers are still allowed even if
|
||||||
|
** this option is off. So, in other words, this option now only disables
|
||||||
|
** triggers in the main database schema or in the schemas of ATTACH-ed
|
||||||
|
** databases.)^ </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
||||||
|
@ -1159,6 +1159,7 @@ typedef struct ParseCleanup ParseCleanup;
|
|||||||
typedef struct PreUpdate PreUpdate;
|
typedef struct PreUpdate PreUpdate;
|
||||||
typedef struct PrintfArguments PrintfArguments;
|
typedef struct PrintfArguments PrintfArguments;
|
||||||
typedef struct RenameToken RenameToken;
|
typedef struct RenameToken RenameToken;
|
||||||
|
typedef struct Returning Returning;
|
||||||
typedef struct RowSet RowSet;
|
typedef struct RowSet RowSet;
|
||||||
typedef struct Savepoint Savepoint;
|
typedef struct Savepoint Savepoint;
|
||||||
typedef struct Select Select;
|
typedef struct Select Select;
|
||||||
@ -3429,6 +3430,7 @@ struct Parse {
|
|||||||
u32 oldmask; /* Mask of old.* columns referenced */
|
u32 oldmask; /* Mask of old.* columns referenced */
|
||||||
u32 newmask; /* Mask of new.* columns referenced */
|
u32 newmask; /* Mask of new.* columns referenced */
|
||||||
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
|
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
|
||||||
|
u8 bReturning; /* Coding a RETURNING trigger */
|
||||||
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
|
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
|
||||||
u8 disableTriggers; /* True to disable triggers */
|
u8 disableTriggers; /* True to disable triggers */
|
||||||
|
|
||||||
@ -3578,6 +3580,7 @@ struct Trigger {
|
|||||||
char *table; /* The table or view to which the trigger applies */
|
char *table; /* The table or view to which the trigger applies */
|
||||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
|
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
|
||||||
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
|
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
|
||||||
|
u8 bReturning; /* This trigger implements a RETURNING clause */
|
||||||
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
|
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
|
||||||
IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
|
IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
|
||||||
the <column-list> is stored here */
|
the <column-list> is stored here */
|
||||||
@ -3636,7 +3639,8 @@ struct Trigger {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct TriggerStep {
|
struct TriggerStep {
|
||||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
|
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT,
|
||||||
|
** or TK_RETURNING */
|
||||||
u8 orconf; /* OE_Rollback etc. */
|
u8 orconf; /* OE_Rollback etc. */
|
||||||
Trigger *pTrig; /* The trigger that this step is a part of */
|
Trigger *pTrig; /* The trigger that this step is a part of */
|
||||||
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
||||||
@ -3651,6 +3655,18 @@ struct TriggerStep {
|
|||||||
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
|
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Information about a RETURNING clause
|
||||||
|
*/
|
||||||
|
struct Returning {
|
||||||
|
Parse *pParse; /* The parse that includes the RETURNING clause */
|
||||||
|
ExprList *pReturnEL; /* List of expressions to return */
|
||||||
|
Trigger retTrig; /* The transient trigger that implements RETURNING */
|
||||||
|
TriggerStep retTStep; /* The trigger step */
|
||||||
|
Select retSel; /* The SELECT statement that implements RETURNING */
|
||||||
|
u64 retSrcList; /* The empty FROM clause of the SELECT */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** An objected used to accumulate the text of a string where we
|
** An objected used to accumulate the text of a string where we
|
||||||
** do not necessarily know how big the string will be in the end.
|
** do not necessarily know how big the string will be in the end.
|
||||||
@ -4252,6 +4268,7 @@ void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
|
|||||||
void sqlite3AddCollateType(Parse*, Token*);
|
void sqlite3AddCollateType(Parse*, Token*);
|
||||||
void sqlite3AddGenerated(Parse*,Expr*,Token*);
|
void sqlite3AddGenerated(Parse*,Expr*,Token*);
|
||||||
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
||||||
|
void sqlite3AddReturning(Parse*,ExprList*);
|
||||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||||
sqlite3_vfs**,char**,char **);
|
sqlite3_vfs**,char**,char **);
|
||||||
#define sqlite3CodecQueryParameters(A,B,C) 0
|
#define sqlite3CodecQueryParameters(A,B,C) 0
|
||||||
|
143
src/trigger.c
143
src/trigger.c
@ -65,11 +65,16 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
|||||||
while( p ){
|
while( p ){
|
||||||
Trigger *pTrig = (Trigger *)sqliteHashData(p);
|
Trigger *pTrig = (Trigger *)sqliteHashData(p);
|
||||||
if( pTrig->pTabSchema==pTab->pSchema
|
if( pTrig->pTabSchema==pTab->pSchema
|
||||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||||
){
|
){
|
||||||
pTrig->pNext = pList;
|
pTrig->pNext = pList;
|
||||||
pList = pTrig;
|
pList = pTrig;
|
||||||
}
|
}else if( pTrig->op==TK_RETURNING ){
|
||||||
|
pTrig->table = pTab->zName;
|
||||||
|
pTrig->pTabSchema = pTab->pSchema;
|
||||||
|
pTrig->pNext = pList;
|
||||||
|
pList = pTrig;
|
||||||
|
}
|
||||||
p = sqliteHashNext(p);
|
p = sqliteHashNext(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,7 +567,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
|
|||||||
** Recursively delete a Trigger structure
|
** Recursively delete a Trigger structure
|
||||||
*/
|
*/
|
||||||
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
|
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
|
||||||
if( pTrigger==0 ) return;
|
if( pTrigger==0 || pTrigger->bReturning ) return;
|
||||||
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
|
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
|
||||||
sqlite3DbFree(db, pTrigger->zName);
|
sqlite3DbFree(db, pTrigger->zName);
|
||||||
sqlite3DbFree(db, pTrigger->table);
|
sqlite3DbFree(db, pTrigger->table);
|
||||||
@ -727,15 +732,48 @@ Trigger *sqlite3TriggersExist(
|
|||||||
Trigger *pList = 0;
|
Trigger *pList = 0;
|
||||||
Trigger *p;
|
Trigger *p;
|
||||||
|
|
||||||
if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
|
pList = sqlite3TriggerList(pParse, pTab);
|
||||||
pList = sqlite3TriggerList(pParse, pTab);
|
assert( pList==0 || IsVirtual(pTab)==0
|
||||||
}
|
|| (pList->bReturning && pList->pNext==0) );
|
||||||
assert( pList==0 || IsVirtual(pTab)==0 );
|
if( pList!=0 ){
|
||||||
for(p=pList; p; p=p->pNext){
|
p = pList;
|
||||||
if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
|
if( (pParse->db->flags & SQLITE_EnableTrigger)==0
|
||||||
mask |= p->tr_tm;
|
&& pTab->pTrigger!=0
|
||||||
|
){
|
||||||
|
/* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that
|
||||||
|
** only TEMP triggers are allowed. Truncate the pList so that it
|
||||||
|
** includes only TEMP triggers */
|
||||||
|
if( pList==pTab->pTrigger ){
|
||||||
|
pList = 0;
|
||||||
|
goto exit_triggers_exist;
|
||||||
|
}
|
||||||
|
while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext;
|
||||||
|
p->pNext = 0;
|
||||||
|
p = pList;
|
||||||
}
|
}
|
||||||
|
do{
|
||||||
|
if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
|
||||||
|
mask |= p->tr_tm;
|
||||||
|
}else if( p->op==TK_RETURNING ){
|
||||||
|
/* The first time a RETURNING trigger is seen, the "op" value tells
|
||||||
|
** us what time of trigger it should be. */
|
||||||
|
assert( sqlite3IsToplevel(pParse) );
|
||||||
|
p->op = op;
|
||||||
|
mask |= TRIGGER_AFTER;
|
||||||
|
if( IsVirtual(pTab) && op!=TK_INSERT ){
|
||||||
|
sqlite3ErrorMsg(pParse,
|
||||||
|
"%s RETURNING is not available on virtual tables",
|
||||||
|
op==TK_DELETE ? "DELETE" : "UPDATE");
|
||||||
|
}
|
||||||
|
}else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
|
||||||
|
&& sqlite3IsToplevel(pParse) ){
|
||||||
|
/* Also fire a RETURNING trigger for an UPSERT */
|
||||||
|
mask |= TRIGGER_AFTER;
|
||||||
|
}
|
||||||
|
p = p->pNext;
|
||||||
|
}while( p );
|
||||||
}
|
}
|
||||||
|
exit_triggers_exist:
|
||||||
if( pMask ){
|
if( pMask ){
|
||||||
*pMask = mask;
|
*pMask = mask;
|
||||||
}
|
}
|
||||||
@ -778,6 +816,47 @@ SrcList *sqlite3TriggerStepSrc(
|
|||||||
return pSrc;
|
return pSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The input list pList is the list of result set terms from a RETURNING
|
||||||
|
** clause. The table that we are returning from is pTab.
|
||||||
|
**
|
||||||
|
** This routine makes a copy of the pList, and at the same time expands
|
||||||
|
** any "*" wildcards to be the complete set of columns from pTab.
|
||||||
|
*/
|
||||||
|
static ExprList *sqlite3ExpandReturning(
|
||||||
|
Parse *pParse, /* Parsing context */
|
||||||
|
ExprList *pList, /* The arguments to RETURNING */
|
||||||
|
Table *pTab /* The table being updated */
|
||||||
|
){
|
||||||
|
ExprList *pNew = 0;
|
||||||
|
sqlite3 *db = pParse->db;
|
||||||
|
int i;
|
||||||
|
for(i=0; i<pList->nExpr; i++){
|
||||||
|
Expr *pOldExpr = pList->a[i].pExpr;
|
||||||
|
if( ALWAYS(pOldExpr!=0) && pOldExpr->op==TK_ASTERISK ){
|
||||||
|
int jj;
|
||||||
|
for(jj=0; jj<pTab->nCol; jj++){
|
||||||
|
if( IsHiddenColumn(pTab->aCol+jj) ) continue;
|
||||||
|
Expr *pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zName);
|
||||||
|
pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
|
||||||
|
if( !db->mallocFailed ){
|
||||||
|
struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
|
||||||
|
pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zName);
|
||||||
|
pItem->eEName = ENAME_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0);
|
||||||
|
pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
|
||||||
|
if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){
|
||||||
|
struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
|
||||||
|
pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName);
|
||||||
|
pItem->eEName = pList->a[i].eEName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pNew;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate VDBE code for the statements inside the body of a single
|
** Generate VDBE code for the statements inside the body of a single
|
||||||
** trigger.
|
** trigger.
|
||||||
@ -827,6 +906,7 @@ static int codeTriggerProgram(
|
|||||||
sqlite3ExprDup(db, pStep->pWhere, 0),
|
sqlite3ExprDup(db, pStep->pWhere, 0),
|
||||||
pParse->eOrconf, 0, 0, 0
|
pParse->eOrconf, 0, 0, 0
|
||||||
);
|
);
|
||||||
|
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_INSERT: {
|
case TK_INSERT: {
|
||||||
@ -837,6 +917,7 @@ static int codeTriggerProgram(
|
|||||||
pParse->eOrconf,
|
pParse->eOrconf,
|
||||||
sqlite3UpsertDup(db, pStep->pUpsert)
|
sqlite3UpsertDup(db, pStep->pUpsert)
|
||||||
);
|
);
|
||||||
|
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_DELETE: {
|
case TK_DELETE: {
|
||||||
@ -844,9 +925,10 @@ static int codeTriggerProgram(
|
|||||||
sqlite3TriggerStepSrc(pParse, pStep),
|
sqlite3TriggerStepSrc(pParse, pStep),
|
||||||
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
||||||
);
|
);
|
||||||
|
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: assert( pStep->op==TK_SELECT ); {
|
case TK_SELECT: {
|
||||||
SelectDest sDest;
|
SelectDest sDest;
|
||||||
Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
|
Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
|
||||||
sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
|
sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
|
||||||
@ -854,10 +936,27 @@ static int codeTriggerProgram(
|
|||||||
sqlite3SelectDelete(db, pSelect);
|
sqlite3SelectDelete(db, pSelect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default: assert( pStep->op==TK_RETURNING ); {
|
||||||
|
Select *pSelect = pStep->pSelect;
|
||||||
|
ExprList *pList = pSelect->pEList;
|
||||||
|
SelectDest sDest;
|
||||||
|
Select *pNew;
|
||||||
|
pSelect->pEList =
|
||||||
|
sqlite3ExpandReturning(pParse, pList, pParse->pTriggerTab);
|
||||||
|
sqlite3SelectDestInit(&sDest, SRT_Output, 0);
|
||||||
|
pNew = sqlite3SelectDup(db, pSelect, 0);
|
||||||
|
if( pNew ){
|
||||||
|
sqlite3Select(pParse, pNew, &sDest);
|
||||||
|
if( pNew->selFlags & (SF_Aggregate|SF_HasAgg|SF_WinRewrite) ){
|
||||||
|
sqlite3ErrorMsg(pParse, "aggregates not allowed in RETURNING");
|
||||||
|
}
|
||||||
|
sqlite3SelectDelete(db, pNew);
|
||||||
|
}
|
||||||
|
sqlite3ExprListDelete(db, pSelect->pEList);
|
||||||
|
pStep->pSelect->pEList = pList;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if( pStep->op!=TK_SELECT ){
|
|
||||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -947,6 +1046,7 @@ static TriggerPrg *codeRowTrigger(
|
|||||||
pSubParse->pToplevel = pTop;
|
pSubParse->pToplevel = pTop;
|
||||||
pSubParse->zAuthContext = pTrigger->zName;
|
pSubParse->zAuthContext = pTrigger->zName;
|
||||||
pSubParse->eTriggerOp = pTrigger->op;
|
pSubParse->eTriggerOp = pTrigger->op;
|
||||||
|
pSubParse->bReturning = pTrigger->bReturning;
|
||||||
pSubParse->nQueryLoop = pParse->nQueryLoop;
|
pSubParse->nQueryLoop = pParse->nQueryLoop;
|
||||||
pSubParse->disableVtab = pParse->disableVtab;
|
pSubParse->disableVtab = pParse->disableVtab;
|
||||||
|
|
||||||
@ -996,6 +1096,9 @@ static TriggerPrg *codeRowTrigger(
|
|||||||
if( db->mallocFailed==0 && pParse->nErr==0 ){
|
if( db->mallocFailed==0 && pParse->nErr==0 ){
|
||||||
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
|
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
|
||||||
}
|
}
|
||||||
|
if( pTrigger->bReturning ){
|
||||||
|
sqlite3VdbeColumnInfoXfer(sqlite3ParseToplevel(pParse)->pVdbe, v);
|
||||||
|
}
|
||||||
pProgram->nMem = pSubParse->nMem;
|
pProgram->nMem = pSubParse->nMem;
|
||||||
pProgram->nCsr = pSubParse->nTab;
|
pProgram->nCsr = pSubParse->nTab;
|
||||||
pProgram->token = (void *)pTrigger;
|
pProgram->token = (void *)pTrigger;
|
||||||
@ -1150,12 +1253,20 @@ void sqlite3CodeRowTrigger(
|
|||||||
assert( p->pSchema==p->pTabSchema
|
assert( p->pSchema==p->pTabSchema
|
||||||
|| p->pSchema==pParse->db->aDb[1].pSchema );
|
|| p->pSchema==pParse->db->aDb[1].pSchema );
|
||||||
|
|
||||||
/* Determine whether we should code this trigger */
|
/* Determine whether we should code this trigger. One of two choices:
|
||||||
if( p->op==op
|
** 1. The trigger is an exact match to the current DML statement
|
||||||
|
** 2. This is a RETURNING trigger for INSERT but we are currently
|
||||||
|
** doing the UPDATE part of an UPSERT.
|
||||||
|
*/
|
||||||
|
if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
|
||||||
&& p->tr_tm==tr_tm
|
&& p->tr_tm==tr_tm
|
||||||
&& checkColumnOverlap(p->pColumns, pChanges)
|
&& checkColumnOverlap(p->pColumns, pChanges)
|
||||||
|
&& (sqlite3IsToplevel(pParse) || !p->bReturning)
|
||||||
){
|
){
|
||||||
|
u8 origOp = p->op;
|
||||||
|
p->op = op;
|
||||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||||
|
p->op = origOp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,6 +643,7 @@ void sqlite3Update(
|
|||||||
if( (db->flags&SQLITE_CountRows)!=0
|
if( (db->flags&SQLITE_CountRows)!=0
|
||||||
&& !pParse->pTriggerTab
|
&& !pParse->pTriggerTab
|
||||||
&& !pParse->nested
|
&& !pParse->nested
|
||||||
|
&& !pParse->bReturning
|
||||||
&& pUpsert==0
|
&& pUpsert==0
|
||||||
){
|
){
|
||||||
regRowCount = ++pParse->nMem;
|
regRowCount = ++pParse->nMem;
|
||||||
@ -1106,7 +1107,7 @@ void sqlite3Update(
|
|||||||
** that information.
|
** that information.
|
||||||
*/
|
*/
|
||||||
if( regRowCount ){
|
if( regRowCount ){
|
||||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
|
||||||
sqlite3VdbeSetNumCols(v, 1);
|
sqlite3VdbeSetNumCols(v, 1);
|
||||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
|
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
|
||||||
}
|
}
|
||||||
|
48
src/vdbe.c
48
src/vdbe.c
@ -1445,6 +1445,26 @@ case OP_IntCopy: { /* out2 */
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Opcode: ChngCntRow P1 P2 * * *
|
||||||
|
** Synopsis: output=r[P1]
|
||||||
|
**
|
||||||
|
** Output value in register P1 as the chance count for a DML statement,
|
||||||
|
** due to the "PRAGMA count_changes=ON" setting. Or, if there was a
|
||||||
|
** foreign key error in the statement, trigger the error now.
|
||||||
|
**
|
||||||
|
** This opcode is a variant of OP_ResultRow that checks the foreign key
|
||||||
|
** immediate constraint count and throws an error if the count is
|
||||||
|
** non-zero. The P2 opcode must be 1.
|
||||||
|
*/
|
||||||
|
case OP_ChngCntRow: {
|
||||||
|
assert( pOp->p2==1 );
|
||||||
|
if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
|
||||||
|
goto abort_due_to_error;
|
||||||
|
}
|
||||||
|
/* Fall through to the next case, OP_String */
|
||||||
|
/* no break */ deliberate_fall_through
|
||||||
|
}
|
||||||
|
|
||||||
/* Opcode: ResultRow P1 P2 * * *
|
/* Opcode: ResultRow P1 P2 * * *
|
||||||
** Synopsis: output=r[P1@P2]
|
** Synopsis: output=r[P1@P2]
|
||||||
**
|
**
|
||||||
@ -1461,34 +1481,6 @@ case OP_ResultRow: {
|
|||||||
assert( pOp->p1>0 );
|
assert( pOp->p1>0 );
|
||||||
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
|
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
|
||||||
|
|
||||||
/* If this statement has violated immediate foreign key constraints, do
|
|
||||||
** not return the number of rows modified. And do not RELEASE the statement
|
|
||||||
** transaction. It needs to be rolled back. */
|
|
||||||
if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
|
|
||||||
assert( db->flags&SQLITE_CountRows );
|
|
||||||
assert( p->usesStmtJournal );
|
|
||||||
goto abort_due_to_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
|
|
||||||
** DML statements invoke this opcode to return the number of rows
|
|
||||||
** modified to the user. This is the only way that a VM that
|
|
||||||
** opens a statement transaction may invoke this opcode.
|
|
||||||
**
|
|
||||||
** In case this is such a statement, close any statement transaction
|
|
||||||
** opened by this VM before returning control to the user. This is to
|
|
||||||
** ensure that statement-transactions are always nested, not overlapping.
|
|
||||||
** If the open statement-transaction is not closed here, then the user
|
|
||||||
** may step another VM that opens its own statement transaction. This
|
|
||||||
** may lead to overlapping statement transactions.
|
|
||||||
**
|
|
||||||
** The statement transaction is never a top-level transaction. Hence
|
|
||||||
** the RELEASE call below can never fail.
|
|
||||||
*/
|
|
||||||
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
|
|
||||||
rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
|
|
||||||
assert( rc==SQLITE_OK );
|
|
||||||
|
|
||||||
/* Invalidate all ephemeral cursor row caches */
|
/* Invalidate all ephemeral cursor row caches */
|
||||||
p->cacheCtr = (p->cacheCtr + 2)|1;
|
p->cacheCtr = (p->cacheCtr + 2)|1;
|
||||||
|
|
||||||
|
@ -259,6 +259,7 @@ void sqlite3VdbeResetStepResult(Vdbe*);
|
|||||||
void sqlite3VdbeRewind(Vdbe*);
|
void sqlite3VdbeRewind(Vdbe*);
|
||||||
int sqlite3VdbeReset(Vdbe*);
|
int sqlite3VdbeReset(Vdbe*);
|
||||||
void sqlite3VdbeSetNumCols(Vdbe*,int);
|
void sqlite3VdbeSetNumCols(Vdbe*,int);
|
||||||
|
void sqlite3VdbeColumnInfoXfer(Vdbe*,Vdbe*);
|
||||||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
||||||
void sqlite3VdbeCountChanges(Vdbe*);
|
void sqlite3VdbeCountChanges(Vdbe*);
|
||||||
sqlite3 *sqlite3VdbeDb(Vdbe*);
|
sqlite3 *sqlite3VdbeDb(Vdbe*);
|
||||||
|
@ -2595,6 +2595,23 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
|
|||||||
initMemArray(p->aColName, n, db, MEM_Null);
|
initMemArray(p->aColName, n, db, MEM_Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Transfer the column count and name information from one Vdbe to
|
||||||
|
** another.
|
||||||
|
*/
|
||||||
|
void sqlite3VdbeColumnInfoXfer(Vdbe *pTo, Vdbe *pFrom){
|
||||||
|
sqlite3 *db = pTo->db;
|
||||||
|
assert( db==pFrom->db );
|
||||||
|
if( pTo->nResColumn ){
|
||||||
|
releaseMemArray(pTo->aColName, pTo->nResColumn*COLNAME_N);
|
||||||
|
sqlite3DbFree(db, pTo->aColName);
|
||||||
|
}
|
||||||
|
pTo->aColName = pFrom->aColName;
|
||||||
|
pFrom->aColName = 0;
|
||||||
|
pTo->nResColumn = pFrom->nResColumn;
|
||||||
|
pFrom->nResColumn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Set the name of the idx'th column to be returned by the SQL statement.
|
** Set the name of the idx'th column to be returned by the SQL statement.
|
||||||
** zName must be a pointer to a nul terminated string.
|
** zName must be a pointer to a nul terminated string.
|
||||||
|
87
test/returning1.test
Normal file
87
test/returning1.test
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# 2021-01-28
|
||||||
|
#
|
||||||
|
# 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 the new RETURNING clause
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
set testprefix returning1
|
||||||
|
|
||||||
|
do_execsql_test 1.0 {
|
||||||
|
CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c DEFAULT 'pax');
|
||||||
|
INSERT INTO t1(b) VALUES(10),('happy'),(NULL) RETURNING a,b,c;
|
||||||
|
} {1 10 pax 2 happy pax 3 {} pax}
|
||||||
|
do_execsql_test 1.1 {
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {1 10 pax 2 happy pax 3 {} pax}
|
||||||
|
do_execsql_test 1.2 {
|
||||||
|
INSERT INTO t1(b,c) VALUES(5,99) RETURNING b,c,a,rowid;
|
||||||
|
} {5 99 4 4}
|
||||||
|
do_execsql_test 1.3 {
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {1 10 pax 2 happy pax 3 {} pax 4 5 99}
|
||||||
|
do_execsql_test 1.4 {
|
||||||
|
INSERT INTO t1 DEFAULT VALUES RETURNING *;
|
||||||
|
} {5 {} pax}
|
||||||
|
do_execsql_test 1.5 {
|
||||||
|
SELECT * FROM t1;
|
||||||
|
} {1 10 pax 2 happy pax 3 {} pax 4 5 99 5 {} pax}
|
||||||
|
do_execsql_test 1.6 {
|
||||||
|
CREATE TABLE t2(x,y,z);
|
||||||
|
INSERT INTO t2 VALUES(11,12,13),(21,'b','c'),(31,'b-value',4.75);
|
||||||
|
}
|
||||||
|
do_execsql_test 1.7 {
|
||||||
|
INSERT INTO t1 SELECT * FROM t2 RETURNING *;
|
||||||
|
} {11 12 13 21 b c 31 b-value 4.75}
|
||||||
|
do_execsql_test 1.8 {
|
||||||
|
SELECT *, '|' FROM t1;
|
||||||
|
} {1 10 pax | 2 happy pax | 3 {} pax | 4 5 99 | 5 {} pax | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||||
|
|
||||||
|
do_execsql_test 2.1 {
|
||||||
|
UPDATE t1 SET c='bellum' WHERE c='pax' RETURNING rowid, b, '|';
|
||||||
|
} {1 10 | 2 happy | 3 {} | 5 {} |}
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
SELECT *, '|' FROM t1;
|
||||||
|
} {1 10 bellum | 2 happy bellum | 3 {} bellum | 4 5 99 | 5 {} bellum | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||||
|
|
||||||
|
do_execsql_test 3.1 {
|
||||||
|
DELETE FROM t1 WHERE c='bellum' RETURNING rowid, *, '|';
|
||||||
|
} {1 1 10 bellum | 2 2 happy bellum | 3 3 {} bellum | 5 5 {} bellum |}
|
||||||
|
do_execsql_test 3.2 {
|
||||||
|
SELECT *, '|' FROM t1;
|
||||||
|
} {4 5 99 | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||||
|
|
||||||
|
do_execsql_test 4.1 {
|
||||||
|
CREATE TABLE t4(a INT, b INT DEFAULT 1234, c INT DEFAULT -16);
|
||||||
|
CREATE UNIQUE INDEX t4a ON t4(a);
|
||||||
|
INSERT INTO t4(a,b,c) VALUES(1,2,3);
|
||||||
|
} {}
|
||||||
|
do_execsql_test 4.2 {
|
||||||
|
INSERT INTO t4(a,b,c) VALUES(1,22,33)
|
||||||
|
ON CONFLICT(a) DO UPDATE SET b=44
|
||||||
|
RETURNING *;
|
||||||
|
} {1 44 3}
|
||||||
|
do_execsql_test 4.3 {
|
||||||
|
SELECT * FROM t4;
|
||||||
|
} {1 44 3}
|
||||||
|
do_execsql_test 4.4 {
|
||||||
|
DELETE FROM t4;
|
||||||
|
INSERT INTO t4 VALUES(1,2,3),(4,5,6),(7,8,9);
|
||||||
|
} {}
|
||||||
|
do_execsql_test 4.5 {
|
||||||
|
INSERT INTO t4(a,b,c) VALUES(2,3,4),(4,5,6),(5,6,7)
|
||||||
|
ON CONFLICT(a) DO UPDATE SET b=100
|
||||||
|
RETURNING *, '|';
|
||||||
|
} {2 3 4 | 4 100 6 | 5 6 7 |}
|
||||||
|
|
||||||
|
|
||||||
|
finish_test
|
@ -94,21 +94,31 @@ ifcapable {update_delete_limit} {
|
|||||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5}
|
execsql {DELETE FROM t1 ORDER BY x LIMIT 5}
|
||||||
execsql {SELECT count(*) FROM t1}
|
execsql {SELECT count(*) FROM t1}
|
||||||
} {15}
|
} {15}
|
||||||
|
create_test_data 4
|
||||||
|
do_test wherelimit-1.3b {
|
||||||
|
# limit 5
|
||||||
|
execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x, y LIMIT 5}
|
||||||
|
} {1 1 | 1 2 | 1 3 | 1 4 | 2 1 |}
|
||||||
|
do_test wherelimit-1.3c {
|
||||||
|
execsql {SELECT count(*) FROM t1}
|
||||||
|
} {11}
|
||||||
do_test wherelimit-1.4 {
|
do_test wherelimit-1.4 {
|
||||||
# limit 5, offset 2
|
# limit 5, offset 2
|
||||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET 2}
|
execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x LIMIT 5 OFFSET 2}
|
||||||
|
} {2 4 | 3 1 | 3 2 | 3 3 | 3 4 |}
|
||||||
|
do_test wherelimit-1.4cnt {
|
||||||
execsql {SELECT count(*) FROM t1}
|
execsql {SELECT count(*) FROM t1}
|
||||||
} {10}
|
} {6}
|
||||||
do_test wherelimit-1.5 {
|
do_test wherelimit-1.5 {
|
||||||
# limit 5, offset -2
|
# limit 5, offset -2
|
||||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET -2}
|
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET -2}
|
||||||
execsql {SELECT count(*) FROM t1}
|
execsql {SELECT count(*) FROM t1}
|
||||||
} {5}
|
} {1}
|
||||||
do_test wherelimit-1.6 {
|
do_test wherelimit-1.6 {
|
||||||
# limit -5 (no limit), offset 2
|
# limit -5 (no limit), offset 2
|
||||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 2, -5}
|
execsql {DELETE FROM t1 ORDER BY x LIMIT 2, -5}
|
||||||
execsql {SELECT count(*) FROM t1}
|
execsql {SELECT count(*) FROM t1}
|
||||||
} {2}
|
} {1}
|
||||||
do_test wherelimit-1.7 {
|
do_test wherelimit-1.7 {
|
||||||
# limit 5, offset -2 (no offset)
|
# limit 5, offset -2 (no offset)
|
||||||
execsql {DELETE FROM t1 ORDER BY x LIMIT -2, 5}
|
execsql {DELETE FROM t1 ORDER BY x LIMIT -2, 5}
|
||||||
@ -227,7 +237,9 @@ ifcapable {update_delete_limit} {
|
|||||||
} {11}
|
} {11}
|
||||||
create_test_data 6
|
create_test_data 6
|
||||||
do_test wherelimit-3.2 {
|
do_test wherelimit-3.2 {
|
||||||
execsql {UPDATE t1 SET y=1 WHERE x=1 LIMIT 5}
|
execsql {UPDATE t1 SET y=1 WHERE x=1 RETURNING x, old.y, '|' LIMIT 5}
|
||||||
|
} {1 1 | 1 2 | 1 3 | 1 4 | 1 5 |}
|
||||||
|
do_test wherelimit-3.2cnt {
|
||||||
execsql {SELECT count(*) FROM t1 WHERE y=1}
|
execsql {SELECT count(*) FROM t1 WHERE y=1}
|
||||||
} {10}
|
} {10}
|
||||||
do_test wherelimit-3.3 {
|
do_test wherelimit-3.3 {
|
||||||
|
@ -155,10 +155,16 @@ struct Keyword {
|
|||||||
# define WINDOWFUNC 0x00100000
|
# define WINDOWFUNC 0x00100000
|
||||||
#endif
|
#endif
|
||||||
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
|
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
|
||||||
# define GENCOL 0
|
# define GENCOL 0
|
||||||
#else
|
#else
|
||||||
# define GENCOL 0x00200000
|
# define GENCOL 0x00200000
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SQLITE_OMIT_RETURNING
|
||||||
|
# define RETURNING 0
|
||||||
|
#else
|
||||||
|
# define RETURNING 0x00400000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** These are the keywords
|
** These are the keywords
|
||||||
@ -280,6 +286,7 @@ static Keyword aKeywordTable[] = {
|
|||||||
{ "RENAME", "TK_RENAME", ALTER, 1 },
|
{ "RENAME", "TK_RENAME", ALTER, 1 },
|
||||||
{ "REPLACE", "TK_REPLACE", CONFLICT, 10 },
|
{ "REPLACE", "TK_REPLACE", CONFLICT, 10 },
|
||||||
{ "RESTRICT", "TK_RESTRICT", FKEY, 1 },
|
{ "RESTRICT", "TK_RESTRICT", FKEY, 1 },
|
||||||
|
{ "RETURNING", "TK_RETURNING", RETURNING, 10 },
|
||||||
{ "RIGHT", "TK_JOIN_KW", ALWAYS, 0 },
|
{ "RIGHT", "TK_JOIN_KW", ALWAYS, 0 },
|
||||||
{ "ROLLBACK", "TK_ROLLBACK", ALWAYS, 1 },
|
{ "ROLLBACK", "TK_ROLLBACK", ALWAYS, 1 },
|
||||||
{ "ROW", "TK_ROW", TRIGGER, 1 },
|
{ "ROW", "TK_ROW", TRIGGER, 1 },
|
||||||
|
Reference in New Issue
Block a user