Compare commits

...

12 Commits

Author SHA1 Message Date
drh 4760a0655f The ability to DROP multiple tables (or views or indexes or triggers) at once,
as implemented by this branch, appears to work.  However, this branch adds over
1000 bytes to the code footprint.  And the changes is significant, adding risk.
The benefit of being able to drop multiple tables in a single statement is not
seen to be enough to overcome those downsides, and so there are no immediate
plans to merge this branch.

FossilOrigin-Name: 1a49788afd90fa4415c6aae4b6bbad68f9306c1df1026762a9d88bcb40e4cd8f
2024-03-04 18:02:36 +00:00
drh 313aaff7cd Remove an unused line of code.
FossilOrigin-Name: 26272590691aee8569a1ea9c233f9b0e55ece63ed167696cc1ecfe3ca2548ba1
2024-03-02 12:17:08 +00:00
drh 4859e9c642 Simplifications to multi-DROP logic.
FossilOrigin-Name: e70d6be7511837ff11deac10b335e0f613dfa6dedcab269df9ce61c8d8495d2a
2024-03-02 11:02:04 +00:00
drh 3ffea6bc31 Improved rebustness against OOM and corruption.
FossilOrigin-Name: 3992f45033d7c1eb7b2bf7790fbf301c495f3e6ef1610a8d66d7357270a227e4
2024-03-02 00:29:11 +00:00
drh c5c3cde0e3 When doing DROP TABLE or DROP INDEX, use a heap to ensure that the various
btrees are dropped in the right order for autovacuum.

FossilOrigin-Name: e5bf9556ffd1d0880cc982d389e229246d0f3ddce4264905c6a7dd9c55d735e8
2024-03-01 22:42:16 +00:00
drh 29962f9194 RTREE uses multi-DROP to erase shadow tables.
FossilOrigin-Name: cf8a58d679d0cd30bf7c9208780855f0567b5e086422d59e3254b907923e0dd1
2024-03-01 19:08:45 +00:00
drh 0809210f9d Omit unreachable branches in the parser.
FossilOrigin-Name: 90de1f73f9aaab321c0f94b41b6d76ea966d8413d5ad864311d462780e1eadcc
2024-03-01 18:05:57 +00:00
drh b3e1f4cc3c Fix harmless compiler warning.
FossilOrigin-Name: 33d3bb848b578d8e7d1029c6603785d90c399d6bc5075d306561fc3c1ab8f202
2024-03-01 15:56:51 +00:00
drh b0f9538628 Improved handling of foreign-key constraints on a multi-drop of tables.
FossilOrigin-Name: 144e332f44b266a0e594f990088faccd1f0f08fe8e84f2efc9467d99fc14e84b
2024-03-01 14:47:01 +00:00
drh 19fb027eb5 When doing a multi-drop, delete btrees beginning with the largest root page
first and working toward smaller root pages, to avoid problems with root page
renumbering due to autovacuum.

FossilOrigin-Name: 37d4c5e207888b49c224275b05abfc1f63a2d8d2133bcc8970188b15387926ef
2024-03-01 12:21:45 +00:00
drh c3004fc223 Attempt to fix multi-object DROP. Working better, but still not perfect.
FossilOrigin-Name: 02f9fc73eef80f528097581580977aae46942baac2d7fb205c52f7f25472f2a8
2024-02-29 19:40:38 +00:00
drh 6b4c974bec Add the ability to DROP one or more objects of the same class
in a single statement by listing the objects as multiple arguments to the
DROP command.

