From: drh <> Date: Thu, 29 Feb 2024 13:44:15 +0000 (+0000) Subject: Add the ability to DROP one or more objects of the same class X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6b4c974bece0f697411acc93f94622b0ac616bd1;p=thirdparty%2Fsqlite.git 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 --- diff --git a/manifest b/manifest index 28b15c7a43..8e933bf204 100644 --- a/manifest +++ b/manifest @@ -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 Add\sthe\sability\sto\sDROP\sone\sor\smore\sobjects\sof\sthe\ssame\sclass\nin\sa\ssingle\sstatement\sby\slisting\sthe\sobjects\sas\smultiple\sarguments\sto\sthe\nDROP\scommand. +D 2024-02-29T13:44:15.183 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -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 786e47a8ccefa14fbdca52e22bc51dbe24074a9c7dbe4f2afd2050736a7352c5 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b @@ -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 85e7464c09fe05a9c1e9ba85fb2fc2dcd647fbcfb6ed3e17424ec938a3daddc0 F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75 F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 @@ -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 a23049b9771927763ade8389fb6a0494324bad15394893895f6aa4e346e6b295 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 w test/drop1.test 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 803481f25020f3c25941f1e7d1a8071937820dea951e8798198b0b0fa3fb48ce +R b06b7d333b1bcab630a4dab891f8b26c +U drh +Z 594e686b84606bfb7b18560464880c6c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 2f2c96f612..c56c4d0e0a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -803481f25020f3c25941f1e7d1a8071937820dea951e8798198b0b0fa3fb48ce \ No newline at end of file +2266086cf08ee710338667d1cf0b1e81ce7380101707db272ce27124404068a0 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 15f8fe1d24..9302e0d195 100644 --- a/src/build.c +++ b/src/build.c @@ -3480,102 +3480,112 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ Vdbe *v; sqlite3 *db = pParse->db; int iDb; - - 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--; - - if( pTab==0 ){ - if( noErr ){ - sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); - sqlite3ForceNotReadOnly(pParse); + int ii; + + sqlite3ReadSchema(pParse); + assert( pName!=0 || pParse->nErr!=0 ); + for(ii=0; pParse->nErr==0 && iinSrc; 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[ii].zDatabase); + sqlite3ForceNotReadOnly(pParse); + } + testcase( ii+1nSrc ); + continue; } - goto exit_drop_table; - } - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - assert( iDb>=0 && iDbnDb ); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDb>=0 && iDbnDb ); - /* 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+1nSrc ); + 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+1nSrc ); + break; + } + if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ + testcase( ii>0 ); + testcase( ii+1nSrc ); + 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+1nSrc ); + 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+1nSrc ); + sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName); + break; + } + if( !isView && IsView(pTab) ){ + testcase( ii>0 ); + testcase( ii+1nSrc ); + sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName); + break; + } #endif - /* Generate code to remove the table from the schema table - ** on disk. - */ - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - if( !isView ){ - sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); - sqlite3FkDropTable(pParse, pName, pTab); + /* Generate code to remove the table from the schema table + ** on disk. + */ + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3BeginWriteOperation(pParse, 1, iDb); + if( !isView ){ + sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName); + sqlite3FkDropTable(pParse, pName, pTab); + } + sqlite3CodeDropTable(pParse, pTab, iDb, isView); } - sqlite3CodeDropTable(pParse, pTab, iDb, isView); } - -exit_drop_table: sqlite3SrcListDelete(db, pName); } @@ -4579,63 +4589,67 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ Vdbe *v; sqlite3 *db = pParse->db; int iDb; - - 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); + int ii; + + sqlite3ReadSchema(pParse); + assert( pName!=0 || pParse->nErr!=0 ); + for(ii=0; pParse->nErr==0 && iinSrc; ii++){ + 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+1nSrc ); + } + 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); -#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; + 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+1nSrc ); + break; } - if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX; - if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ - goto exit_drop_index; + 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) ){ + testcase( ii>0 ); + testcase( ii+1nSrc ); + 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+1nSrc ); + break; + } } - } #endif - /* Generate code to remove the index and from the schema table */ - v = sqlite3GetVdbe(pParse); - if( v ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3NestedParse(pParse, - "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'", - db->aDb[iDb].zDbSName, pIndex->zName - ); - sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); - sqlite3ChangeCookie(pParse, iDb); - destroyRootPage(pParse, pIndex->tnum, iDb); - sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); + /* Generate code to remove the index and from the schema table */ + v = sqlite3GetVdbe(pParse); + if( v ){ + sqlite3BeginWriteOperation(pParse, 1, iDb); + sqlite3NestedParse(pParse, + "DELETE FROM %Q." LEGACY_SCHEMA_TABLE + " WHERE name=%Q AND type='index'", + db->aDb[iDb].zDbSName, pIndex->zName + ); + sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName); + sqlite3ChangeCookie(pParse, iDb); + destroyRootPage(pParse, pIndex->tnum, iDb); + sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); + } } - -exit_drop_index: sqlite3SrcListDelete(db, pName); } diff --git a/src/parse.y b/src/parse.y index 37c9fa8bc9..9ee27de3d2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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); + if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X); +} +fullnamelist(A) ::= fullnamelist(L) COMMA nm(X) DOT nm(Y). { + A = sqlite3SrcListAppend(pParse,L,&X,&Y); + if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y); +} + %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 diff --git a/src/trigger.c b/src/trigger.c index 97ca249be5..c388bacfbe 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -624,35 +624,34 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ const char *zDb; const char *zName; sqlite3 *db = pParse->db; - - 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; inDb; 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); + int ii; + + sqlite3ReadSchema(pParse); + assert( pName!=0 || pParse->nErr!=0 ); + for(ii=0; pParse->nErr==0 && iinSrc; ii++){ + zDb = pName->a[ii].zDatabase; + zName = pName->a[ii].zName; + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); + for(i=OMIT_TEMPDB; inDb; 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+ii); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, zDb); + } + testcase( ii>0 ); + testcase( ii+1nSrc ); + pParse->checkSchema = 1; }else{ - sqlite3CodeVerifyNamedSchema(pParse, zDb); + sqlite3DropTriggerPtr(pParse, pTrigger); } - pParse->checkSchema = 1; - goto drop_trigger_cleanup; } - sqlite3DropTriggerPtr(pParse, pTrigger); - -drop_trigger_cleanup: sqlite3SrcListDelete(db, pName); } diff --git a/test/aggnested.test b/test/aggnested.test index f3539076bd..627bdf98ca 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -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; diff --git a/test/aggorderby.test b/test/aggorderby.test index eed1f83a7e..fe82d36bd1 100644 --- a/test/aggorderby.test +++ b/test/aggorderby.test @@ -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; diff --git a/test/alter3.test b/test/alter3.test index c6f26b0c50..649a50320d 100644 --- a/test/alter3.test +++ b/test/alter3.test @@ -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; } } {} } diff --git a/test/alter4.test b/test/alter4.test index c63ba6b072..6a778ed89f 100644 --- a/test/alter4.test +++ b/test/alter4.test @@ -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; } } {} diff --git a/test/analyze.test b/test/analyze.test index f97c78aff1..c42c6ca751 100644 --- a/test/analyze.test +++ b/test/analyze.test @@ -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; } } {} diff --git a/test/auth.test b/test/auth.test index 1d56f70343..9a1639a0ee 100644 --- a/test/auth.test +++ b/test/auth.test @@ -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 diff --git a/test/drop-many.test b/test/drop-many.test new file mode 100644 index 0000000000..999176398b --- /dev/null +++ b/test/drop-many.test @@ -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 diff --git a/test/fkey1.test b/test/fkey1.test index 46e7f64a19..82f7779ca4 100644 --- a/test/fkey1.test +++ b/test/fkey1.test @@ -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 {