drh | d0e4a6c | 2005-02-15 20:47:57 +0000 | [diff] [blame^] | 1 | /* |
| 2 | ** 2005 February 15 |
| 3 | ** |
| 4 | ** The author disclaims copyright to this source code. In place of |
| 5 | ** a legal notice, here is a blessing: |
| 6 | ** |
| 7 | ** May you do good and not evil. |
| 8 | ** May you find forgiveness for yourself and forgive others. |
| 9 | ** May you share freely, never taking more than you give. |
| 10 | ** |
| 11 | ************************************************************************* |
| 12 | ** This file contains C code routines that used to generate VDBE code |
| 13 | ** that implements the ALTER TABLE command. |
| 14 | ** |
| 15 | ** $Id: alter.c,v 1.1 2005/02/15 20:47:57 drh Exp $ |
| 16 | */ |
| 17 | #include "sqliteInt.h" |
| 18 | |
| 19 | |
| 20 | #ifndef SQLITE_OMIT_ALTERTABLE |
| 21 | /* |
| 22 | ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" |
| 23 | ** command. |
| 24 | */ |
| 25 | void sqlite3AlterRenameTable( |
| 26 | Parse *pParse, /* Parser context. */ |
| 27 | SrcList *pSrc, /* The table to rename. */ |
| 28 | Token *pName /* The new table name. */ |
| 29 | ){ |
| 30 | int iDb; /* Database that contains the table */ |
| 31 | char *zDb; /* Name of database iDb */ |
| 32 | Table *pTab; /* Table being renamed */ |
| 33 | char *zName = 0; /* NULL-terminated version of pName */ |
| 34 | char *zWhere = 0; /* Where clause of schema elements to reparse */ |
| 35 | sqlite3 *db = pParse->db; /* Database connection */ |
| 36 | Vdbe *v; |
| 37 | #ifndef SQLITE_OMIT_TRIGGER |
| 38 | char *zTempTrig = 0; /* Where clause to locate temp triggers */ |
| 39 | #endif |
| 40 | |
| 41 | assert( pSrc->nSrc==1 ); |
| 42 | |
| 43 | pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); |
| 44 | if( !pTab ) goto exit_rename_table; |
| 45 | iDb = pTab->iDb; |
| 46 | zDb = db->aDb[iDb].zName; |
| 47 | |
| 48 | /* Get a NULL terminated version of the new table name. */ |
| 49 | zName = sqlite3NameFromToken(pName); |
| 50 | if( !zName ) goto exit_rename_table; |
| 51 | |
| 52 | /* Check that a table or index named 'zName' does not already exist |
| 53 | ** in database iDb. If so, this is an error. |
| 54 | */ |
| 55 | if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ |
| 56 | sqlite3ErrorMsg(pParse, |
| 57 | "there is already another table or index with this name: %s", zName); |
| 58 | goto exit_rename_table; |
| 59 | } |
| 60 | |
| 61 | /* Make sure it is not a system table being altered, or a reserved name |
| 62 | ** that the table is being renamed to. |
| 63 | */ |
| 64 | if( strlen(pTab->zName)>6 && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ){ |
| 65 | sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); |
| 66 | goto exit_rename_table; |
| 67 | } |
| 68 | if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ |
| 69 | goto exit_rename_table; |
| 70 | } |
| 71 | |
| 72 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 73 | /* Invoke the authorization callback. */ |
| 74 | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| 75 | goto exit_rename_table; |
| 76 | } |
| 77 | #endif |
| 78 | |
| 79 | /* Begin a transaction and code the VerifyCookie for database iDb. |
| 80 | ** Then modify the schema cookie (since the ALTER TABLE modifies the |
| 81 | ** schema). |
| 82 | */ |
| 83 | v = sqlite3GetVdbe(pParse); |
| 84 | if( v==0 ){ |
| 85 | goto exit_rename_table; |
| 86 | } |
| 87 | sqlite3BeginWriteOperation(pParse, 0, iDb); |
| 88 | sqlite3ChangeCookie(db, v, iDb); |
| 89 | |
| 90 | /* Modify the sqlite_master table to use the new table name. */ |
| 91 | sqlite3NestedParse(pParse, |
| 92 | "UPDATE %Q.%s SET " |
| 93 | #ifdef SQLITE_OMIT_TRIGGER |
| 94 | "sql = sqlite_rename_table(sql, %Q), " |
| 95 | #else |
| 96 | "sql = CASE " |
| 97 | "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" |
| 98 | "ELSE sqlite_rename_table(sql, %Q) END, " |
| 99 | #endif |
| 100 | "tbl_name = %Q, " |
| 101 | "name = CASE " |
| 102 | "WHEN type='table' THEN %Q " |
| 103 | "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " |
| 104 | "'sqlite_autoindex_' || %Q || substr(name, %d+18,10) " |
| 105 | "ELSE name END " |
| 106 | "WHERE tbl_name=%Q AND " |
| 107 | "(type='table' OR type='index' OR type='trigger');", |
| 108 | zDb, SCHEMA_TABLE(iDb), zName, zName, zName, |
| 109 | #ifndef SQLITE_OMIT_TRIGGER |
| 110 | zName, |
| 111 | #endif |
| 112 | zName, strlen(pTab->zName), pTab->zName |
| 113 | ); |
| 114 | |
| 115 | #ifndef SQLITE_OMIT_AUTOINCREMENT |
| 116 | /* If the sqlite_sequence table exists in this database, then update |
| 117 | ** it with the new table name. |
| 118 | */ |
| 119 | if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ |
| 120 | sqlite3NestedParse(pParse, |
| 121 | "UPDATE %Q.sqlite_sequence set name = %Q WHERE name = %Q", |
| 122 | zDb, zName, pTab->zName); |
| 123 | } |
| 124 | #endif |
| 125 | |
| 126 | #ifndef SQLITE_OMIT_TRIGGER |
| 127 | /* If there are TEMP triggers on this table, modify the sqlite_temp_master |
| 128 | ** table. Don't do this if the table being ALTERed is itself located in |
| 129 | ** the temp database. |
| 130 | */ |
| 131 | if( iDb!=1 ){ |
| 132 | Trigger *pTrig; |
| 133 | char *tmp = 0; |
| 134 | for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ |
| 135 | if( pTrig->iDb==1 ){ |
| 136 | if( !zTempTrig ){ |
| 137 | zTempTrig = |
| 138 | sqlite3MPrintf("type = 'trigger' AND (name=%Q", pTrig->name); |
| 139 | }else{ |
| 140 | tmp = zTempTrig; |
| 141 | zTempTrig = sqlite3MPrintf("%s OR name=%Q", zTempTrig, pTrig->name); |
| 142 | sqliteFree(tmp); |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | if( zTempTrig ){ |
| 147 | tmp = zTempTrig; |
| 148 | zTempTrig = sqlite3MPrintf("%s)", zTempTrig); |
| 149 | sqliteFree(tmp); |
| 150 | sqlite3NestedParse(pParse, |
| 151 | "UPDATE sqlite_temp_master SET " |
| 152 | "sql = sqlite_rename_trigger(sql, %Q), " |
| 153 | "tbl_name = %Q " |
| 154 | "WHERE %s;", zName, zName, zTempTrig); |
| 155 | } |
| 156 | } |
| 157 | #endif |
| 158 | |
| 159 | /* Drop the elements of the in-memory schema that refered to the table |
| 160 | ** renamed and load the new versions from the database. |
| 161 | */ |
| 162 | if( pParse->nErr==0 ){ |
| 163 | #ifndef SQLITE_OMIT_TRIGGER |
| 164 | Trigger *pTrig; |
| 165 | for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ |
| 166 | assert( pTrig->iDb==iDb || pTrig->iDb==1 ); |
| 167 | sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0); |
| 168 | } |
| 169 | #endif |
| 170 | sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); |
| 171 | zWhere = sqlite3MPrintf("tbl_name=%Q", zName); |
| 172 | sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC); |
| 173 | #ifndef SQLITE_OMIT_TRIGGER |
| 174 | if( zTempTrig ){ |
| 175 | sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zTempTrig, P3_DYNAMIC); |
| 176 | } |
| 177 | }else{ |
| 178 | sqliteFree(zTempTrig); |
| 179 | #endif |
| 180 | } |
| 181 | |
| 182 | exit_rename_table: |
| 183 | sqlite3SrcListDelete(pSrc); |
| 184 | sqliteFree(zName); |
| 185 | } |
| 186 | #endif /* SQLITE_ALTER_TABLE */ |