FossilOrigin-Name: 2266086cf08ee710338667d1cf0b1e81ce7380101707db272ce27124404068a0
2024-02-29 13:44:15 +00:00
16 changed files with 578 additions and 285 deletions
+3 -3
View File
@@ -1053,9 +1053,9 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
int rc;
char *zCreate = sqlite3_mprintf(
"DROP TABLE '%q'.'%q_node';"
"DROP TABLE '%q'.'%q_rowid';"
"DROP TABLE '%q'.'%q_parent';",
"DROP TABLE '%q'.'%q_node',"
"'%q'.'%q_rowid',"
"'%q'.'%q_parent';",
pRtree->zDb, pRtree->zName,
pRtree->zDb, pRtree->zName,
pRtree->zDb, pRtree->zName
+20 -20
View File
@@ -1,5 +1,5 @@
C Fix\stwo\s-Werror=lto-type-mismatch\swarnings\sreported\sin\s[forum:ef62b57bd5|forum\spost\sef62b57bd5].
D 2024-02-29T10:55:02.008
C The\sability\sto\sDROP\smultiple\stables\s(or\sviews\sor\sindexes\sor\striggers)\sat\sonce,\nas\simplemented\sby\sthis\sbranch,\sappears\sto\swork.\s\sHowever,\sthis\sbranch\sadds\sover\n1000\sbytes\sto\sthe\scode\sfootprint.\s\sAnd\sthe\schanges\sis\ssignificant,\sadding\srisk.\nThe\sbenefit\sof\sbeing\sable\sto\sdrop\smultiple\stables\sin\sa\ssingle\sstatement\sis\snot\nseen\sto\sbe\senough\sto\sovercome\sthose\sdownsides,\sand\sso\sthere\sare\sno\simmediate\nplans\sto\smerge\sthis\sbranch.
D 2024-03-04T18:02:36.966
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -506,7 +506,7 @@ F ext/repair/test/checkindex01.test b530f141413b587c9eb78ff734de6bb79bc3515c3350
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/geopoly.c 0dd4775e896cee6067979d67aff7c998e75c2c9d9cd8d62a1a790c09cde7adca
F ext/rtree/rtree.c 1baf9f6aa7251ddecdb019d914df4db46eb227720c00f89c485c66dbfdc7863d
F ext/rtree/rtree.c c65a133bb697d20c0ac60ac09dc3a1cbf55500bfd6e9e730d2469aa6113480ed
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
F ext/rtree/rtree1.test 2b5b8c719c6a4abe377f57766f428a49af36a93061cb146cccfdc3b30000c0a4
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
@@ -692,7 +692,7 @@ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
F src/btree.c 285b493d843e7ba8ef78b6ae7d31238e904901dbc0c484f7904de4cf18fd8802
F src/btree.h 55066f513eb095db935169dab1dc2f7c7a747ef223c533f5d4ad4dfed346cbd0
F src/btreeInt.h 98aadb6dcb77b012cab2574d6a728fad56b337fc946839b9898c4b4c969e30b6
F src/build.c 04f1bcee189f045ab086d84fee95db42cb49df82ff8e84af8136309ff3c8a75f
F src/build.c 7ee3616b8a25577f2fb715d91eaa27f62a34e6ee6d0130cc8b9644c07875b759
F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b
@@ -702,7 +702,7 @@ F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
F src/expr.c 2803f5e7e3458ced24c0de48ec3640cd7a68ec61350d99c9f30b1bcd6640bd61
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00
F src/fkey.c 0592046ed7598625234298211cc1fe611efb3fe7870a3f1938038fd9092c82f9
F src/func.c 4204c56196847faefef57fa14e43b8e4d65eb8d7e65318abe463472e3fd148cb
F src/global.c 765a0656d6cbf043cb272ff0ae38f39cc46713539ffe6793258ed3eb4b188b52
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
@@ -739,7 +739,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c ff60e98138d2499082ac6230f01ac508aba545315debccfca2fd6042f5f10fcd
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y 6209f01e8e7495379571454744fa82a5cfc2e7eeb89e46dee3f410d73ea6252d
F src/parse.y 9a77520c83950ced6e8b981c7826ebfd8e4a5c6e4b5d9743639e357910180c94
F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
@@ -755,7 +755,7 @@ F src/shell.c.in 2ec564ed3ff0147036be313efeb47b3dbfb8753d5eb5ea0e90636427c6b3a36
F src/sqlite.h.in 19a2db3995a699bd7f6dfb423856242bfceb7ec849a93c91d241d19fc28d9f0f
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
F src/sqliteInt.h 58b7295a748ca5ed9e211510205b4a66a24c68f864225b81e19d4cf6038b40a1
F src/sqliteInt.h 532224eec9f53245c8450c662c2a9d06a1aec22cb28e5fc4cdfd0f67239e9049
F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -814,7 +814,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/treeview.c c6fc972683fd00f975d8b32a81c1f25d2fb7d4035366bf45c9f5622d3ccd70ee
F src/trigger.c 0905b96b04bb6658509f711a8207287f1315cdbc3df1a1b13ba6483c8e341c81
F src/trigger.c 15ca35f491c03197c3ccd5a0b3c5b926719cbe4e185b73da0507dbeb84a52d0e
F src/update.c 6904814dd62a7a93bbb86d9f1419c7f134a9119582645854ab02b36b676d9f92
F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242
F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e
@@ -845,14 +845,14 @@ F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867d
F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggfault.test 777f269d0da5b0c2524c7ff6d99ae9a93db4f1b1839a914dd2a12e3035c29829
F test/aggnested.test 610b0ce2c3e8f3daee25f9752800ee8d785db10da4aa1fbeea0ea1aabaf1d704
F test/aggorderby.test cc3abf5de64d46ff66395ca8c2346b66c2576d5aedb7bffc5b0742508856e3bf
F test/aggnested.test 4dfdbfab105cec5750a1eeb59284ffffc3b37a32aaaf09143df3f549e28e2081
F test/aggorderby.test af1e07766e8f752d54dd317aa1d956e8e896671a70ddb20228e8cdf9391023b1
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 2ecb8bbd52416642e41c9081182a8df05d42c75637afd4488aace78cc4b69e13
F test/alter.test 3c00eff1e2036b9f93e9cd0f3d3e63750ac87ecb5bc71b9d7bd07cbf2ac4c494
F test/alter2.test 7e3d26ab409df52df887b366a63902c3429b935c41cb962fd58ffc25784f2f19
F test/alter3.test ffc4ab29ce78a3517a66afd69b2730667e3471622509c283b2bd4c46f680fba3
F test/alter4.test 716caa071dd8a3c6d57225778d15d3c3cbf5e34b2e84ae44199aeb2bbf50a707
F test/alter3.test 0663df0cb3b9558ccc21abe642b792d4e2e747164156208e2a44ce88c09fe09b
F test/alter4.test 0ef8e62259bbc17495456d19e33a97b14cc2f634ef67bfdfb6a3f826eb9cfbc1
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
F test/alterauth2.test 48967abae0494d9a300d1c92473d99fcb66edfcc23579c89322f033f49410adc
F test/altercol.test 29fed774747777fbbaacdd865b4413ed2d0844a4c824f8af531b5c7d4a832087
@@ -870,7 +870,7 @@ F test/altertab2.test 62597b6fd08feaba1b6bfe7d31dac6117c67e06dc9ce9c478a3abe75b5
F test/altertab3.test 6c432fbb9963e0bd6549bf1422f6861d744ee5a80cb3298564e81e556481df16
F test/altertrig.test aacc980b657354fe2d3d4d3a004f07d04ccc1a93e5ef82d68a79088c274ddc6b
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test 2fb21d7d64748636384e6cb8998dbf83968caf644c07fcb4f76c18f2e7ede94b
F test/analyze.test e8ab7a3fa8b0294dd72c6d0acd822c3b69fd4ab9392789b29dd29daaa8601b4c
F test/analyze3.test 03f4b3d794760cf15da2d85a52df9bae300e51c8fefe9c36cfae1f86dc10d23f
F test/analyze4.test 68bd069f3ac7ac1e652ddd9f04f57d5606ddb4208450f5297005db7aa0dd707d
F test/analyze5.test fa5131952303ac4146aba101b116b9c8cb89e2637531c334a6df7f7d19dddc0d
@@ -898,7 +898,7 @@ F test/attach2.test 6d1e3a457ce260d6fc8e5945c07fba6c76dc2aa90e1c701f067b50ee88f7
F test/attach3.test c59d92791070c59272e00183b7353eeb94915976
F test/attach4.test 00e754484859998d124d144de6d114d920f2ed6ca2f961e6a7f4183c714f885e
F test/attachmalloc.test 67309af95c6b765c13e7d2279d7fccbef78e6eb0565d75d51cefd5dc88784549
F test/auth.test 5b8558a40571ebc55c1581cb7cec3b2348a699542a0a51b83ef21c6a953d95e3
F test/auth.test 8d4152a8dca10e6dbe15b0c6dfcc183d5e9a7f6f983d9ae6e05f70e947aed7c3
F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1
F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab
F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec
@@ -1069,6 +1069,7 @@ F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
F test/distinct.test 691c9e850b0d0b56b66e7e235453198cb4cf0760e324b7403d3c5abbeab0a014
F test/distinct2.test bb71cc7b5e58e895787f9910a788c254f679928d324732d063fe9bc202ecbe71
F test/distinctagg.test 40d7169ae5846caaf62c6e307d2ca3c333daf9b6f7cde888956a339a97afe85f
F test/drop-many.test 30dd091a4fd0a04b1a38e70c93f69c55749e62a3d895ac5e09277cd97caa5bbf
F test/e_blobbytes.test 4c01dfe4f12087b92b20705a3fdfded45dc4ed16d5a211fed4e1d2786ba68a52
F test/e_blobclose.test 692fc02a058476c2222a63d97e3f3b2b809c1842e5525ded7f854d540ac2e075
F test/e_blobopen.test 29f6055ee453b8e679fe9570c4d3acfedbef821622c5dad16875148c5952ef50
@@ -1119,7 +1120,7 @@ F test/filter1.test 590f8ba9a0cd0823b80d89ac75c5ce72276189cef9225d2436adaf1ee87f
F test/filter2.tcl 44e525497ce07382915f01bd29ffd0fa49dab3adb87253b5e5103ba8f93393e8
F test/filter2.test 3cc20eaea2ea1ab245197cc4a62468deb460b78f5aa9bd7d5d3353c2fe569bae
F test/filterfault.test c08fb491d698e8df6c122c98f7db1c65ffcfcad2c1ab0e07fa8a5be1b34eaa8b
F test/fkey1.test e563bcb4cb108ce3f40363cda4f84009dc89a39e2973076e5057ba99fca35378
F test/fkey1.test 6cf3fb520def0950799499b5bd0c6f22517cf7b6aee07523cbb52c1496aa8d00
F test/fkey2.test 1063d65e5923c054cfb8f0555a92a3ae0fa8c067275a33ee1715bd856cdb304c
F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
@@ -2176,9 +2177,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P d51c699ae413215d534f954c129691ef5d95d540dc5d4304cbba576c976d931c 29f94610dc8319709b8d3726a60e2a7a33ffbf4998c681f772da26c9701cc0b0
R 728f9a968510fa9cb95ea7ce7eec45f1
T +closed 29f94610dc8319709b8d3726a60e2a7a33ffbf4998c681f772da26c9701cc0b0 Closed\sby\sintegrate-merge.
U stephan
Z 37dee8fa6539319388e07ab4c515c697
P 26272590691aee8569a1ea9c233f9b0e55ece63ed167696cc1ecfe3ca2548ba1
R ffcb677589cb061417252fbc7f9600de
U drh
Z 148d8810a6ba162cbd3b9cd95d027cbb
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
803481f25020f3c25941f1e7d1a8071937820dea951e8798198b0b0fa3fb48ce
1a49788afd90fa4415c6aae4b6bbad68f9306c1df1026762a9d88bcb40e4cd8f
+292 -194
View File
@@ -3260,84 +3260,129 @@ void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){
#endif
/*
** Write code to erase the table with root-page iTable from database iDb.
** Also write code to modify the sqlite_schema table and internal schema
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
** The RootStack object holds a list (really a Heap) of btree root pages
** and schema numbers that need to be deleted using OP_Destroy.
**
** OP_Destroy opcodes must be issued in order of decreasing root page
** numbers in order to avoid having auto-vacuum disrupt subsequent
** OP_Destroy opcodes. For that reason, all pending OP_Destroy calls
** are accumulated in an instance of this object. Then at the end of
** code generation, this object is used to generate the OP_Destroy opcodes
** in decreasing order.
**
** The data structure is a max-heap. Each rootpage/schema-number combo
** is stored as a 64-bit integer, with the schema-number in the upper 32
** bits and the page number in the lower 32-bits. The root of the heap
** (RootStack.a[0]) is the largest entry in the heap. The children of
** heap entry i are i*2+1 and i*2+2. The heap always stays balanced by
** ensuring that a parent entry is larger than both children.
**
*/
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema");
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
/* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
** location iTable. The following code modifies the sqlite_schema table to
** reflect this.
**
** The "#NNN" in the SQL is a special constant that means whatever value
** is in register NNN. See grammar rules associated with the TK_REGISTER
** token for additional information.
*/
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE
" SET rootpage=%d WHERE #%d AND rootpage=#%d",
pParse->db->aDb[iDb].zDbSName, iTable, r1, r1);
#endif
sqlite3ReleaseTempReg(pParse, r1);
typedef struct RootStack RootStack;
struct RootStack {
u32 nAlloc; /* Slots allocated for a[] */
u32 nUsed; /* Slots used for in a[] */
u64 *a; /* Sorting heap. Each entry has iDb in the upper 32 bits and
** a page number in the lower 32 bits */
};
/*
** Add a new Pgno and iDb to the RootStack in question.
**
** The new entry is inserted at the of the heap (most distant child)
** and then the heap is rebalanced.
*/
static void rootStackPush(Parse *pParse, RootStack *p, Pgno pgno, int iDb){
u64 iNew;
int i, j;
if( p->nAlloc<p->nUsed+1 ){
p->nAlloc = p->nAlloc*2 + 12;
p->a = sqlite3DbRealloc(pParse->db, p->a, sizeof(Pgno)*p->nAlloc);
if( p->a==0 ){
p->nAlloc = p->nUsed = 0;
return;
}
}
assert( pgno>0 || CORRUPT_DB );
assert( iDb>=0 && iDb<pParse->db->nDb );
iNew = (((u64)iDb)<<32) | pgno;
i = p->nUsed++;
p->a[i] = iNew;
while( i>=1 && p->a[j = (i-1)/2]<p->a[i] ){
u64 tmp = p->a[i];
p->a[i] = p->a[j];
p->a[j] = tmp;
i = j;
}
}
/*
** Write VDBE code to erase table pTab and all associated indices on disk.
** Code to update the sqlite_schema tables and internal schema definitions
** in case a root-page belonging to another table is moved by the btree layer
** is also added (this can happen with an auto-vacuum database).
** Remove the largest entry from the RootStack heap. Rebalance the
** heap and then return that largest entry.
*/
static void destroyTable(Parse *pParse, Table *pTab){
/* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
** is not defined), then it is important to call OP_Destroy on the
** table and index root-pages in order, starting with the numerically
** largest root-page number. This guarantees that none of the root-pages
** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
** following were coded:
**
** OP_Destroy 4 0
** ...
** OP_Destroy 5 0
**
** and root page 5 happened to be the largest root-page number in the
** database, then root page 5 would be moved to page 4 by the
** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
** a free-list page.
*/
Pgno iTab = pTab->tnum;
Pgno iDestroyed = 0;
while( 1 ){
Index *pIdx;
Pgno iLargest = 0;
if( iDestroyed==0 || iTab<iDestroyed ){
iLargest = iTab;
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
Pgno iIdx = pIdx->tnum;
assert( pIdx->pSchema==pTab->pSchema );
if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
iLargest = iIdx;
}
}
if( iLargest==0 ){
return;
}else{
int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
assert( iDb>=0 && iDb<pParse->db->nDb );
destroyRootPage(pParse, iLargest, iDb);
iDestroyed = iLargest;
}
static u64 rootStackPop(RootStack *p){
u64 iMax;
int i, j;
assert( p->nUsed>0 );
assert( p->a!=0 );
iMax = p->a[0];
p->a[0] = p->a[--p->nUsed];
i = 0;
while( (j = i*2+1)<p->nUsed ){
u64 tmp;
if( j+1<p->nUsed && p->a[j+1]>p->a[j] ) j++;
if( p->a[i]>p->a[j] ) break;
tmp = p->a[i];
p->a[i] = p->a[j];
p->a[j] = tmp;
i = j;
}
return iMax;
}
/*
** Generate OP_Destroy opcodes for every btree named in the given
** RootStack object. Issue these OP_Destroy opcodes in order of decreasing
** root page number. Then clean up any memory used by the RootStack.
*/
static void rootStackCode(Parse *pParse, RootStack *p){
int iLastDb = -1;
int r1 = sqlite3GetTempReg(pParse);
int r2 = sqlite3GetTempReg(pParse);
int regReturn = sqlite3GetTempReg(pParse);
Vdbe *v = pParse->pVdbe;
int addrSub = 0;
while( pParse->nErr==0 && p->nUsed>0 ){
u64 iNext = rootStackPop(p);
Pgno pgno = iNext & 0xffffffff;
int iDb = (iNext>>32)&0xffff;
if( pgno<2 ) sqlite3ErrorMsg(pParse, "corrupt schema");
sqlite3MayAbort(pParse);
if( iDb!=iLastDb ){
/* Code a subroutine to that will update the schema table when
** a root page number changes. The old root page is in register r1.
** Root page is moved to the value in register r2. */
iLastDb = iDb;
sqlite3VdbeAddOp0(v, OP_Goto);
addrSub = sqlite3VdbeCurrentAddr(v);
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE
" SET rootpage=#%d WHERE rootpage=#%d",
pParse->db->aDb[iDb].zDbSName, r2, r1);
sqlite3VdbeAddOp1(v, OP_Return, regReturn);
sqlite3VdbeJumpHere(v, addrSub-1);
}
sqlite3VdbeAddOp3(v, OP_Destroy, pgno, r1, iDb);
sqlite3VdbeAddOp2(v, OP_IfNot, r1, sqlite3VdbeCurrentAddr(v)+3);
VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, pgno, r2);
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, addrSub);
}
if( pParse->nErr==0 ){
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, regReturn);
}
sqlite3DbFree(pParse->db, p->a);
}
/*
@@ -3367,7 +3412,12 @@ static void sqlite3ClearStatTables(
/*
** Generate code to drop a table.
*/
void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
static void sqlite3CodeDropTable(
Parse *pParse, /* Parsing context */
RootStack *pStack, /* List of pending OP_Destroys */
Table *pTab, /* Table to be dropped */
int iDb /* Schema holding pTab */
){
Vdbe *v;
sqlite3 *db = pParse->db;
Trigger *pTrigger;
@@ -3420,8 +3470,13 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
"DELETE FROM %Q." LEGACY_SCHEMA_TABLE
" WHERE tbl_name=%Q and type!='trigger'",
pDb->zDbSName, pTab->zName);
if( !isView && !IsVirtual(pTab) ){
destroyTable(pParse, pTab);
if( IsOrdinaryTable(pTab) ){
Index *pIdx;
rootStackPush(pParse, pStack, pTab->tnum, iDb);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->tnum==pTab->tnum ) continue;
rootStackPush(pParse, pStack, pIdx->tnum, iDb);
}
}
/* Remove the table entry from SQLite's internal schema and modify
@@ -3477,106 +3532,134 @@ static int tableMayNotBeDropped(sqlite3 *db, Table *pTab){
*/
void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
Table *pTab;
Vdbe *v;
sqlite3 *db = pParse->db;
int iDb;
int ii, jj;
RootStack rootStack;
if( db->mallocFailed ){
goto exit_drop_table;
}
assert( pParse->nErr==0 );
assert( pName->nSrc==1 );
if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
if( noErr ) db->suppressErr++;
assert( isView==0 || isView==LOCATE_VIEW );
pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
if( noErr ) db->suppressErr--;
memset(&rootStack, 0, sizeof(rootStack));
(void)sqlite3GetVdbe(pParse);
sqlite3ReadSchema(pParse);
assert( pName!=0 || pParse->nErr!=0 );
for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){
if( noErr ) db->suppressErr++;
assert( isView==0 || isView==LOCATE_VIEW );
pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[ii]);
if( noErr ) db->suppressErr--;
if( pTab==0 ){
if( noErr ){
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
sqlite3ForceNotReadOnly(pParse);
if( pTab==0 ){
if( noErr ){
sqlite3CodeVerifyNamedSchema(pParse, pName->a[ii].zDatabase);
sqlite3ForceNotReadOnly(pParse);
}
testcase( ii+1<pName->nSrc );
continue;
}
goto exit_drop_table;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 && iDb<db->nDb );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb>=0 && iDb<db->nDb );
/* If pTab is a virtual table, call ViewGetColumnNames() to ensure
** it is initialized.
*/
if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
goto exit_drop_table;
}
/* If pTab is a virtual table, call ViewGetColumnNames() to ensure
** it is initialized.
*/
if( IsVirtual(pTab) && sqlite3ViewGetColumnNames(pParse, pTab) ){
break;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code;
const char *zTab = SCHEMA_TABLE(iDb);
const char *zDb = db->aDb[iDb].zDbSName;
const char *zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
}
if( isView ){
if( !OMIT_TEMPDB && iDb==1 ){
code = SQLITE_DROP_TEMP_VIEW;
}else{
code = SQLITE_DROP_VIEW;
{
int code;
const char *zTab = SCHEMA_TABLE(iDb);
const char *zDb = db->aDb[iDb].zDbSName;
const char *zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
if( isView ){
if( !OMIT_TEMPDB && iDb==1 ){
code = SQLITE_DROP_TEMP_VIEW;
}else{
code = SQLITE_DROP_VIEW;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( IsVirtual(pTab) ){
code = SQLITE_DROP_VTABLE;
zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
}else if( IsVirtual(pTab) ){
code = SQLITE_DROP_VTABLE;
zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
#endif
}else{
if( !OMIT_TEMPDB && iDb==1 ){
code = SQLITE_DROP_TEMP_TABLE;
}else{
code = SQLITE_DROP_TABLE;
if( !OMIT_TEMPDB && iDb==1 ){
code = SQLITE_DROP_TEMP_TABLE;
}else{
code = SQLITE_DROP_TABLE;
}
}
if( sqlite3AuthCheck(pParse, code, pTab->zName, zArg2, zDb) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
}
if( sqlite3AuthCheck(pParse, code, pTab->zName, zArg2, zDb) ){
goto exit_drop_table;
}
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
goto exit_drop_table;
}
}
#endif
if( tableMayNotBeDropped(db, pTab) ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
if( tableMayNotBeDropped(db, pTab) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
break;
}
#ifndef SQLITE_OMIT_VIEW
/* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
if( isView && !IsView(pTab) ){
sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
goto exit_drop_table;
}
if( !isView && IsView(pTab) ){
sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
goto exit_drop_table;
}
/* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
if( isView && !IsView(pTab) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
break;
}
if( !isView && IsView(pTab) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
break;
}
#endif
/* If this table or view has appeared previously in the list of tables
** or views to be dropped, then the prior appearance is sufficient so
** skip this one. */
for(jj=ii-1; jj>=0 && pName->a[jj].pTab!=pTab; jj--){}
if( jj>=0 ) continue;
/* Generate code to remove the table from the schema table
** on disk.
*/
v = sqlite3GetVdbe(pParse);
if( v ){
/* Remember the table for use in the second pass */
pName->a[ii].pTab = pTab;
pTab->nTabRef++;
/* Generate code to clear this table from sqlite_statN and to
** cascade foreign key constraints.
*/
sqlite3BeginWriteOperation(pParse, 1, iDb);
if( !isView ){
if( IsOrdinaryTable(pTab) ){
sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
sqlite3FkDropTable(pParse, pName, pTab);
}
sqlite3CodeDropTable(pParse, pTab, iDb, isView);
}
exit_drop_table:
/* Generate code to actually delete the tables/views in a second pass.
** Btrees must be deleted largest root page first, to avoid problems
** caused by autovacuum page reordering. */
for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){
pTab = pName->a[ii].pTab;
if( pTab==0 ) continue;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3CodeDropTable(pParse, &rootStack, pTab, iDb);
}
sqlite3SrcListDelete(db, pName);
rootStackCode(pParse, &rootStack);
}
/*
@@ -4576,67 +4659,82 @@ void sqlite3DefaultRowEst(Index *pIdx){
*/
void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
Index *pIndex;
Vdbe *v;
sqlite3 *db = pParse->db;
Vdbe *v;
int iDb;
int ii, jj;
RootStack rootStack;
if( db->mallocFailed ){
goto exit_drop_index;
}
assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */
assert( pName->nSrc==1 );
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_drop_index;
}
pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
if( pIndex==0 ){
if( !ifExists ){
sqlite3ErrorMsg(pParse, "no such index: %S", pName->a);
}else{
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
sqlite3ForceNotReadOnly(pParse);
memset(&rootStack, 0, sizeof(rootStack));
v = sqlite3GetVdbe(pParse);
sqlite3ReadSchema(pParse);
assert( pName!=0 || pParse->nErr!=0 );
for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){
pName->a[ii].regReturn = 0;
pIndex = sqlite3FindIndex(db, pName->a[ii].zName, pName->a[ii].zDatabase);
if( pIndex==0 ){
if( !ifExists ){
sqlite3ErrorMsg(pParse, "no such index: %S", pName->a+ii);
}else{
sqlite3CodeVerifyNamedSchema(pParse, pName->a[ii].zDatabase);
sqlite3ForceNotReadOnly(pParse);
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
}
pParse->checkSchema = 1;
continue;
}
pParse->checkSchema = 1;
goto exit_drop_index;
}
if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
"or PRIMARY KEY constraint cannot be dropped", 0);
goto exit_drop_index;
}
iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
"or PRIMARY KEY constraint cannot be dropped", 0);
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
break;
}
}
if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
goto exit_drop_index;
}
}
#endif
/* Generate code to remove the index and from the schema table */
v = sqlite3GetVdbe(pParse);
if( v ){
/* Skip over redundant DROP INDEXes */
for(jj=ii-1; jj>=0 && pName->a[jj].u2.pIdx!=pIndex; jj--){}
if( jj>=0 ) continue;
/* Record that this index needs to be dropped. */
pName->a[ii].u2.pIdx = pIndex;
/* Generate code to remove the index and from the schema table and
** from sqlite_statN tables */
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'",
"DELETE FROM %Q." LEGACY_SCHEMA_TABLE
" WHERE name=%Q AND type='index'",
db->aDb[iDb].zDbSName, pIndex->zName
);
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
rootStackPush(pParse, &rootStack, pIndex->tnum, iDb);
sqlite3ChangeCookie(pParse, iDb);
destroyRootPage(pParse, pIndex->tnum, iDb);
sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
}
exit_drop_index:
sqlite3SrcListDelete(db, pName);
rootStackCode(pParse, &rootStack);
}
/*
+25 -6
View File
@@ -718,8 +718,9 @@ void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){
/*
** This function is called to generate code that runs when table pTab is
** being dropped from the database. The SrcList passed as the second argument
** to this function contains a single entry guaranteed to resolve to
** table pTab.
** to this function contains entries for every table that is being dropped,
** with the SrcItem.pTab line being set to the table being dropped. pTab
** will be one of those tables.
**
** Normally, no code is required. However, if either
**
@@ -728,15 +729,23 @@ void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){
** determined at runtime that there are outstanding deferred FK
** constraint violations in the database,
**
** then the equivalent of "DELETE FROM <tbl>" is executed before dropping
** the table from the database. Triggers are disabled while running this
** DELETE, but foreign key actions are not.
** then the equivalent of "DELETE FROM <tbl>" is executed for every table
** being dropped, in the order specified in the DROP TABLE statement, which
** is the same as the order in which these tables appear in pName.
** Triggers are disabled while running these DELETEs, but foreign key
** actions are not.
**
** This routine sets the SrcList.addrFillSub value for all pName entries
** for which DELETE FROM has been run. This prevents the DELETE FROM
** from being run multiple times.
*/
void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
sqlite3 *db = pParse->db;
if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){
int iSkip = 0;
Vdbe *v = sqlite3GetVdbe(pParse);
SrcList *pSrc;
int ii;
assert( v ); /* VDBE has already been allocated */
assert( IsOrdinaryTable(pTab) );
@@ -756,7 +765,17 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
}
pParse->disableTriggers = 1;
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0);
for(ii=0; ii<pName->nSrc; ii++){
if( pName->a[ii].pTab==0 ) continue;
if( pName->a[ii].addrFillSub ) continue;
pName->a[ii].addrFillSub = 1;
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pSrc ){
pSrc->a[0].zDatabase = sqlite3DbStrDup(db, pName->a[ii].zDatabase);
pSrc->a[0].zName = sqlite3DbStrDup(db, pName->a[ii].zName);
sqlite3DeleteFrom(pParse, pSrc, 0, 0, 0);
}
}
pParse->disableTriggers = 0;
/* If the DELETE has generated immediate foreign key constraint
+16 -4
View File
@@ -471,7 +471,7 @@ resolvetype(A) ::= REPLACE. {A = OE_Replace;}
////////////////////////// The DROP TABLE /////////////////////////////////////
//
cmd ::= DROP TABLE ifexists(E) fullname(X). {
cmd ::= DROP TABLE ifexists(E) fullnamelist(X). {
sqlite3DropTable(pParse, X, 0, E);
}
%type ifexists {int}
@@ -485,7 +485,7 @@ cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z) eidlist_opt(C)
AS select(S). {
sqlite3CreateView(pParse, &X, &Y, &Z, C, S, T, E);
}
cmd ::= DROP VIEW ifexists(E) fullname(X). {
cmd ::= DROP VIEW ifexists(E) fullnamelist(X). {
sqlite3DropTable(pParse, X, 1, E);
}
%endif SQLITE_OMIT_VIEW
@@ -774,6 +774,18 @@ fullname(A) ::= nm(X) DOT nm(Y). {
if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y);
}
%type fullnamelist {SrcList*}
%destructor fullnamelist {sqlite3SrcListDelete(pParse->db, $$);}
fullnamelist(A) ::= fullname(A).
fullnamelist(A) ::= fullnamelist(L) COMMA nm(X). {
A = sqlite3SrcListAppend(pParse,L,&X,0);
assert( !IN_RENAME_OBJECT ); /* Used only by DROP, which cannot be part of schema */
}
fullnamelist(A) ::= fullnamelist(L) COMMA nm(X) DOT nm(Y). {
A = sqlite3SrcListAppend(pParse,L,&X,&Y);
assert( !IN_RENAME_OBJECT ); /* Used only by DROP, which cannot be part of schema */
}
%type xfullname {SrcList*}
%destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
xfullname(A) ::= nm(X).
@@ -1504,7 +1516,7 @@ collate(C) ::= COLLATE ids. {C = 1;}
///////////////////////////// The DROP INDEX command /////////////////////////
//
cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
cmd ::= DROP INDEX ifexists(E) fullnamelist(X). {sqlite3DropIndex(pParse, X, E);}
///////////////////////////// The VACUUM command /////////////////////////////
//
@@ -1661,7 +1673,7 @@ raisetype(A) ::= FAIL. {A = OE_Fail;}
//////////////////////// DROP TRIGGER statement //////////////////////////////
%ifndef SQLITE_OMIT_TRIGGER
cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
cmd ::= DROP TRIGGER ifexists(NOERR) fullnamelist(X). {
sqlite3DropTrigger(pParse,X,NOERR);
}
%endif !SQLITE_OMIT_TRIGGER
+3 -2
View File
@@ -3314,6 +3314,8 @@ struct SrcItem {
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */
Trigger *pTrig; /* Trigger in argument list of DROP TRIGGER */
Index *pIdx; /* Index in argument list to DROP INDEX */
} u2;
};
@@ -4926,7 +4928,6 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int);
int sqlite3DbMaskAllZero(yDbMask);
#endif
void sqlite3DropTable(Parse*, SrcList*, int, int);
void sqlite3CodeDropTable(Parse*, Table*, int, int);
void sqlite3DeleteTable(sqlite3*, Table*);
void sqlite3DeleteTableGeneric(sqlite3*, void*);
void sqlite3FreeIndex(sqlite3*, Index*);
@@ -5574,7 +5575,7 @@ const char *sqlite3JournalModename(int);
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
void sqlite3FkDropTable(Parse*, SrcList *, Table*);
void sqlite3FkDropTable(Parse*, SrcList*, Table*);
void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
int sqlite3FkRequired(Parse*, Table*, int*, int);
u32 sqlite3FkOldmask(Parse*, Table*);
+30 -26
View File
@@ -624,35 +624,39 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){
const char *zDb;
const char *zName;
sqlite3 *db = pParse->db;
int ii, jj;
if( db->mallocFailed ) goto drop_trigger_cleanup;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto drop_trigger_cleanup;
}
assert( pName->nSrc==1 );
zDb = pName->a[0].zDatabase;
zName = pName->a[0].zName;
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
if( pTrigger ) break;
}
if( !pTrigger ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a);
}else{
sqlite3CodeVerifyNamedSchema(pParse, zDb);
sqlite3ReadSchema(pParse);
assert( pName!=0 || pParse->nErr!=0 );
for(ii=0; pParse->nErr==0 && ii<pName->nSrc; ii++){
zDb = pName->a[ii].zDatabase;
zName = pName->a[ii].zName;
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
if( pTrigger ) break;
}
pParse->checkSchema = 1;
goto drop_trigger_cleanup;
pName->a[ii].u2.pTrig = pTrigger;
if( !pTrigger ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a+ii);
}else{
sqlite3CodeVerifyNamedSchema(pParse, zDb);
}
testcase( ii>0 );
testcase( ii+1<pName->nSrc );
pParse->checkSchema = 1;
continue;
}
for(jj=ii-1; jj>=0; jj--){
if( pName->a[jj].u2.pTrig==pTrigger ) break;
}
if( jj>=0 ) continue;
sqlite3DropTriggerPtr(pParse, pTrigger);
}
sqlite3DropTriggerPtr(pParse, pTrigger);
drop_trigger_cleanup:
sqlite3SrcListDelete(db, pName);
}
+6 -12
View File
@@ -117,8 +117,7 @@ do_test aggnested-3.0 {
#
do_test aggnested-3.1 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (
id1 INTEGER PRIMARY KEY AUTOINCREMENT,
value1 INTEGER
@@ -150,8 +149,7 @@ do_test aggnested-3.1-rj {
do_test aggnested-3.2 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (
id1 INTEGER,
value1 INTEGER,
@@ -178,8 +176,7 @@ do_test aggnested-3.2 {
} {1 0}
do_test aggnested-3.3 {
db eval {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t2;
DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1(id1, value1);
INSERT INTO t1 VALUES(4469,2),(4469,1);
CREATE TABLE t2 (value2);
@@ -249,8 +246,7 @@ do_test aggnested-3.16 {
# Problem found by dbsqlfuzz
#
do_execsql_test aggnested-4.1 {
DROP TABLE IF EXISTS aa;
DROP TABLE IF EXISTS bb;
DROP TABLE IF EXISTS aa, bb;
CREATE TABLE aa(x INT); INSERT INTO aa(x) VALUES(123);
CREATE TABLE bb(y INT); INSERT INTO bb(y) VALUES(456);
SELECT (SELECT sum(x+(SELECT y)) FROM bb) FROM aa;
@@ -259,8 +255,7 @@ do_execsql_test aggnested-4.2 {
SELECT (SELECT sum(x+y) FROM bb) FROM aa;
} {579}
do_execsql_test aggnested-4.3 {
DROP TABLE IF EXISTS tx;
DROP TABLE IF EXISTS ty;
DROP TABLE IF EXISTS tx, ty;
CREATE TABLE tx(x INT);
INSERT INTO tx VALUES(1),(2),(3),(4),(5);
CREATE TABLE ty(y INT);
@@ -477,8 +472,7 @@ do_execsql_test 9.5 {
# https://bugs.chromium.org/p/chromium/issues/detail?id=1511689
#
do_execsql_test 10.1 {
DROP TABLE IF EXISTS t0;
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t0, t1;
CREATE TABLE t0(c1, c2); INSERT INTO t0 VALUES(1,2);
CREATE TABLE t1(c3, c4); INSERT INTO t1 VALUES(3,4);
SELECT * FROM t0 WHERE EXISTS (SELECT 1 FROM t1 GROUP BY c3 HAVING ( SELECT count(*) FROM (SELECT 1 UNION ALL SELECT sum(DISTINCT c1) ) ) ) BETWEEN 1 AND 1;
+1 -2
View File
@@ -60,8 +60,7 @@ do_execsql_test aggorderby-4.1 {
do_execsql_test aggorderby-5.0 {
DROP TABLE IF EXISTS t1;
DROP TABLE IF EXISTS t3;
DROP TABLE IF EXISTS t1, t3;
CREATE TABLE t1(a TEXT); INSERT INTO t1 VALUES('aaa'),('bbb');
CREATE TABLE t3(d TEXT); INSERT INTO t3 VALUES('/'),('-');
SELECT (SELECT string_agg(a,d) FROM t3) FROM t1;
+2 -5
View File
@@ -105,9 +105,7 @@ do_test alter3-1.99 {
DROP TABLE t2;
}
execsql {
DROP TABLE abc;
DROP TABLE t1;
DROP TABLE t3;
DROP TABLE abc, t1, t3;
}
} {}
@@ -296,8 +294,7 @@ ifcapable attach {
} {1 one 2 two}
do_test alter3-5.99 {
execsql {
DROP TABLE aux.t1;
DROP TABLE t1;
DROP TABLE aux.t1, main.t1;
}
} {}
}
+1 -3
View File
@@ -114,9 +114,7 @@ do_test alter4-1.99 {
DROP TABLE t2;
}
execsql {
DROP TABLE abc;
DROP TABLE t1;
DROP TABLE t3;
DROP TABLE abc, t1, t3;
}
} {}
+1 -2
View File
@@ -195,8 +195,7 @@ do_test analyze-3.8 {
CREATE INDEX t3i1 ON t3(a);
CREATE INDEX t3i2 ON t3(a,b,c,d);
CREATE INDEX t3i3 ON t3(d,b,c,a);
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t1, t2;
SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
}
} {}
+5 -5
View File
@@ -216,7 +216,7 @@ do_test auth-1.21.1 {
}
return SQLITE_OK
}
catchsql {DROP TABLE t2}
catchsql {DROP TABLE IF EXISTS none1, none2, t2, none3}
} {1 {not authorized}}
do_test auth-1.21.2 {
set ::authargs
@@ -483,7 +483,7 @@ do_test auth-1.63 {
}
return SQLITE_OK
}
catchsql {DROP TABLE t2}
catchsql {DROP TABLE IF EXISTS none1, t2, none2}
} {1 {not authorized}}
do_test auth-1.64 {
execsql {SELECT name FROM sqlite_master}
@@ -726,7 +726,7 @@ do_test auth-1.101 {
}
return SQLITE_OK
}
catchsql {DROP VIEW v2}
catchsql {DROP VIEW IF EXISTS none1, v2, none2}
} {1 {not authorized}}
do_test auth-1.102 {
set ::authargs
@@ -1090,7 +1090,7 @@ do_test auth-1.153 {
}
return SQLITE_OK
}
catchsql {DROP TRIGGER r2}
catchsql {DROP TRIGGER IF EXISTS none1, r2, none2}
} {1 {not authorized}}
do_test auth-1.154 {
set ::authargs
@@ -1389,7 +1389,7 @@ do_test auth-1.205 {
}
return SQLITE_OK
}
catchsql {DROP INDEX i2}
catchsql {DROP INDEX IF EXISTS none1, i2, none2}
} {1 {not authorized}}
do_test auth-1.205a {
set ::authargs
+161
View File
@@ -0,0 +1,161 @@
# 2024-02-29
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Test cases for DROP TABLE, DROP INDEX, DROP TRIGGER, and DROP VIEW that
# list multiple objects to be dropped.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix drop-many
do_execsql_test 1.1 {
CREATE TABLE t1(a);
CREATE TABLE t2(b, c UNIQUE);
CREATE TABLE t3(d TEXT PRIMARY KEY, e);
CREATE INDEX t3e ON t3(e);
ATTACH ':memory:' AS aux1;
CREATE TABLE aux1.t4(f INT, g INT, h INT PRIMARY KEY) WITHOUT ROWID;
CREATE INDEX aux1.t4g ON t4(g);
CREATE INDEX t2b ON t2(b);
CREATE VIEW v5 AS SELECT 1,2,3;
CREATE VIEW v6 AS SELECT * FROM t3;
CREATE VIEW aux1.v7 AS SELECT 'hello';
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT 'this is trigger r1'; END;
CREATE TRIGGER r2 BEFORE DELETE ON t2 BEGIN INSERT INTO t1 VALUES(old.b); END;
CREATE TRIGGER aux1.r3 AFTER UPDATE ON t4 BEGIN SELECT 'trigger r3'; END;
CREATE TRIGGER aux1.r4 INSTEAD OF UPDATE ON v7 BEGIN SELECT NULL; END;
} {}
do_execsql_test 1.2 {
BEGIN;
DROP TABLE t2, t4, t3, t1;
SELECT name FROM sqlite_schema WHERE type='table'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='table'
ORDER BY name;
ROLLBACK;
} {}
do_catchsql_test 1.3.1 {
DROP TABLE t2, t4, t3, t05, t1;
} {1 {no such table: t05}}
do_execsql_test 1.3.2 {
SELECT name FROM sqlite_schema WHERE type='table'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='table'
ORDER BY name;
} {t1 t2 t3 t4}
do_execsql_test 1.4 {
BEGIN;
DROP TABLE IF EXISTS t01, t2, t02, t4, t03, t3, t04, t1, t05;
SELECT name FROM sqlite_schema WHERE type='table'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='table'
ORDER BY name;
ROLLBACK;
} {}
do_catchsql_test 1.5.1 {
DROP TABLE IF EXISTS t2, t4, t3, v7, t1;
} {1 {use DROP VIEW to delete view v7}}
do_execsql_test 1.5.2 {
SELECT name FROM sqlite_schema WHERE type='table'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='table'
ORDER BY name;
} {t1 t2 t3 t4}
do_execsql_test 2.1 {
BEGIN;
DROP VIEW v5, v6, v7;
SELECT name FROM sqlite_schema WHERE type='view'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='view'
ORDER BY name;
ROLLBACK;
} {}
do_catchsql_test 2.2.1 {
DROP VIEW v5, v6, v8, v7;
} {1 {no such view: v8}}
do_execsql_test 2.2.2 {
SELECT name FROM sqlite_schema WHERE type='view'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='view'
ORDER BY name;
} {v5 v6 v7}
do_catchsql_test 2.3.1 {
DROP VIEW v5, v6, t1, v7;
} {1 {use DROP TABLE to delete table t1}}
do_execsql_test 2.3.2 {
SELECT name FROM sqlite_schema WHERE type='view'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='view'
ORDER BY name;
} {v5 v6 v7}
do_execsql_test 3.1 {
BEGIN;
DROP INDEX t2b, aux1.t4g, main.t3e;
SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='index'
ORDER BY name;
ROLLBACK;
} {}
do_catchsql_test 3.2.1 {
DROP INDEX t2b, aux1.t4g, t1, main.t3e;
} {1 {no such index: t1}}
do_execsql_test 3.2.2 {
SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='index'
ORDER BY name;
} {t2b t3e t4g}
do_execsql_test 3.3 {
BEGIN;
DROP INDEX IF EXISTS aux1.none, t2b, none2, aux1.t4g, main.t3e, none3;
SELECT name FROM sqlite_schema WHERE type='index' AND name NOT LIKE 'sqlite%'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='index'
ORDER BY name;
ROLLBACK;
} {}
do_execsql_test 4.1 {
BEGIN;
DROP TRIGGER main.r1, r2, r3, aux1.r4;
SELECT name FROM sqlite_schema WHERE type='trigger'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='trigger'
ORDER BY name;
ROLLBACK;
} {}
do_catchsql_test 4.2.1 {
DROP TRIGGER main.r1, r2, r3, main.t1, aux1.r4;
} {1 {no such trigger: main.t1}}
do_execsql_test 4.2.2 {
SELECT name FROM sqlite_schema WHERE type='trigger'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='trigger'
ORDER BY name;
} {r1 r2 r3 r4}
do_execsql_test 4.3 {
BEGIN;
DROP TRIGGER IF EXISTS none1, main.r1, r2, aux1.none2, r3, aux1.r4;
SELECT name FROM sqlite_schema WHERE type='trigger'
UNION ALL
SELECT name from aux1.sqlite_schema WHERE type='trigger'
ORDER BY name;
ROLLBACK;
} {}
finish_test
+11
View File
@@ -71,6 +71,17 @@ do_test fkey1-2.1 {
DROP TABLE t10;
}
} {}
do_test fkey1-2.2 {
execsql {
CREATE TABLE t5(x references t4);
CREATE TABLE t6(x references t4);
CREATE TABLE t7(x references t4);
CREATE TABLE t8(x references t4);
CREATE TABLE t9(x references t4);
CREATE TABLE t10(x references t4);
DROP TABLE t7, t9, t5, t8, t6, t10;
}
} {}
do_test fkey1-3.1 {
execsql {