diff --git a/manifest b/manifest index 6aff9ae547..ee32bec370 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\san\sunnecessary\sIN_RENAME_COLUMN\smacro\sfrom\sresolve.c. -D 2018-08-25T03:29:34.617 +C Extend\sRENAME\sTABLE\sto\sedit\striggers\sand\sviews.\sStill\sbuggy. +D 2018-08-29T21:00:16.110 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 2729786d5d188974913f07ea63cc84cd42cb9cac5f4aac823c40105e68e22f63 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 64dcb3cb53c93d49347ee2366f4583308be1e5022a83825c4554d7caeb9e5559 +F src/alter.c 78188ded77cf1d0a9abb273e7139e218ab24a4bcfdfa6ee07300430e539a12b6 F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173 F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 -F src/build.c b49367a942fb67b74b20927ef6a30a335d29a466c441a7f309fdf35cf0967ecc +F src/build.c 79c5f75243665d9287e7a58b1a1de898d6f2baa094feeaa0741e40e174fea04d F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387 @@ -450,7 +450,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3 F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91 F src/delete.c 107e28d3ef8bd72fd11953374ca9107cd74e8b09c3ded076a6048742d26ce7d2 -F src/expr.c 665eafd45010fe6aeb8952c07e44d913ac25aec72e9a0c8784c84aeb3cc14efe +F src/expr.c e7d04a23bc4f42f0ad32b4d5c52a3fff13e113d68192c10d7c6c7ebe47a5f3f1 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c f59253c0be4b1e9dfcb073b6d6d6ab83090ae50c08b5c113b76013c4b157cd6a F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f @@ -487,23 +487,23 @@ F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 705de01dff9c3df9739c37a6d3b58cd2b1734fdabcef829b16cdc7721a9eeaa4 F src/pager.h ecc554a55bc55d1c4ba5e17137b72e238e00bd81e72ff2662d8b9c8c10ae3963 -F src/parse.y adc9a1fb8dab56e53ad81876796cdbea221bb05919d1ed432a6538d5ef231caa +F src/parse.y b3ca0ebaba6abe775e22c380fecfea17776ea150a6fb8e419a2a2274e478bc01 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 F src/pragma.c 873b767f233932e97cbffd094aa61928be90aca03f946a94bb29ce5695e4885b F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 -F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9 +F src/prepare.c f8e260d940a0e08494c0f30744521b2f832d7263eca9d02b050cea0ba144b097 F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 -F src/resolve.c 11c177efc9c038629cb39b7a4802931d0befe69fc478f7ce72110b21cdd43a4d +F src/resolve.c 352c6af1a99441206ff62a6f7429dbf537827f42c428639695220b9c8639e33b F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04 F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f F src/sqlite.h.in 82b5768e36ce796ecf93c73bd88bad99def831ce7d470138e213ac693bf4ceab F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 679196cdd2f753ff21144198aeafa3e69d69acd3c226855584ef9b3551d85471 +F src/sqliteInt.h 83b8d196810c41eb0818793f07f9393b0a5b9173a16588ac9436245b279d3a11 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -561,9 +561,9 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9 F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c -F src/tokenize.c 6449902efa1a880788ede649dfc65fca0d5685d39408a9a9b3a750a3e7a8b6c7 +F src/tokenize.c 9f55961518f77793edd56eee860ecf035d4370ebbb0726ad2f6cada6637fd16b F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f -F src/trigger.c f6760d78be400164c95732b4aabccab27072ec66e9db2ee33baf70e65e467af0 +F src/trigger.c d3d78568f37fb2e6cdcc2d1e7b60156f15b0b600adec55b83c5d42f6cad250bd F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 @@ -595,13 +595,14 @@ F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 18b00de006597e960a6b27ccec51474ac66cf1070a87c1933e5694dc02190ef1 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test ae17c4412639e16bd797c7617864a16c2badc0035c808ae8246f145e38f8e2f9 -F test/alter.test b820ab9dcf85f8e3a65bc8326accb2f0c7be64ef +F test/alter.test 8388947daa6caee09f624242d5907dbb0c0da6704cb40c44a6a1c1b4119142ed F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3 F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b F test/altercol.test 919898b3eef41d5d582af22d3c67c8b5a211ba30ac06821933421c24e3449e7d F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/altermalloc2.test 0231398534c494401a70a1d06a63d7849cb5b317fcc14228cbdb53039eba7eae +F test/altertab.test 6b2ebbe3a8fc3222ba773c7f884900367ef528cd90be361468fb15eb97dfcc51 F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df F test/analyze3.test ff62d9029e6deb2c914490c6b00caf7fae47cc85cdc046e4a0d0a4d4b87c71d8 @@ -1758,7 +1759,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f0eed21d4e2e1f2c8e680a510a283ac21156c9766efec5b80a362a040424bce7 -R 765df4694289b6c53a17c97f9702ba6c -U drh -Z db8eccde43135de3a0e592fd50ea76f1 +P 5858c0bde7c210f07cfa72cee95325847b26733abab4f56301b57cd816931e97 +R 2ca3dc5affa16de92546f4b6ec0792d4 +T *branch * alter-table-rename-table +T *sym-alter-table-rename-table * +T -sym-alter-table-rename-column * +U dan +Z 93949a0c450c29bbb5ee4cb93008ceac diff --git a/manifest.uuid b/manifest.uuid index 6c371ddbf1..dbb09bc3cb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5858c0bde7c210f07cfa72cee95325847b26733abab4f56301b57cd816931e97 \ No newline at end of file +01308bae3acf33f78b5bb90892085eab340df093aafc17e6ccf6a7d6cf324897 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 50690b9819..65880752c9 100644 --- a/src/alter.c +++ b/src/alter.c @@ -20,211 +20,6 @@ */ #ifndef SQLITE_OMIT_ALTERTABLE - -/* -** This function is used by SQL generated to implement the -** ALTER TABLE command. The first argument is the text of a CREATE TABLE or -** CREATE INDEX command. The second is a table name. The table name in -** the CREATE TABLE or CREATE INDEX statement is replaced with the third -** argument and the result returned. Examples: -** -** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def') -** -> 'CREATE TABLE def(a, b, c)' -** -** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def') -** -> 'CREATE INDEX i ON def(a, b, c)' -*/ -static void renameTableFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - unsigned char const *zSql = sqlite3_value_text(argv[0]); - unsigned char const *zTableName = sqlite3_value_text(argv[1]); - - int token; - Token tname; - unsigned char const *zCsr = zSql; - int len = 0; - char *zRet; - - sqlite3 *db = sqlite3_context_db_handle(context); - - UNUSED_PARAMETER(NotUsed); - - /* The principle used to locate the table name in the CREATE TABLE - ** statement is that the table name is the first non-space token that - ** is immediately followed by a TK_LP or TK_USING token. - */ - if( zSql ){ - do { - if( !*zCsr ){ - /* Ran out of input before finding an opening bracket. Return NULL. */ - return; - } - - /* Store the token that zCsr points to in tname. */ - tname.z = (char*)zCsr; - tname.n = len; - - /* Advance zCsr to the next token. Store that token type in 'token', - ** and its length in 'len' (to be used next iteration of this loop). - */ - do { - zCsr += len; - len = sqlite3GetToken(zCsr, &token); - } while( token==TK_SPACE ); - assert( len>0 || !*zCsr ); - } while( token!=TK_LP && token!=TK_USING ); - - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), - zSql, zTableName, tname.z+tname.n); - sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); - } -} - -/* -** This C function implements an SQL user function that is used by SQL code -** generated by the ALTER TABLE ... RENAME command to modify the definition -** of any foreign key constraints that use the table being renamed as the -** parent table. It is passed three arguments: -** -** 1) The complete text of the CREATE TABLE statement being modified, -** 2) The old name of the table being renamed, and -** 3) The new name of the table being renamed. -** -** It returns the new CREATE TABLE statement. For example: -** -** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3') -** -> 'CREATE TABLE t1(a REFERENCES t3)' -*/ -#ifndef SQLITE_OMIT_FOREIGN_KEY -static void renameParentFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - sqlite3 *db = sqlite3_context_db_handle(context); - char *zOutput = 0; - char *zResult; - unsigned char const *zInput = sqlite3_value_text(argv[0]); - unsigned char const *zOld = sqlite3_value_text(argv[1]); - unsigned char const *zNew = sqlite3_value_text(argv[2]); - - unsigned const char *z; /* Pointer to token */ - int n; /* Length of token z */ - int token; /* Type of token */ - - UNUSED_PARAMETER(NotUsed); - if( zInput==0 || zOld==0 ) return; - for(z=zInput; *z; z=z+n){ - n = sqlite3GetToken(z, &token); - if( token==TK_REFERENCES ){ - char *zParent; - do { - z += n; - n = sqlite3GetToken(z, &token); - }while( token==TK_SPACE ); - - if( token==TK_ILLEGAL ) break; - zParent = sqlite3DbStrNDup(db, (const char *)z, n); - if( zParent==0 ) break; - sqlite3Dequote(zParent); - if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ - char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", - (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew - ); - sqlite3DbFree(db, zOutput); - zOutput = zOut; - zInput = &z[n]; - } - sqlite3DbFree(db, zParent); - } - } - - zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput); - sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC); - sqlite3DbFree(db, zOutput); -} -#endif - -#ifndef SQLITE_OMIT_TRIGGER -/* This function is used by SQL generated to implement the -** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER -** statement. The second is a table name. The table name in the CREATE -** TRIGGER statement is replaced with the third argument and the result -** returned. This is analagous to renameTableFunc() above, except for CREATE -** TRIGGER, not CREATE INDEX and CREATE TABLE. -*/ -static void renameTriggerFunc( - sqlite3_context *context, - int NotUsed, - sqlite3_value **argv -){ - unsigned char const *zSql = sqlite3_value_text(argv[0]); - unsigned char const *zTableName = sqlite3_value_text(argv[1]); - - int token; - Token tname; - int dist = 3; - unsigned char const *zCsr = zSql; - int len = 0; - char *zRet; - sqlite3 *db = sqlite3_context_db_handle(context); - - UNUSED_PARAMETER(NotUsed); - - /* The principle used to locate the table name in the CREATE TRIGGER - ** statement is that the table name is the first token that is immediately - ** preceded by either TK_ON or TK_DOT and immediately followed by one - ** of TK_WHEN, TK_BEGIN or TK_FOR. - */ - if( zSql ){ - do { - - if( !*zCsr ){ - /* Ran out of input before finding the table name. Return NULL. */ - return; - } - - /* Store the token that zCsr points to in tname. */ - tname.z = (char*)zCsr; - tname.n = len; - - /* Advance zCsr to the next token. Store that token type in 'token', - ** and its length in 'len' (to be used next iteration of this loop). - */ - do { - zCsr += len; - len = sqlite3GetToken(zCsr, &token); - }while( token==TK_SPACE ); - assert( len>0 || !*zCsr ); - - /* Variable 'dist' stores the number of tokens read since the most - ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN - ** token is read and 'dist' equals 2, the condition stated above - ** to be met. - ** - ** Note that ON cannot be a database, table or column name, so - ** there is no need to worry about syntax like - ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. - */ - dist++; - if( token==TK_DOT || token==TK_ON ){ - dist = 0; - } - } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); - - /* Variable tname now contains the token that is the old table-name - ** in the CREATE TRIGGER statement. - */ - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), - zSql, zTableName, tname.z+tname.n); - sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); - } -} -#endif /* !SQLITE_OMIT_TRIGGER */ - /* ** This function is used to create the text of expressions of the form: ** @@ -383,9 +178,6 @@ void sqlite3AlterRenameTable( int nTabName; /* Number of UTF-8 characters in zTabName */ const char *zTabName; /* Original name of the table */ Vdbe *v; -#ifndef SQLITE_OMIT_TRIGGER - char *zWhere = 0; /* Where clause to locate temp triggers */ -#endif VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ u32 savedDbFlags; /* Saved value of db->mDbFlags */ @@ -479,31 +271,20 @@ void sqlite3AlterRenameTable( zTabName = pTab->zName; nTabName = sqlite3Utf8CharLen(zTabName, -1); -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - if( db->flags&SQLITE_ForeignKeys ){ - /* If foreign-key support is enabled, rewrite the CREATE TABLE - ** statements corresponding to all child tables of foreign key constraints - ** for which the renamed table is the parent table. */ - if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ - sqlite3NestedParse(pParse, - "UPDATE \"%w\".%s SET " - "sql = sqlite_rename_parent(sql, %Q, %Q) " - "WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere); - sqlite3DbFree(db, zWhere); - } - } -#endif + /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in + ** the schema to use the new table name. */ + sqlite3NestedParse(pParse, + "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_table(%Q, sql, %Q, %Q, 0) " + "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" + "AND name NOT LIKE 'sqlite_%%'" + , zDb, MASTER_NAME, zDb, zTabName, zName, zTabName + ); - /* Modify the sqlite_master table to use the new table name. */ + /* Update the tbl_name and name columns of the sqlite_master table + ** as required. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET " -#ifdef SQLITE_OMIT_TRIGGER - "sql = sqlite_rename_table(sql, %Q), " -#else - "sql = CASE " - "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" - "ELSE sqlite_rename_table(sql, %Q) END, " -#endif "tbl_name = %Q, " "name = CASE " "WHEN type='table' THEN %Q " @@ -512,11 +293,9 @@ void sqlite3AlterRenameTable( "ELSE name END " "WHERE tbl_name=%Q COLLATE nocase AND " "(type='table' OR type='index' OR type='trigger');", - zDb, MASTER_NAME, zName, zName, zName, -#ifndef SQLITE_OMIT_TRIGGER - zName, -#endif - zName, nTabName, zTabName + zDb, MASTER_NAME, + zName, zName, zName, + nTabName, zTabName ); #ifndef SQLITE_OMIT_AUTOINCREMENT @@ -530,35 +309,21 @@ void sqlite3AlterRenameTable( } #endif -#ifndef SQLITE_OMIT_TRIGGER - /* If there are TEMP triggers on this table, modify the sqlite_temp_master - ** table. Don't do this if the table being ALTERed is itself located in - ** the temp database. - */ - if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ + /* If the table being renamed is not itself part of the temp database, + ** edit view and trigger definitions within the temp database + ** as required. */ + if( iDb!=1 ){ sqlite3NestedParse(pParse, "UPDATE sqlite_temp_master SET " - "sql = sqlite_rename_trigger(sql, %Q), " - "tbl_name = %Q " - "WHERE %s;", zName, zName, zWhere); - sqlite3DbFree(db, zWhere); + "sql = sqlite_rename_table(%Q, sql, %Q, %Q, 1), " + "tbl_name = " + "CASE WHEN tbl_name=%Q COLLATE nocase THEN %Q ELSE tbl_name END " + "WHERE type IN ('view', 'trigger')" + , zDb, zTabName, zName, zTabName, zTabName, zName); } -#endif -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - if( db->flags&SQLITE_ForeignKeys ){ - FKey *p; - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - Table *pFrom = p->pFrom; - if( pFrom!=pTab ){ - reloadTableSchema(pParse, p->pFrom, pFrom->zName); - } - } - } -#endif - - /* Drop and reload the internal table schema. */ - reloadTableSchema(pParse, pTab, zName); + sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); + if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); exit_rename_table: sqlite3SrcListDelete(db, pSrc); @@ -946,6 +711,16 @@ struct RenameCtx { const char *zOld; /* Old column name */ }; +void renameTokenClear(Parse *pParse, void *pPtr){ + RenameToken *p; + assert( pPtr ); + for(p=pParse->pRename; p; p=p->pNext){ + if( p->p==pPtr ){ + p->p = 0; + } + } +} + /* ** Add a new RenameToken object mapping parse tree element pPtr into ** token *pToken to the Parse object currently under construction. @@ -954,6 +729,9 @@ struct RenameCtx { */ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ RenameToken *pNew; + assert( pPtr || pParse->db->mallocFailed ); + + renameTokenClear(pParse, pPtr); pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); if( pNew ){ pNew->p = pPtr; @@ -961,6 +739,7 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ pNew->pNext = pParse->pRename; pParse->pRename = pNew; } + return pPtr; } @@ -971,13 +750,13 @@ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ */ void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){ RenameToken *p; - for(p=pParse->pRename; ALWAYS(p); p=p->pNext){ + if( pTo ) renameTokenClear(pParse, pTo); + for(p=pParse->pRename; p; p=p->pNext){ if( p->p==pFrom ){ p->p = pTo; break; } } - assert( pTo==0 || p ); } /* @@ -1143,6 +922,221 @@ static void renameColumnIdlistNames( } } +static int renameParseSql( + Parse *p, + const char *zDb, + int bTable, + sqlite3 *db, + const char *zSql, + int bTemp +){ + int rc; + char *zErr = 0; + + db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); + + /* Parse the SQL statement passed as the first argument. If no error + ** occurs and the parse does not result in a new table, index or + ** trigger object, the database must be corrupt. */ + memset(p, 0, sizeof(Parse)); + p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN); + p->db = db; + p->nQueryLoop = 1; + rc = sqlite3RunParser(p, zSql, &zErr); + assert( p->zErrMsg==0 ); + assert( rc!=SQLITE_OK || zErr==0 ); + assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 ); + p->zErrMsg = zErr; + if( db->mallocFailed ) rc = SQLITE_NOMEM; + if( rc==SQLITE_OK + && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 + ){ + rc = SQLITE_CORRUPT_BKPT; + } + +#ifdef SQLITE_DEBUG + /* Ensure that all mappings in the Parse.pRename list really do map to + ** a part of the input string. */ + if( rc==SQLITE_OK ){ + int nSql = sqlite3Strlen30(zSql); + RenameToken *pToken; + for(pToken=p->pRename; pToken; pToken=pToken->pNext){ + assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] ); + } + } +#endif + + db->init.iDb = 0; + return rc; +} + +static int renameEditSql( + sqlite3_context *pCtx, /* Return result here */ + RenameCtx *pRename, /* Rename context */ + const char *zSql, /* SQL statement to edit */ + const char *zNew, /* New token text */ + int bQuote /* True to always quote token */ +){ + int nNew = sqlite3Strlen30(zNew); + int nSql = sqlite3Strlen30(zSql); + sqlite3 *db = sqlite3_context_db_handle(pCtx); + int rc = SQLITE_OK; + char *zQuot; + char *zOut; + int nQuot; + + /* Set zQuot to point to a buffer containing a quoted copy of the + ** identifier zNew. If the corresponding identifier in the original + ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to + ** point to zQuot so that all substitutions are made using the + ** quoted version of the new column name. */ + zQuot = sqlite3_mprintf("\"%w\"", zNew); + if( zQuot==0 ){ + return SQLITE_NOMEM; + }else{ + nQuot = sqlite3Strlen30(zQuot); + } + if( bQuote ){ + zNew = zQuot; + nNew = nQuot; + } + + /* At this point pRename->pList contains a list of RenameToken objects + ** corresponding to all tokens in the input SQL that must be replaced + ** with the new column name. All that remains is to construct and + ** return the edited SQL string. */ + assert( nQuot>=nNew ); + zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); + if( zOut ){ + int nOut = nSql; + memcpy(zOut, zSql, nSql); + while( pRename->pList ){ + int iOff; /* Offset of token to replace in zOut */ + RenameToken *pBest = renameColumnTokenNext(pRename); + + u32 nReplace; + const char *zReplace; + if( sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + } + + iOff = pBest->t.z - zSql; + if( pBest->t.n!=nReplace ){ + memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], + nOut - (iOff + pBest->t.n) + ); + nOut += nReplace - pBest->t.n; + zOut[nOut] = '\0'; + } + memcpy(&zOut[iOff], zReplace, nReplace); + sqlite3DbFree(db, pBest); + } + + sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT); + sqlite3DbFree(db, zOut); + }else{ + rc = SQLITE_NOMEM; + } + + sqlite3_free(zQuot); + return rc; +} + +static int renameResolveTrigger( + Parse *pParse, + const char *zDb +){ + sqlite3 *db = pParse->db; + TriggerStep *pStep; + NameContext sNC; + int rc = SQLITE_OK; + + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + pParse->pTriggerTab = sqlite3FindTable(db, pParse->pNewTrigger->table, zDb); + pParse->eTriggerOp = pParse->pNewTrigger->op; + + /* Resolve symbols in WHEN clause */ + if( pParse->pNewTrigger->pWhen ){ + rc = sqlite3ResolveExprNames(&sNC, pParse->pNewTrigger->pWhen); + } + + for(pStep=pParse->pNewTrigger->step_list; + rc==SQLITE_OK && pStep; + pStep=pStep->pNext + ){ + if( pStep->pSelect ){ + sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); + if( pParse->nErr ) rc = pParse->rc; + } + if( rc==SQLITE_OK && pStep->zTarget ){ + Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb); + if( pTarget==0 ){ + rc = SQLITE_ERROR; + }else{ + SrcList sSrc; + memset(&sSrc, 0, sizeof(sSrc)); + sSrc.nSrc = 1; + sSrc.a[0].zName = pStep->zTarget; + sSrc.a[0].pTab = pTarget; + sNC.pSrcList = &sSrc; + if( pStep->pWhere ){ + rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); + } + assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); + if( pStep->pUpsert ){ + Upsert *pUpsert = pStep->pUpsert; + assert( rc==SQLITE_OK ); + pUpsert->pUpsertSrc = &sSrc; + sNC.uNC.pUpsert = pUpsert; + sNC.ncFlags = NC_UUpsert; + rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); + if( rc==SQLITE_OK ){ + ExprList *pUpsertSet = pUpsert->pUpsertSet; + rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); + } + sNC.ncFlags = 0; + } + } + } + } + return rc; +} + +static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ + TriggerStep *pStep; + + /* Find tokens to edit in WHEN clause */ + sqlite3WalkExpr(pWalker, pTrigger->pWhen); + + /* Find tokens to edit in trigger steps */ + for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ + sqlite3WalkSelect(pWalker, pStep->pSelect); + sqlite3WalkExpr(pWalker, pStep->pWhere); + sqlite3WalkExprList(pWalker, pStep->pExprList); + if( pStep->pUpsert ){ + Upsert *pUpsert = pStep->pUpsert; + sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget); + sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet); + sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); + sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); + } + } +} + /* ** SQL function: ** @@ -1191,6 +1185,7 @@ static void renameColumnFunc( int nNew = sqlite3_value_bytes(argv[6]); int bQuote = sqlite3_value_int(argv[7]); const char *zOld; + int bTemp = 0; int rc; char *zErr = 0; @@ -1199,8 +1194,6 @@ static void renameColumnFunc( Index *pIdx; char *zOut = 0; - char *zQuot = 0; /* Quoted version of zNew */ - int nQuot = 0; /* Length of zQuot in bytes */ int i; Table *pTab; @@ -1219,54 +1212,7 @@ static void renameColumnFunc( memset(&sCtx, 0, sizeof(sCtx)); sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); - /* Parse the SQL statement passed as the first argument. If no error - ** occurs and the parse does not result in a new table, index or - ** trigger object, the database must be corrupt. */ - memset(&sParse, 0, sizeof(sParse)); - sParse.eParseMode = PARSE_MODE_RENAME_COLUMN; - sParse.db = db; - sParse.nQueryLoop = 1; - rc = sqlite3RunParser(&sParse, zSql, &zErr); - assert( sParse.zErrMsg==0 ); - assert( rc!=SQLITE_OK || zErr==0 ); - assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 ); - sParse.zErrMsg = zErr; - if( db->mallocFailed ) rc = SQLITE_NOMEM; - if( rc==SQLITE_OK - && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0 - ){ - rc = SQLITE_CORRUPT_BKPT; - } - -#ifdef SQLITE_DEBUG - /* Ensure that all mappings in the Parse.pRename list really do map to - ** a part of the input string. */ - assert( sqlite3Strlen30(zSql)==nSql ); - if( rc==SQLITE_OK ){ - RenameToken *pToken; - for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){ - assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] ); - } - } -#endif - - /* Set zQuot to point to a buffer containing a quoted copy of the - ** identifier zNew. If the corresponding identifier in the original - ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to - ** point to zQuot so that all substitutions are made using the - ** quoted version of the new column name. */ - if( rc==SQLITE_OK ){ - zQuot = sqlite3_mprintf("\"%w\"", zNew); - if( zQuot==0 ){ - rc = SQLITE_NOMEM; - }else{ - nQuot = sqlite3Strlen30(zQuot); - } - } - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; - } + rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp); /* Find tokens that need to be replaced. */ memset(&sWalker, 0, sizeof(Walker)); @@ -1325,140 +1271,35 @@ static void renameColumnFunc( }else{ /* A trigger */ TriggerStep *pStep; - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = &sParse; - sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb); - sParse.eTriggerOp = sParse.pNewTrigger->op; + rc = renameResolveTrigger(&sParse, zDb); + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; - /* Resolve symbols in WHEN clause */ - if( sParse.pNewTrigger->pWhen ){ - rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); - } - - for(pStep=sParse.pNewTrigger->step_list; - rc==SQLITE_OK && pStep; - pStep=pStep->pNext - ){ - if( pStep->pSelect ){ - sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC); - if( sParse.nErr ) rc = sParse.rc; - } - if( rc==SQLITE_OK && pStep->zTarget ){ + for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ + if( pStep->zTarget ){ Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb); - if( pTarget==0 ){ - rc = SQLITE_ERROR; - }else{ - SrcList sSrc; - memset(&sSrc, 0, sizeof(sSrc)); - sSrc.nSrc = 1; - sSrc.a[0].zName = pStep->zTarget; - sSrc.a[0].pTab = pTarget; - sNC.pSrcList = &sSrc; - if( pStep->pWhere ){ - rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); - } - if( rc==SQLITE_OK ){ - rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); - } - assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); + if( pTarget==pTab ){ if( pStep->pUpsert ){ - Upsert *pUpsert = pStep->pUpsert; - assert( rc==SQLITE_OK ); - pUpsert->pUpsertSrc = &sSrc; - sNC.uNC.pUpsert = pUpsert; - sNC.ncFlags = NC_UUpsert; - rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); - if( rc==SQLITE_OK ){ - ExprList *pUpsertSet = pUpsert->pUpsertSet; - if( pTarget==pTab ){ - renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld); - } - rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet); - } - if( rc==SQLITE_OK ){ - rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere); - } - if( rc==SQLITE_OK ){ - rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); - } - sNC.ncFlags = 0; - } - - if( rc==SQLITE_OK && pTarget==pTab ){ - renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld); - renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld); + ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet; + renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld); } + renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld); + renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld); } } } - if( rc!=SQLITE_OK ) goto renameColumnFunc_done; /* Find tokens to edit in UPDATE OF clause */ if( sParse.pTriggerTab==pTab ){ renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld); } - /* Find tokens to edit in WHEN clause */ - sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); - - /* Find tokens to edit in trigger steps */ - for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ - sqlite3WalkSelect(&sWalker, pStep->pSelect); - sqlite3WalkExpr(&sWalker, pStep->pWhere); - sqlite3WalkExprList(&sWalker, pStep->pExprList); - if( pStep->pUpsert ){ - Upsert *pUpsert = pStep->pUpsert; - sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget); - sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet); - sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere); - sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere); - } - } + /* Find tokens to edit in various expressions and selects */ + renameWalkTrigger(&sWalker, sParse.pNewTrigger); } - /* At this point sCtx.pList contains a list of RenameToken objects - ** corresponding to all tokens in the input SQL that must be replaced - ** with the new column name. All that remains is to construct and - ** return the edited SQL string. */ assert( rc==SQLITE_OK ); - assert( nQuot>=nNew ); - zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1); - if( zOut ){ - int nOut = nSql; - memcpy(zOut, zSql, nSql); - while( sCtx.pList ){ - int iOff; /* Offset of token to replace in zOut */ - RenameToken *pBest = renameColumnTokenNext(&sCtx); - - u32 nReplace; - const char *zReplace; - if( sqlite3IsIdChar(*pBest->t.z) ){ - nReplace = nNew; - zReplace = zNew; - }else{ - nReplace = nQuot; - zReplace = zQuot; - } - - iOff = pBest->t.z - zSql; - if( pBest->t.n!=nReplace ){ - memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], - nOut - (iOff + pBest->t.n) - ); - nOut += nReplace - pBest->t.n; - zOut[nOut] = '\0'; - } - memcpy(&zOut[iOff], zReplace, nReplace); - sqlite3DbFree(db, pBest); - } - - sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); - sqlite3DbFree(db, zOut); - }else{ - rc = SQLITE_NOMEM; - } + rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote); renameColumnFunc_done: if( rc!=SQLITE_OK ){ @@ -1479,23 +1320,180 @@ renameColumnFunc_done: renameTokenFree(db, sCtx.pList); sqlite3DbFree(db, sParse.zErrMsg); sqlite3ParserReset(&sParse); - sqlite3_free(zQuot); sqlite3BtreeLeaveAll(db); } +static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ + RenameCtx *p = pWalker->u.pRename; + if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){ + renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab); + } + return WRC_Continue; +} + +/* +** This is a Walker select callback. +*/ +static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ + int i; + RenameCtx *p = pWalker->u.pRename; + SrcList *pSrc = pSelect->pSrc; + for(i=0; inSrc; i++){ + struct SrcList_item *pItem = &pSrc->a[i]; + if( pItem->pTab==p->pTab ){ + renameTokenFind(pWalker->pParse, p, pItem->zName); + } + } + + return WRC_Continue; +} + + +/* +** This C function implements an SQL user function that is used by SQL code +** generated by the ALTER TABLE ... RENAME command to modify the definition +** of any foreign key constraints that use the table being renamed as the +** parent table. It is passed three arguments: +** +** 1) The complete text of the CREATE TABLE statement being modified, +** 2) The old name of the table being renamed, and +** 3) The new name of the table being renamed. +** +** It returns the new CREATE TABLE statement. For example: +** +** sqlite_rename_table('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3') +** -> 'CREATE TABLE t1(a REFERENCES t3)' +*/ +static void renameTableFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + char *zOutput = 0; + char *zResult; + unsigned char const *zDb = sqlite3_value_text(argv[0]); + unsigned char const *zInput = sqlite3_value_text(argv[1]); + unsigned char const *zOld = sqlite3_value_text(argv[2]); + unsigned char const *zNew = sqlite3_value_text(argv[3]); + int bTemp = sqlite3_value_int(argv[4]); + + unsigned const char *z; /* Pointer to token */ + int n; /* Length of token z */ + int token; /* Type of token */ + + Parse sParse; + int rc; + int bQuote = 1; + RenameCtx sCtx; + Walker sWalker; + + if( zInput==0 || zOld==0 || zNew==0 ) return; + + memset(&sCtx, 0, sizeof(RenameCtx)); + sCtx.pTab = sqlite3FindTable(db, zOld, zDb); + memset(&sWalker, 0, sizeof(Walker)); + sWalker.pParse = &sParse; + sWalker.xExprCallback = renameTableExprCb; + sWalker.xSelectCallback = renameTableSelectCb; + sWalker.u.pRename = &sCtx; + + sqlite3BtreeEnterAll(db); + + rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); + + if( rc==SQLITE_OK ){ + if( sParse.pNewTable ){ + Table *pTab = sParse.pNewTable; + + if( pTab->pSelect ){ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; + + sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC); + if( sParse.nErr ) rc = sParse.rc; + sqlite3WalkSelect(&sWalker, pTab->pSelect); + }else{ + /* Modify any FK definitions to point to the new table. */ +#ifndef SQLITE_OMIT_FOREIGN_KEY + FKey *pFKey; + for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ + renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); + } + } +#endif + + /* If this is the table being altered, fix any table refs in CHECK + ** expressions. Also update the name that appears right after the + ** "CREATE [VIRTUAL] TABLE" bit. */ + if( sqlite3_stricmp(zOld, pTab->zName)==0 ){ + sCtx.pTab = pTab; + sqlite3WalkExprList(&sWalker, pTab->pCheck); + renameTokenFind(&sParse, &sCtx, pTab->zName); + } + } + } + + else if( sParse.pNewIndex ){ + renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName); + sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + } + +#ifndef SQLITE_OMIT_TRIGGER + else if( sParse.pNewTrigger ){ + Trigger *pTrigger = sParse.pNewTrigger; + TriggerStep *pStep; + if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld) + && sCtx.pTab->pSchema==pTrigger->pTabSchema + ){ + renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table); + } + + rc = renameResolveTrigger(&sParse, zDb); + if( rc==SQLITE_OK ){ + renameWalkTrigger(&sWalker, pTrigger); + } + + for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ + if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ + renameTokenFind(&sParse, &sCtx, pStep->zTarget); + } + } + } +#endif + } + + if( rc==SQLITE_OK ){ + rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); + } + + if( rc!=SQLITE_OK ){ + sqlite3_result_error_code(context, rc); + } + if( sParse.pVdbe ){ + sqlite3VdbeFinalize(sParse.pVdbe); + } + sqlite3DeleteTable(db, sParse.pNewTable); + if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex); + sqlite3DeleteTrigger(db, sParse.pNewTrigger); + renameTokenFree(db, sParse.pRename); + renameTokenFree(db, sCtx.pList); + sqlite3DbFree(db, sParse.zErrMsg); + sqlite3ParserReset(&sParse); + sqlite3BtreeLeaveAll(db); + + return; +} + /* ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { - FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), FUNCTION(sqlite_rename_column, 8, 0, 0, renameColumnFunc), -#ifndef SQLITE_OMIT_TRIGGER - FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), -#endif -#ifndef SQLITE_OMIT_FOREIGN_KEY - FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), -#endif + FUNCTION(sqlite_rename_table, 5, 0, 0, renameTableFunc), }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } diff --git a/src/build.c b/src/build.c index 7f8013a0cc..4ab467b62e 100644 --- a/src/build.c +++ b/src/build.c @@ -783,7 +783,7 @@ int sqlite3TwoPartName( return -1; } }else{ - assert( db->init.iDb==0 || db->init.busy + assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT || (db->mDbFlags & DBFLAG_Vacuum)!=0); iDb = db->init.iDb; *pUnqual = pName1; @@ -878,6 +878,9 @@ void sqlite3StartTable( } if( !OMIT_TEMPDB && isTemp ) iDb = 1; zName = sqlite3NameFromToken(db, pName); + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, (void*)zName, pName); + } } pParse->sNameToken = *pName; if( zName==0 ) return; @@ -1072,7 +1075,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ } z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); if( z==0 ) return; - if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)z, pName); + if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName); memcpy(z, pName->z, pName->n); z[pName->n] = 0; sqlite3Dequote(z); @@ -1370,7 +1373,7 @@ void sqlite3AddPrimaryKey( && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 && sortOrder!=SQLITE_SO_DESC ){ - if( IN_RENAME_COLUMN && pList ){ + if( IN_RENAME_OBJECT && pList ){ sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr); } pTab->iPKey = iCol; @@ -2175,7 +2178,7 @@ void sqlite3CreateView( ** allocated rather than point to the input string - which means that ** they will persist after the current sqlite3_exec() call returns. */ - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ p->pSelect = pSelect; pSelect = 0; }else{ @@ -2748,6 +2751,9 @@ void sqlite3CreateForeignKey( pFKey->pNextFrom = p->pFKey; z = (char*)&pFKey->aCol[nCol]; pFKey->zTo = z; + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, (void*)z, pTo); + } memcpy(z, pTo->z, pTo->n); z[pTo->n] = 0; sqlite3Dequote(z); @@ -2770,7 +2776,7 @@ void sqlite3CreateForeignKey( pFromCol->a[i].zName); goto fk_end; } - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName); } } @@ -2779,7 +2785,7 @@ void sqlite3CreateForeignKey( for(i=0; ia[i].zName); pFKey->aCol[i].zCol = z; - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName); } memcpy(z, pToCol->a[i].zName, n); @@ -3114,7 +3120,7 @@ void sqlite3CreateIndex( if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto exit_create_index; } - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ if( !db->init.busy ){ if( sqlite3FindTable(db, zName, 0)!=0 ){ sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); @@ -3151,7 +3157,7 @@ void sqlite3CreateIndex( /* Check for authorization to create an index. */ #ifndef SQLITE_OMIT_AUTHORIZATION - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ const char *zDb = pDb->zDbSName; if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ goto exit_create_index; @@ -3239,7 +3245,7 @@ void sqlite3CreateIndex( ** index key. */ pListItem = pList->a; - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ pIndex->aColExpr = pList; pList = 0; } @@ -3399,7 +3405,7 @@ void sqlite3CreateIndex( } } - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ /* Link the new Index structure to its table and to the other ** in-memory database structures. @@ -3517,7 +3523,7 @@ void sqlite3CreateIndex( } pIndex = 0; } - else if( IN_RENAME_COLUMN ){ + else if( IN_RENAME_OBJECT ){ assert( pParse->pNewIndex==0 ); pParse->pNewIndex = pIndex; pIndex = 0; @@ -3713,7 +3719,7 @@ IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ return 0; } pList->a[i].zName = sqlite3NameFromToken(db, pToken); - if( IN_RENAME_COLUMN && pList->a[i].zName ){ + if( IN_RENAME_OBJECT && pList->a[i].zName ){ sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken); } return pList; @@ -3962,6 +3968,10 @@ SrcList *sqlite3SrcListAppendFromTerm( } assert( p->nSrc>0 ); pItem = &p->a[p->nSrc-1]; + if( IN_RENAME_OBJECT && pItem->zName ){ + Token *pToken = (pDatabase && pDatabase->z) ? pDatabase : pTable; + sqlite3RenameTokenMap(pParse, pItem->zName, pToken); + } assert( pAlias!=0 ); if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); diff --git a/src/expr.c b/src/expr.c index 6f9c733079..701f10856d 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1666,7 +1666,7 @@ void sqlite3ExprListSetName( assert( pItem->zName==0 ); pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); if( dequote ) sqlite3Dequote(pItem->zName); - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName); } } diff --git a/src/parse.y b/src/parse.y index 44e300e198..8bf5d83fc2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -325,7 +325,7 @@ ccons ::= DEFAULT scanpt id(X). { sqlite3ExprIdToTrueFalse(p); testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) ); } - sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n); + sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n); } // In addition to the type name, we also care about the primary key and @@ -683,10 +683,14 @@ dbnm(A) ::= DOT nm(X). {A = X;} %type fullname {SrcList*} %destructor fullname {sqlite3SrcListDelete(pParse->db, $$);} -fullname(A) ::= nm(X). - {A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/} -fullname(A) ::= nm(X) DOT nm(Y). - {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/} +fullname(A) ::= nm(X). { + A = sqlite3SrcListAppend(pParse->db,0,&X,0); + if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X); +} +fullname(A) ::= nm(X) DOT nm(Y). { + A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); + if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y); +} %type xfullname {SrcList*} %destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);} @@ -954,7 +958,7 @@ idlist(A) ::= nm(Y). #if SQLITE_MAX_EXPR_DEPTH>0 p->nHeight = 1; #endif - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t); } } @@ -970,7 +974,10 @@ expr(A) ::= JOIN_KW(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); - if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp2, &Y); + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, (void*)temp2, &Y); + sqlite3RenameTokenMap(pParse, (void*)temp1, &X); + } A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { @@ -978,7 +985,10 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3); - if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp3, &Z); + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, (void*)temp3, &Z); + sqlite3RenameTokenMap(pParse, (void*)temp2, &Y); + } A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/} @@ -1277,6 +1287,9 @@ cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U, &S, W, SQLITE_SO_ASC, NE, SQLITE_IDXTYPE_APPDEF); + if( IN_RENAME_OBJECT && pParse->pNewIndex ){ + sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &Y); + } } %type uniqueflag {int} @@ -1325,9 +1338,6 @@ uniqueflag(A) ::= . {A = OE_None;} pIdToken->n, pIdToken->z); } sqlite3ExprListSetName(pParse, p, pIdToken, 1); - if( IN_RENAME_COLUMN && p ){ - sqlite3RenameTokenMap(pParse, (void*)(p->a[p->nExpr-1].zName), pIdToken); - } return p; } } // end %include diff --git a/src/prepare.c b/src/prepare.c index 4d4b98d8a9..602e4dc49d 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -93,7 +93,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ rc = db->errCode; assert( (rc&0xFF)==(rcp&0xFF) ); db->init.iDb = saved_iDb; - assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); + /* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */ if( SQLITE_OK!=rc ){ if( db->init.orphanTrigger ){ assert( iDb==1 ); diff --git a/src/resolve.c b/src/resolve.c index fb38332287..9fb4d4c63f 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -264,6 +264,9 @@ static int lookupName( if( sqlite3StrICmp(zTabName, zTab)!=0 ){ continue; } + if( IN_RENAME_OBJECT && pItem->zAlias ){ + sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab); + } } if( 0==(cntTab++) ){ pMatch = pItem; @@ -349,7 +352,7 @@ static int lookupName( #ifndef SQLITE_OMIT_UPSERT if( pExpr->iTable==2 ){ testcase( iCol==(-1) ); - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ pExpr->iColumn = iCol; pExpr->pTab = pTab; eNewExprOp = TK_COLUMN; @@ -442,7 +445,7 @@ static int lookupName( cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr); } goto lookupname_end; @@ -672,21 +675,25 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ zTable = 0; zColumn = pExpr->u.zToken; }else{ + Expr *pLeft = pExpr->pLeft; notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr); pRight = pExpr->pRight; if( pRight->op==TK_ID ){ zDb = 0; - zTable = pExpr->pLeft->u.zToken; }else{ assert( pRight->op==TK_DOT ); - zDb = pExpr->pLeft->u.zToken; - zTable = pRight->pLeft->u.zToken; + zDb = pLeft->u.zToken; + pLeft = pRight->pLeft; pRight = pRight->pRight; } + zTable = pLeft->u.zToken; zColumn = pRight->u.zToken; - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); } + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft); + } } return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); } @@ -769,7 +776,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } } - if( 0==IN_RENAME_COLUMN ){ + if( 0==IN_RENAME_OBJECT ){ #ifndef SQLITE_OMIT_WINDOWFUNC assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX) || (pDef->xValue==0 && pDef->xInverse==0) diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 25f101d900..d8e3a795d0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3123,6 +3123,7 @@ struct Parse { #define PARSE_MODE_NORMAL 0 #define PARSE_MODE_DECLARE_VTAB 1 #define PARSE_MODE_RENAME_COLUMN 2 +#define PARSE_MODE_RENAME_TABLE 3 /* ** Sizes and pointers of various parts of the Parse object. @@ -3142,9 +3143,9 @@ struct Parse { #endif #if defined(SQLITE_OMIT_ALTERTABLE) - #define IN_RENAME_COLUMN 0 + #define IN_RENAME_OBJECT 0 #else - #define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN) + #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN) #endif #if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE) diff --git a/src/tokenize.c b/src/tokenize.c index 94a5b85150..262144ff7d 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -695,7 +695,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ */ sqlite3DeleteTable(db, pParse->pNewTable); } - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ sqlite3DeleteTrigger(db, pParse->pNewTrigger); } diff --git a/src/trigger.c b/src/trigger.c index 330e14ea72..dd6224da6d 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -181,7 +181,7 @@ void sqlite3BeginTrigger( goto trigger_cleanup; } assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); @@ -214,7 +214,7 @@ void sqlite3BeginTrigger( } #ifndef SQLITE_OMIT_AUTHORIZATION - if( !IN_RENAME_COLUMN ){ + if( !IN_RENAME_OBJECT ){ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int code = SQLITE_CREATE_TRIGGER; const char *zDb = db->aDb[iTabDb].zDbSName; @@ -248,7 +248,8 @@ void sqlite3BeginTrigger( pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = (u8)op; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName); pTrigger->pWhen = pWhen; pWhen = 0; }else{ @@ -305,7 +306,7 @@ void sqlite3FinishTrigger( } #ifndef SQLITE_OMIT_ALTERTABLE - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ assert( !db->init.busy ); pParse->pNewTrigger = pTrig; pTrig = 0; @@ -353,7 +354,7 @@ void sqlite3FinishTrigger( triggerfinish_cleanup: sqlite3DeleteTrigger(db, pTrig); - assert( IN_RENAME_COLUMN || !pParse->pNewTrigger ); + assert( IN_RENAME_OBJECT || !pParse->pNewTrigger ); sqlite3DeleteTriggerStep(db, pStepList); } @@ -400,12 +401,13 @@ TriggerStep *sqlite3TriggerSelectStep( ** If an OOM error occurs, NULL is returned and db->mallocFailed is set. */ static TriggerStep *triggerStepAllocate( - sqlite3 *db, /* Database connection */ + Parse *pParse, /* Parser context */ u8 op, /* Trigger opcode */ Token *pName, /* The target name */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); @@ -416,6 +418,9 @@ static TriggerStep *triggerStepAllocate( pTriggerStep->zTarget = z; pTriggerStep->op = op; pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd); + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName); + } } return pTriggerStep; } @@ -442,9 +447,9 @@ TriggerStep *sqlite3TriggerInsertStep( assert(pSelect != 0 || db->mallocFailed); - pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd); if( pTriggerStep ){ - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ pTriggerStep->pSelect = pSelect; pSelect = 0; }else{ @@ -481,9 +486,9 @@ TriggerStep *sqlite3TriggerUpdateStep( sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; - pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd); if( pTriggerStep ){ - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ pTriggerStep->pExprList = pEList; pTriggerStep->pWhere = pWhere; pEList = 0; @@ -514,9 +519,9 @@ TriggerStep *sqlite3TriggerDeleteStep( sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; - pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd); if( pTriggerStep ){ - if( IN_RENAME_COLUMN ){ + if( IN_RENAME_OBJECT ){ pTriggerStep->pWhere = pWhere; pWhere = 0; }else{ diff --git a/test/alter.test b/test/alter.test index 76c18cedc9..4e6b6da739 100644 --- a/test/alter.test +++ b/test/alter.test @@ -898,28 +898,28 @@ do_execsql_test alter-17.6 { SELECT sqlite_rename_trigger(NULL,'abc'); } {{}} do_execsql_test alter-17.7 { - SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")', 'xyzzy','lmnop'); } {{CREATE TABLE t1(a REFERENCES "lmnop")}} do_execsql_test alter-17.8 { - SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")', 'xyzzy',NULL); -} {{CREATE TABLE t1(a REFERENCES "(NULL)")}} +} {{}} do_execsql_test alter-17.9 { - SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")', + SELECT sqlite_rename_parent('main', 'CREATE TABLE t1(a REFERENCES "xyzzy")', NULL, 'lmnop'); } {{}} do_execsql_test alter-17.10 { - SELECT sqlite_rename_parent(NULL,'abc','xyz'); + SELECT sqlite_rename_parent('main', NULL,'abc','xyz'); } {{}} -do_execsql_test alter-17.11 { - SELECT sqlite_rename_parent('create references ''','abc','xyz'); -} {{create references '}} -do_execsql_test alter-17.12 { - SELECT sqlite_rename_parent('create references "abc"123" ','abc','xyz'); -} {{create references "xyz"123" }} -do_execsql_test alter-17.13 { - SELECT sqlite_rename_parent("references '''",'abc','xyz'); -} {{references '''}} +do_catchsql_test alter-17.11 { + SELECT sqlite_rename_parent('main', 'create references ''','abc','xyz'); +} {1 {SQL logic error}} +do_catchsql_test alter-17.12 { + SELECT sqlite_rename_parent('main', 'create references "abc"123" ','abc','xyz'); +} {1 {SQL logic error}} +do_catchsql_test alter-17.13 { + SELECT sqlite_rename_parent('main', "references '''",'abc','xyz'); +} {1 {SQL logic error}} finish_test diff --git a/test/altertab.test b/test/altertab.test new file mode 100644 index 0000000000..d09e19b79a --- /dev/null +++ b/test/altertab.test @@ -0,0 +1,165 @@ +# 2018 August 24 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix altertab + +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, CHECK(t1.a != t1.b)); + + CREATE TABLE t2(a, b); + CREATE INDEX t2expr ON t2(a) WHERE t2.b>0; +} + +do_execsql_test 1.1 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE t1(a, b, CHECK(t1.a != t1.b))} + {CREATE TABLE t2(a, b)} + {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0} +} + +do_execsql_test 1.2 { + ALTER TABLE t1 RENAME TO t1new; +} + +do_execsql_test 1.3 { + CREATE TABLE t3(c, d); + ALTER TABLE t3 RENAME TO t3new; + DROP TABLE t3new; +} + +do_execsql_test 1.4 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))} + {CREATE TABLE t2(a, b)} + {CREATE INDEX t2expr ON t2(a) WHERE t2.b>0} +} + + +do_execsql_test 1.3 { + ALTER TABLE t2 RENAME TO t2new; +} +do_execsql_test 1.4 { + SELECT sql FROM sqlite_master +} { + {CREATE TABLE "t1new"(a, b, CHECK("t1new".a != "t1new".b))} + {CREATE TABLE "t2new"(a, b)} + {CREATE INDEX t2expr ON "t2new"(a) WHERE "t2new".b>0} +} + + +#------------------------------------------------------------------------- +reset_db +register_echo_module db + +do_execsql_test 2.0 { + CREATE TABLE abc(a, b, c); + INSERT INTO abc VALUES(1, 2, 3); + CREATE VIRTUAL TABLE eee USING echo('abc'); + SELECT * FROM eee; +} {1 2 3} + +do_execsql_test 2.1 { + ALTER TABLE eee RENAME TO fff; + SELECT * FROM fff; +} {1 2 3} + +db close +sqlite3 db test.db + +do_catchsql_test 2.2 { + ALTER TABLE fff RENAME TO ggg; +} {1 {no such module: echo}} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 3.0 { + CREATE TABLE txx(a, b, c); + INSERT INTO txx VALUES(1, 2, 3); + CREATE VIEW vvv AS SELECT main.txx.a, txx.b, c FROM txx; + CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM txx AS one; + CREATE VIEW temp.ttt AS SELECT main.txx.a, txx.b, one.b, main.one.a FROM txx AS one, txx; +} + +do_execsql_test 3.1.1 { + SELECT * FROM vvv; +} {1 2 3} +do_execsql_test 3.1.2 { + ALTER TABLE txx RENAME TO "t xx"; + SELECT * FROM vvv; +} {1 2 3} +do_execsql_test 3.1.3 { + SELECT sql FROM sqlite_master WHERE name='vvv'; +} {{CREATE VIEW vvv AS SELECT main."t xx".a, "t xx".b, c FROM "t xx"}} + + +do_execsql_test 3.2.1 { + SELECT * FROM uuu; +} {1 2 3} +do_execsql_test 3.2.2 { + SELECT sql FROM sqlite_master WHERE name='uuu';; +} {{CREATE VIEW uuu AS SELECT main.one.a, one.b, c FROM "t xx" AS one}} + +do_execsql_test 3.3.1 { + SELECT * FROM ttt; +} {1 2 2 1} +do_execsql_test 3.3.2 { + SELECT sql FROM sqlite_temp_master WHERE name='ttt'; +} {{CREATE VIEW ttt AS SELECT main."t xx".a, "t xx".b, one.b, main.one.a FROM "t xx" AS one, "t xx"}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 4.0 { + CREATE table t1(x, y); + CREATE table t2(a, b); + + CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN + SELECT t1.x, * FROM t1, t2; + INSERT INTO t2 VALUES(new.x, new.y); + END; +} + +do_execsql_test 4.1 { + INSERT INTO t1 VALUES(1, 1); + ALTER TABLE t1 RENAME TO t11; + INSERT INTO t11 VALUES(2, 2); + ALTER TABLE t2 RENAME TO t22; + INSERT INTO t11 VALUES(3, 3); +} + +proc squish {a} { + string trim [regsub -all {[[:space:]][[:space:]]*} $a { }] +} +db func squish squish +do_test 4.2 { + execsql { SELECT squish(sql) FROM sqlite_master WHERE name = 'tr1' } +} [list [squish { + CREATE TRIGGER tr1 AFTER INSERT ON "t11" BEGIN + SELECT "t11".x, * FROM "t11", "t22"; + INSERT INTO "t22" VALUES(new.x, new.y); + END +}]] + + +finish_test + +