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 | ** |
drh | 1f01ec1 | 2005-02-15 21:36:18 +0000 | [diff] [blame^] | 15 | ** $Id: alter.c,v 1.2 2005/02/15 21:36:18 drh Exp $ |
drh | d0e4a6c | 2005-02-15 20:47:57 +0000 | [diff] [blame] | 16 | */ |
| 17 | #include "sqliteInt.h" |
| 18 | |
drh | 1f01ec1 | 2005-02-15 21:36:18 +0000 | [diff] [blame^] | 19 | /* |
| 20 | ** The code in this file only exists if we are not omitting the |
| 21 | ** ALTER TABLE logic from the build. |
| 22 | */ |
drh | d0e4a6c | 2005-02-15 20:47:57 +0000 | [diff] [blame] | 23 | #ifndef SQLITE_OMIT_ALTERTABLE |
drh | 1f01ec1 | 2005-02-15 21:36:18 +0000 | [diff] [blame^] | 24 | |
| 25 | |
| 26 | /* |
| 27 | ** This function is used by SQL generated to implement the |
| 28 | ** ALTER TABLE command. The first argument is the text of a CREATE TABLE or |
| 29 | ** CREATE INDEX command. The second is a table name. The table name in |
| 30 | ** the CREATE TABLE or CREATE INDEX statement is replaced with the second |
| 31 | ** argument and the result returned. Examples: |
| 32 | ** |
| 33 | ** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def') |
| 34 | ** -> 'CREATE TABLE def(a, b, c)' |
| 35 | ** |
| 36 | ** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def') |
| 37 | ** -> 'CREATE INDEX i ON def(a, b, c)' |
| 38 | */ |
| 39 | static void renameTableFunc( |
| 40 | sqlite3_context *context, |
| 41 | int argc, |
| 42 | sqlite3_value **argv |
| 43 | ){ |
| 44 | unsigned char const *zSql = sqlite3_value_text(argv[0]); |
| 45 | unsigned char const *zTableName = sqlite3_value_text(argv[1]); |
| 46 | |
| 47 | int token; |
| 48 | Token tname; |
| 49 | char const *zCsr = zSql; |
| 50 | int len = 0; |
| 51 | char *zRet; |
| 52 | |
| 53 | /* The principle used to locate the table name in the CREATE TABLE |
| 54 | ** statement is that the table name is the first token that is immediatedly |
| 55 | ** followed by a left parenthesis - TK_LP. |
| 56 | */ |
| 57 | if( zSql ){ |
| 58 | do { |
| 59 | /* Store the token that zCsr points to in tname. */ |
| 60 | tname.z = zCsr; |
| 61 | tname.n = len; |
| 62 | |
| 63 | /* Advance zCsr to the next token. Store that token type in 'token', |
| 64 | ** and it's length in 'len' (to be used next iteration of this loop). |
| 65 | */ |
| 66 | do { |
| 67 | zCsr += len; |
| 68 | len = sqlite3GetToken(zCsr, &token); |
| 69 | } while( token==TK_SPACE ); |
| 70 | assert( len>0 ); |
| 71 | } while( token!=TK_LP ); |
| 72 | |
| 73 | zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, |
| 74 | zTableName, tname.z+tname.n); |
| 75 | sqlite3_result_text(context, zRet, -1, sqlite3FreeX); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | #ifndef SQLITE_OMIT_TRIGGER |
| 80 | /* This function is used by SQL generated to implement the ALTER TABLE |
| 81 | ** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER |
| 82 | ** statement. The second is a table name. The table name in the CREATE |
| 83 | ** TRIGGER statement is replaced with the second argument and the result |
| 84 | ** returned. This is analagous to renameTableFunc() above, except for CREATE |
| 85 | ** TRIGGER, not CREATE INDEX and CREATE TABLE. |
| 86 | */ |
| 87 | static void renameTriggerFunc( |
| 88 | sqlite3_context *context, |
| 89 | int argc, |
| 90 | sqlite3_value **argv |
| 91 | ){ |
| 92 | unsigned char const *zSql = sqlite3_value_text(argv[0]); |
| 93 | unsigned char const *zTableName = sqlite3_value_text(argv[1]); |
| 94 | |
| 95 | int token; |
| 96 | Token tname; |
| 97 | int dist = 3; |
| 98 | char const *zCsr = zSql; |
| 99 | int len = 0; |
| 100 | char *zRet; |
| 101 | |
| 102 | /* The principle used to locate the table name in the CREATE TRIGGER |
| 103 | ** statement is that the table name is the first token that is immediatedly |
| 104 | ** preceded by either TK_ON or TK_DOT and immediatedly followed by one |
| 105 | ** of TK_WHEN, TK_BEGIN or TK_FOR. |
| 106 | */ |
| 107 | if( zSql ){ |
| 108 | do { |
| 109 | /* Store the token that zCsr points to in tname. */ |
| 110 | tname.z = zCsr; |
| 111 | tname.n = len; |
| 112 | |
| 113 | /* Advance zCsr to the next token. Store that token type in 'token', |
| 114 | ** and it's length in 'len' (to be used next iteration of this loop). |
| 115 | */ |
| 116 | do { |
| 117 | zCsr += len; |
| 118 | len = sqlite3GetToken(zCsr, &token); |
| 119 | }while( token==TK_SPACE ); |
| 120 | assert( len>0 ); |
| 121 | |
| 122 | /* Variable 'dist' stores the number of tokens read since the most |
| 123 | ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN |
| 124 | ** token is read and 'dist' equals 2, the condition stated above |
| 125 | ** to be met. |
| 126 | ** |
| 127 | ** Note that ON cannot be a database, table or column name, so |
| 128 | ** there is no need to worry about syntax like |
| 129 | ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. |
| 130 | */ |
| 131 | dist++; |
| 132 | if( token==TK_DOT || token==TK_ON ){ |
| 133 | dist = 0; |
| 134 | } |
| 135 | } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); |
| 136 | |
| 137 | /* Variable tname now contains the token that is the old table-name |
| 138 | ** in the CREATE TRIGGER statement. |
| 139 | */ |
| 140 | zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, |
| 141 | zTableName, tname.z+tname.n); |
| 142 | sqlite3_result_text(context, zRet, -1, sqlite3FreeX); |
| 143 | } |
| 144 | } |
| 145 | #endif /* !SQLITE_OMIT_TRIGGER */ |
| 146 | |
| 147 | /* |
| 148 | ** Register built-in functions used to help implement ALTER TABLE |
| 149 | */ |
| 150 | void sqlite3AlterFunctions(sqlite3 *db){ |
| 151 | static const struct { |
| 152 | char *zName; |
| 153 | signed char nArg; |
| 154 | void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
| 155 | } aFuncs[] = { |
| 156 | { "sqlite_rename_table", 2, renameTableFunc}, |
| 157 | #ifndef SQLITE_OMIT_TRIGGER |
| 158 | { "sqlite_rename_trigger", 2, renameTriggerFunc}, |
| 159 | #endif |
| 160 | }; |
| 161 | int i; |
| 162 | |
| 163 | for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ |
| 164 | sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, |
| 165 | SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0); |
| 166 | } |
| 167 | } |
| 168 | |
drh | d0e4a6c | 2005-02-15 20:47:57 +0000 | [diff] [blame] | 169 | /* |
| 170 | ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" |
| 171 | ** command. |
| 172 | */ |
| 173 | void sqlite3AlterRenameTable( |
| 174 | Parse *pParse, /* Parser context. */ |
| 175 | SrcList *pSrc, /* The table to rename. */ |
| 176 | Token *pName /* The new table name. */ |
| 177 | ){ |
| 178 | int iDb; /* Database that contains the table */ |
| 179 | char *zDb; /* Name of database iDb */ |
| 180 | Table *pTab; /* Table being renamed */ |
| 181 | char *zName = 0; /* NULL-terminated version of pName */ |
| 182 | char *zWhere = 0; /* Where clause of schema elements to reparse */ |
| 183 | sqlite3 *db = pParse->db; /* Database connection */ |
| 184 | Vdbe *v; |
| 185 | #ifndef SQLITE_OMIT_TRIGGER |
| 186 | char *zTempTrig = 0; /* Where clause to locate temp triggers */ |
| 187 | #endif |
| 188 | |
| 189 | assert( pSrc->nSrc==1 ); |
| 190 | |
| 191 | pTab = sqlite3LocateTable(pParse, pSrc->a[0].zName, pSrc->a[0].zDatabase); |
| 192 | if( !pTab ) goto exit_rename_table; |
| 193 | iDb = pTab->iDb; |
| 194 | zDb = db->aDb[iDb].zName; |
| 195 | |
| 196 | /* Get a NULL terminated version of the new table name. */ |
| 197 | zName = sqlite3NameFromToken(pName); |
| 198 | if( !zName ) goto exit_rename_table; |
| 199 | |
| 200 | /* Check that a table or index named 'zName' does not already exist |
| 201 | ** in database iDb. If so, this is an error. |
| 202 | */ |
| 203 | if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){ |
| 204 | sqlite3ErrorMsg(pParse, |
| 205 | "there is already another table or index with this name: %s", zName); |
| 206 | goto exit_rename_table; |
| 207 | } |
| 208 | |
| 209 | /* Make sure it is not a system table being altered, or a reserved name |
| 210 | ** that the table is being renamed to. |
| 211 | */ |
| 212 | if( strlen(pTab->zName)>6 && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ){ |
| 213 | sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); |
| 214 | goto exit_rename_table; |
| 215 | } |
| 216 | if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ |
| 217 | goto exit_rename_table; |
| 218 | } |
| 219 | |
| 220 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 221 | /* Invoke the authorization callback. */ |
| 222 | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| 223 | goto exit_rename_table; |
| 224 | } |
| 225 | #endif |
| 226 | |
| 227 | /* Begin a transaction and code the VerifyCookie for database iDb. |
| 228 | ** Then modify the schema cookie (since the ALTER TABLE modifies the |
| 229 | ** schema). |
| 230 | */ |
| 231 | v = sqlite3GetVdbe(pParse); |
| 232 | if( v==0 ){ |
| 233 | goto exit_rename_table; |
| 234 | } |
| 235 | sqlite3BeginWriteOperation(pParse, 0, iDb); |
| 236 | sqlite3ChangeCookie(db, v, iDb); |
| 237 | |
| 238 | /* Modify the sqlite_master table to use the new table name. */ |
| 239 | sqlite3NestedParse(pParse, |
| 240 | "UPDATE %Q.%s SET " |
| 241 | #ifdef SQLITE_OMIT_TRIGGER |
| 242 | "sql = sqlite_rename_table(sql, %Q), " |
| 243 | #else |
| 244 | "sql = CASE " |
| 245 | "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)" |
| 246 | "ELSE sqlite_rename_table(sql, %Q) END, " |
| 247 | #endif |
| 248 | "tbl_name = %Q, " |
| 249 | "name = CASE " |
| 250 | "WHEN type='table' THEN %Q " |
| 251 | "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " |
| 252 | "'sqlite_autoindex_' || %Q || substr(name, %d+18,10) " |
| 253 | "ELSE name END " |
| 254 | "WHERE tbl_name=%Q AND " |
| 255 | "(type='table' OR type='index' OR type='trigger');", |
| 256 | zDb, SCHEMA_TABLE(iDb), zName, zName, zName, |
| 257 | #ifndef SQLITE_OMIT_TRIGGER |
| 258 | zName, |
| 259 | #endif |
| 260 | zName, strlen(pTab->zName), pTab->zName |
| 261 | ); |
| 262 | |
| 263 | #ifndef SQLITE_OMIT_AUTOINCREMENT |
| 264 | /* If the sqlite_sequence table exists in this database, then update |
| 265 | ** it with the new table name. |
| 266 | */ |
| 267 | if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ |
| 268 | sqlite3NestedParse(pParse, |
| 269 | "UPDATE %Q.sqlite_sequence set name = %Q WHERE name = %Q", |
| 270 | zDb, zName, pTab->zName); |
| 271 | } |
| 272 | #endif |
| 273 | |
| 274 | #ifndef SQLITE_OMIT_TRIGGER |
| 275 | /* If there are TEMP triggers on this table, modify the sqlite_temp_master |
| 276 | ** table. Don't do this if the table being ALTERed is itself located in |
| 277 | ** the temp database. |
| 278 | */ |
| 279 | if( iDb!=1 ){ |
| 280 | Trigger *pTrig; |
| 281 | char *tmp = 0; |
| 282 | for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ |
| 283 | if( pTrig->iDb==1 ){ |
| 284 | if( !zTempTrig ){ |
| 285 | zTempTrig = |
| 286 | sqlite3MPrintf("type = 'trigger' AND (name=%Q", pTrig->name); |
| 287 | }else{ |
| 288 | tmp = zTempTrig; |
| 289 | zTempTrig = sqlite3MPrintf("%s OR name=%Q", zTempTrig, pTrig->name); |
| 290 | sqliteFree(tmp); |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | if( zTempTrig ){ |
| 295 | tmp = zTempTrig; |
| 296 | zTempTrig = sqlite3MPrintf("%s)", zTempTrig); |
| 297 | sqliteFree(tmp); |
| 298 | sqlite3NestedParse(pParse, |
| 299 | "UPDATE sqlite_temp_master SET " |
| 300 | "sql = sqlite_rename_trigger(sql, %Q), " |
| 301 | "tbl_name = %Q " |
| 302 | "WHERE %s;", zName, zName, zTempTrig); |
| 303 | } |
| 304 | } |
| 305 | #endif |
| 306 | |
| 307 | /* Drop the elements of the in-memory schema that refered to the table |
| 308 | ** renamed and load the new versions from the database. |
| 309 | */ |
| 310 | if( pParse->nErr==0 ){ |
| 311 | #ifndef SQLITE_OMIT_TRIGGER |
| 312 | Trigger *pTrig; |
| 313 | for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ |
| 314 | assert( pTrig->iDb==iDb || pTrig->iDb==1 ); |
| 315 | sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->iDb, 0, pTrig->name, 0); |
| 316 | } |
| 317 | #endif |
| 318 | sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); |
| 319 | zWhere = sqlite3MPrintf("tbl_name=%Q", zName); |
| 320 | sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC); |
| 321 | #ifndef SQLITE_OMIT_TRIGGER |
| 322 | if( zTempTrig ){ |
| 323 | sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zTempTrig, P3_DYNAMIC); |
| 324 | } |
| 325 | }else{ |
| 326 | sqliteFree(zTempTrig); |
| 327 | #endif |
| 328 | } |
| 329 | |
| 330 | exit_rename_table: |
| 331 | sqlite3SrcListDelete(pSrc); |
| 332 | sqliteFree(zName); |
| 333 | } |
| 334 | #endif /* SQLITE_ALTER_TABLE */ |