From: danielk1977 Date: Fri, 12 Nov 2004 13:42:30 +0000 (+0000) Subject: Add the "ALTER TABLE xxx RENAME TO yyy" command. (CVS 2092) X-Git-Tag: version-3.6.10~4058 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9fd2a9a02854aa0cce1c57491d0935d14172cb36;p=thirdparty%2Fsqlite.git Add the "ALTER TABLE xxx RENAME TO yyy" command. (CVS 2092) FossilOrigin-Name: a1b2cc63e604785bd51e358ff72c485d858752e3 --- diff --git a/manifest b/manifest index 75021c5d08..57dca557ce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Autoincrement\scode\sinstalled.\s\sSimple\ssmoke-testing\sonly.\s\sNo\sregression\ntests\sdeveloped\syet.\s(CVS\s2091) -D 2004-11-12T03:56:15 +C Add\sthe\s"ALTER\sTABLE\sxxx\sRENAME\sTO\syyy"\scommand.\s(CVS\s2092) +D 2004-11-12T13:42:31 F Makefile.in c4d2416860f472a1e3393714d0372074197565df F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -31,11 +31,11 @@ F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea F src/btree.c 9fd74df65bad768a441afefc3b73174d45b85d5b F src/btree.h 861e40b759a195ba63819740e484390012cf81ab -F src/build.c 6d1687c66138af18e210981465ee342fe36985d3 +F src/build.c cbd985e9d0168204fcca3e1123f0b7f621f402e6 F src/date.c 4fd4e90b3880dacd67305e96330940dc243ffc10 F src/delete.c f0af21a1ede15524a5edd59fe10ef486283a1ee9 F src/expr.c 4ee3e47358c92a919062255b14057a7a8f641e01 -F src/func.c 600e506bccf7648df8ad03efb417560d0f7ad4c1 +F src/func.c b68572e79ada56dd038b7e71755212215092b717 F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/insert.c e80b009f9ef2864bbaaaae836286f56125a180f6 @@ -54,14 +54,14 @@ F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/pager.c ee88fcecb081e3635c281bc09d604e934429e2f5 F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862 -F src/parse.y 91a01b5f1170a574b4b9f19871b32bd0f00923a9 +F src/parse.y b64ebeb91d9fc1c813f4517b80e3a3659e94b032 F src/pragma.c bb1c76dae9911b9312997b353c1e316fa603a0c6 F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c 156990c636102bb6b8de85e7ff3396a62568476b F src/shell.c 55adda3cf3c1cc2f6c1919aac17b2318f9c2a96f F src/sqlite.h.in 4f97b5907acfd2a5068cb0cec9d5178816734db7 -F src/sqliteInt.h 0fdf2b0cf6f2db12c4abefad0f1085268d911f74 +F src/sqliteInt.h 8569ce94e891a854de71d7bd628da1d25ee6dfe4 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 0302e3f42f015d132d1291f3388c06e86c24a008 F src/test1.c e80e070fe794cdc204ec144ce03bd7b27dab4946 @@ -69,7 +69,7 @@ F src/test2.c b11fa244fff02190707dd0879987c37c75e61fc8 F src/test3.c 6f1ec93e13632a004b527049535079eda84c459d F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df F src/test5.c b001fa7f1b9e2dc5c2331de62fc641b5ab2bd7a1 -F src/tokenize.c 4dc14256d80d25da622d506bfe3b817153b97032 +F src/tokenize.c 2ad3d1ae1a0a70746db0b31a0a74f58050a3c39a F src/trigger.c f9a0a8d3a87238de1a934eeb7d0b6b1f13e6a55b F src/update.c 3cc67f6053495152e82a6a48c93ed331218e936e F src/utf.c f4f83acd73389090e32d6589d307fc55d794c7ed @@ -83,6 +83,7 @@ F src/vdbeaux.c c6da55e0096e141211f918837eca98e0be6400b4 F src/vdbemem.c ef9ac7d32acfe4bce5c5b408b1294c8d9e0cdb56 F src/where.c 6e637a6b3e61fe3104adc4e5caa4738bf6570daa F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c +F test/alter.test faf3440c4170033f5ea38d1343a0d82f4f3f7017 F test/attach.test e305dd59a375e37c658c6d401f19f8a95880bf9a F test/attach2.test 399128a7b3b209a339a8dbf53ca2ed42eb982d1a F test/attach3.test 8a0309e284cf9aa1d7d6cc444989031881f7a21c @@ -203,7 +204,7 @@ F tool/lempar.c 1e61d2b6cb9d8affa264a13336bc0c088498caa4 F tool/memleak.awk b744b6109566206c746d826f6ecdba34662216bc F tool/memleak2.awk 9cc20c8e8f3c675efac71ea0721ee6874a1566e8 F tool/memleak3.tcl 336eb50b0849dbf99b1d5462d9c37291b01b2b43 -F tool/mkkeywordhash.c b651bd7f7e0b5efea1f9aea3957d5ca4e2067d56 +F tool/mkkeywordhash.c 27753dd082cb1a7132866a7b73b91a55c6c8f2d4 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e x F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816 @@ -255,7 +256,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P 60fb0cef093b9827ac2d36b8a94e37f4b79dd2ea -R ab6b6f9534118088586ec8c53a8170c1 -U drh -Z 72c9d0289de8ee06d1a20ab843f47f3d +P 8fde833c812b91c5a574208a70b5f92b9d4b0a87 +R 8245e15b31d6598597e65a4dce12b056 +U danielk1977 +Z 014cc6af755028b6df88d60e431d5a3f diff --git a/manifest.uuid b/manifest.uuid index 9a2824f59e..09fe0612a5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8fde833c812b91c5a574208a70b5f92b9d4b0a87 \ No newline at end of file +a1b2cc63e604785bd51e358ff72c485d858752e3 \ No newline at end of file diff --git a/src/build.c b/src/build.c index ead40ec42a..ab3877dab9 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.276 2004/11/12 03:56:15 drh Exp $ +** $Id: build.c,v 1.277 2004/11/12 13:42:31 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -2359,7 +2359,7 @@ void sqlite3CreateIndex( */ if( pStart && pEnd ){ /* A named index with an explicit CREATE INDEX statement */ - zStmt = sqlite3MPrintf("CREATE%s INDEX %.*q", + zStmt = sqlite3MPrintf("CREATE%s INDEX %.*s", onError==OE_None ? "" : " UNIQUE", Addr(pEnd->z) - Addr(pName->z) + 1, pName->z); @@ -2919,3 +2919,79 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); } #endif + +#ifndef SQLITE_OMIT_ALTERTABLE +/* +** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" +** command. +*/ +void sqlite3AlterRenameTable( + Parse *pParse, /* Parser context. */ + SrcList *pSrc, /* The table to rename. */ + Token *pName /* The new table name. */ +){ + int iDb; /* Database that contains the table */ + Table *pTab; /* Table being renamed */ + sqlite3 *db = pParse->db; /* Database connection */ + char *zName = 0; /* NULL-terminated version of pName */ + char *zWhere = 0; /* Where clause of schema elements to reparse */ + Vdbe *v; + + assert( pSrc->nSrc==1 ); + + pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); + if( !pTab ) return; + iDb = pTab->iDb; + + /* Get a NULL terminated version of the new table name. */ + zName = sqlite3NameFromToken(pName); + if( !zName ) return; + + /* Check that a table or index named 'zName' does not already exist + ** in database iDb. If so, this is an error. + */ + if( sqlite3FindTable(db, zName, db->aDb[iDb].zName) || + sqlite3FindIndex(db, zName, db->aDb[iDb].zName) ){ + sqlite3ErrorMsg(pParse, + "there is already another table or index with this name: %s", zName); + sqliteFree(zName); + return; + } + + /* Begin a transaction and code the VerifyCookie for database iDb. + ** Then modify the schema cookie (since the ALTER TABLE modifies the + ** schema). + */ + v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3ChangeCookie(db, v, iDb); + + /* Modify the sqlite_master table to use the new table name. */ + sqlite3NestedParse(pParse, + "UPDATE %Q.%s SET " + "sql = sqlite_alter_table(sql, %Q), " + "tbl_name = %Q, " + "name = CASE " + "WHEN type='table' THEN %Q " + "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " + "'sqlite_autoindex_' || %Q || substr(name, %d+18,10) " + "ELSE name END " + "WHERE tbl_name=%Q AND type IN ('table', 'index');", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, zName, zName, + zName, strlen(pTab->zName), pTab->zName + ); + + /* Drop the elements of the in-memory schema that refered to the table + ** renamed and load the new versions from the database. + */ + if( pParse->nErr==0 ){ + sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); + zWhere = sqlite3MPrintf("tbl_name=%Q", zName); + sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC); + } + + sqliteFree(zName); +} +#endif + diff --git a/src/func.c b/src/func.c index 46952284e6..2d760b7f8f 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.85 2004/10/06 15:41:17 drh Exp $ +** $Id: func.c,v 1.86 2004/11/12 13:42:31 danielk1977 Exp $ */ #include #include @@ -507,6 +507,49 @@ static void versionFunc( sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); } +#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 second +** argument and the result returned. Examples: +** +** sqlite_alter_table('CREATE TABLE abc(a, b, c)', 'def') +** -> 'CREATE TABLE def(a, b, c)' +** +** sqlite_alter_table('CREATE INDEX i ON abc(a)', 'def') +** -> 'CREATE INDEX i ON def(a, b, c)' +*/ +static void altertableFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + char const *zSql = sqlite3_value_text(argv[0]); + char const *zTableName = sqlite3_value_text(argv[1]); + + char const *zCsr = zSql; + char const *zPrev; + char *zRet = 0; + int tokenType = 0; + int len; + + assert( argc==2 ); + if( zSql ){ + while( tokenType!=TK_LP ){ + zPrev = zCsr-len; + len = sqlite3GetToken(zCsr, &tokenType); + zCsr += len; + } + + zRet = sqlite3MPrintf("%.*s%Q(%s", zPrev-zSql, zSql, zTableName, zCsr); + sqlite3_result_text(context, zRet, -1, SQLITE_TRANSIENT); + sqliteFree(zRet); + } +} +#endif + /* ** EXPERIMENTAL - This is not an official function. The interface may ** change. This function may disappear. Do not write code that depends @@ -952,6 +995,9 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ { "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid }, { "changes", 0, 1, SQLITE_UTF8, 0, changes }, { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, +#ifndef SQLITE_OMIT_ALTERTABLE + { "sqlite_alter_table", 2, 0, SQLITE_UTF8, 0, altertableFunc}, +#endif #ifdef SQLITE_SOUNDEX { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, #endif diff --git a/src/parse.y b/src/parse.y index c8ebc76e1c..1ff1b0dcca 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.153 2004/11/12 03:56:15 drh Exp $ +** @(#) $Id: parse.y,v 1.154 2004/11/12 13:42:31 danielk1977 Exp $ */ %token_prefix TK_ %token_type {Token} @@ -953,3 +953,10 @@ cmd ::= DETACH database_kw_opt nm(D). { cmd ::= REINDEX. {sqlite3Reindex(pParse, 0, 0);} cmd ::= REINDEX nm(X) dbnm(Y). {sqlite3Reindex(pParse, &X, &Y);} %endif + +//////////////////////// ALTER TABLE table ... //////////////////////////////// +%ifndef SQLITE_OMIT_ALTERTABLE +cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). { + sqlite3AlterRenameTable(pParse,X,&Z); +} +%endif diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 50f7f61a41..4f4e1b294c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.338 2004/11/12 03:56:15 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.339 2004/11/12 13:42:31 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -107,6 +107,7 @@ /* #define SQLITE_OMIT_DATETIME_FUNCS 1 */ /* #define SQLITE_OMIT_PROGRESS_CALLBACK 1 */ /* #define SQLITE_OMIT_AUTOVACUUM */ +/* #define SQLITE_OMIT_ALTERTABLE */ /* ** GCC does not define the offsetof() macro so we'll have to do it @@ -1457,6 +1458,8 @@ sqlite3_value *sqlite3GetTransientValue(sqlite3*db); extern const unsigned char sqlite3UpperToLower[]; void sqlite3RootPageMoved(Db*, int, int); void sqlite3Reindex(Parse*, Token*, Token*); +void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); +int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); #endif diff --git a/src/tokenize.c b/src/tokenize.c index 9cda93948a..5ae6613794 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.96 2004/11/12 03:56:15 drh Exp $ +** $Id: tokenize.c,v 1.97 2004/11/12 13:42:31 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -65,7 +65,7 @@ static const char isIdChar[] = { ** Return the length of the token that begins at z[0]. ** Store the token type in *tokenType before returning. */ -static int sqliteGetToken(const unsigned char *z, int *tokenType){ +int sqlite3GetToken(const unsigned char *z, int *tokenType){ int i, c; switch( *z ){ case ' ': case '\t': case '\n': case '\f': case '\r': { @@ -349,7 +349,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ assert( i>=0 ); pParse->sLastToken.z = &zSql[i]; assert( pParse->sLastToken.dyn==0 ); - pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType); + pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType); i += pParse->sLastToken.n; switch( tokenType ){ case TK_SPACE: diff --git a/test/alter.test b/test/alter.test new file mode 100644 index 0000000000..52e7146605 --- /dev/null +++ b/test/alter.test @@ -0,0 +1,171 @@ +# +# The author or author's hereby grant to the public domain a non-exclusive, +# fully paid-up, perpetual, license in the software and all related +# intellectual property to make, have made, use, have used, reproduce, +# prepare derivative works, distribute, perform and display the work. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing the ALTER TABLE statement. +# +# $Id: alter.test,v 1.1 2004/11/12 13:42:32 danielk1977 Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + + +# Create some tables to rename. Be sure to include some TEMP tables +# and some tables with odd names. +# +do_test alter-1.1 { + execsql { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY); + INSERT INTO [t1'x1] VALUES(3,4); + CREATE INDEX t1i1 ON T1(B); + CREATE INDEX t1i2 ON t1(a,b); + CREATE INDEX i3 ON [t1'x1](b,c); + CREATE TEMP TABLE "temp table"(e,f,g UNIQUE); + CREATE INDEX i2 ON [temp table](f); + INSERT INTO [temp table] VALUES(5,6,7); + } + execsql { + SELECT 't1', * FROM t1 + UNION ALL + SELECT 't1''x1', * FROM "t1'x1" + UNION ALL + SELECT * FROM [temp table] + } +} {t1 1 2 t1'x1 3 4 5 6 7} +do_test alter-1.2 { + execsql { + SELECT type, name, tbl_name FROM sqlite_master + UNION ALL + SELECT type, name, tbl_name FROM sqlite_temp_master + ORDER BY tbl_name, type desc, name + } +} [list \ + table t1 t1 \ + index t1i1 t1 \ + index t1i2 t1 \ + table t1'x1 t1'x1 \ + index i3 t1'x1 \ + index {sqlite_autoindex_t1'x1_1} t1'x1 \ + index {sqlite_autoindex_t1'x1_2} t1'x1 \ + table {temp table} {temp table} \ + index i2 {temp table} \ + index {sqlite_autoindex_temp table_1} {temp table} \ + ] + +# Make some changes +# +do_test alter-1.3 { + execsql { + ALTER TABLE [T1] RENAME to [-t1-]; + ALTER TABLE "t1'x1" RENAME TO T2; + ALTER TABLE [temp table] RENAME to TempTab; + } +} {} +integrity_check alter-1.3.1 +do_test alter-1.4 { + execsql { + SELECT 't1', * FROM [-t1-] + UNION ALL + SELECT 't2', * FROM t2 + UNION ALL + SELECT * FROM temptab + } +} {t1 1 2 t2 3 4 5 6 7} +do_test alter-1.5 { + execsql { + SELECT type, name, tbl_name FROM sqlite_master + UNION ALL + SELECT type, name, tbl_name FROM sqlite_temp_master + ORDER BY tbl_name, type desc, name + } +} [list \ + table -t1- -t1- \ + index t1i1 -t1- \ + index t1i2 -t1- \ + table T2 T2 \ + index i3 T2 \ + index {sqlite_autoindex_T2_1} T2 \ + index {sqlite_autoindex_T2_2} T2 \ + table {TempTab} {TempTab} \ + index i2 {TempTab} \ + index {sqlite_autoindex_TempTab_1} {TempTab} \ + ] + +# Make sure the changes persist after restarting the database. +# (The TEMP table will not persist, of course.) +# +do_test alter-1.6 { + db close + set DB [sqlite3 db test.db] + execsql { + SELECT type, name, tbl_name FROM sqlite_master + UNION ALL + SELECT type, name, tbl_name FROM sqlite_temp_master + ORDER BY tbl_name, type desc, name + } +} [list \ + table -t1- -t1- \ + index t1i1 -t1- \ + index t1i2 -t1- \ + table T2 T2 \ + index i3 T2 \ + index {sqlite_autoindex_T2_1} T2 \ + index {sqlite_autoindex_T2_2} T2 \ + ] + +# Make sure the ALTER TABLE statements work with the +# non-callback API +# +do_test alter-1.7 { + stepsql $DB { + ALTER TABLE [-t1-] RENAME to [*t1*]; + ALTER TABLE T2 RENAME TO []; + } + execsql { + SELECT type, name, tbl_name FROM sqlite_master + UNION ALL + SELECT type, name, tbl_name FROM sqlite_temp_master + ORDER BY tbl_name, type desc, name + } +} [list \ + table *t1* *t1* \ + index t1i1 *t1* \ + index t1i2 *t1* \ + table \ + index i3 \ + index {sqlite_autoindex__1} \ + index {sqlite_autoindex__2} \ + ] + + +# Test error messages +# +do_test alter-2.1 { + catchsql { + ALTER TABLE none RENAME TO hi; + } +} {1 {no such table: none}} +do_test alter-2.2 { + execsql { + CREATE TABLE t3(p,q,r); + } + catchsql { + ALTER TABLE [] RENAME TO t3; + } +} {1 {there is already another table or index with this name: t3}} +do_test alter-2.3 { + catchsql { + ALTER TABLE [] RENAME TO i3; + } +} {1 {there is already another table or index with this name: i3}} + + + +finish_test diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c index 468d472f7f..003a051bbc 100644 --- a/tool/mkkeywordhash.c +++ b/tool/mkkeywordhash.c @@ -190,6 +190,7 @@ static Keyword aKeywordTable[] = { { "TEMP", "TK_TEMP", ALWAYS }, { "TEMPORARY", "TK_TEMP", ALWAYS }, { "THEN", "TK_THEN", ALWAYS }, + { "TO", "TK_TO", ALWAYS }, { "TRANSACTION", "TK_TRANSACTION", ALWAYS }, { "TRIGGER", "TK_TRIGGER", TRIGGER }, { "UNION", "TK_UNION", COMPOUND },