Index: third_party/sqlite/src/src/alter.c |
diff --git a/third_party/sqlite/src/src/alter.c b/third_party/sqlite/src/src/alter.c |
index 819d5bbf8f4486636bb6dbe791ce2b1b1b7f5cbc..aa3fa929f82353aeaeb73d61a73c812af0620507 100644 |
--- a/third_party/sqlite/src/src/alter.c |
+++ b/third_party/sqlite/src/src/alter.c |
@@ -11,8 +11,6 @@ |
************************************************************************* |
** This file contains C code routines that used to generate VDBE code |
** that implements the ALTER TABLE command. |
-** |
-** $Id: alter.c,v 1.62 2009/07/24 17:58:53 danielk1977 Exp $ |
*/ |
#include "sqliteInt.h" |
@@ -85,6 +83,69 @@ static void renameTableFunc( |
} |
} |
+/* |
+** 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); |
+ 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 ); |
+ |
+ 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:""), 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 |
@@ -165,16 +226,70 @@ static void renameTriggerFunc( |
/* |
** Register built-in functions used to help implement ALTER TABLE |
*/ |
-void sqlite3AlterFunctions(sqlite3 *db){ |
- sqlite3CreateFunc(db, "sqlite_rename_table", 2, SQLITE_UTF8, 0, |
- renameTableFunc, 0, 0); |
+void sqlite3AlterFunctions(void){ |
+ static SQLITE_WSD FuncDef aAlterTableFuncs[] = { |
+ FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), |
#ifndef SQLITE_OMIT_TRIGGER |
- sqlite3CreateFunc(db, "sqlite_rename_trigger", 2, SQLITE_UTF8, 0, |
- renameTriggerFunc, 0, 0); |
+ FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), |
+#endif |
+#ifndef SQLITE_OMIT_FOREIGN_KEY |
+ FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), |
#endif |
+ }; |
+ int i; |
+ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); |
+ FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs); |
+ |
+ for(i=0; i<ArraySize(aAlterTableFuncs); i++){ |
+ sqlite3FuncDefInsert(pHash, &aFunc[i]); |
+ } |
} |
/* |
+** This function is used to create the text of expressions of the form: |
+** |
+** name=<constant1> OR name=<constant2> OR ... |
+** |
+** If argument zWhere is NULL, then a pointer string containing the text |
+** "name=<constant>" is returned, where <constant> is the quoted version |
+** of the string passed as argument zConstant. The returned buffer is |
+** allocated using sqlite3DbMalloc(). It is the responsibility of the |
+** caller to ensure that it is eventually freed. |
+** |
+** If argument zWhere is not NULL, then the string returned is |
+** "<where> OR name=<constant>", where <where> is the contents of zWhere. |
+** In this case zWhere is passed to sqlite3DbFree() before returning. |
+** |
+*/ |
+static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){ |
+ char *zNew; |
+ if( !zWhere ){ |
+ zNew = sqlite3MPrintf(db, "name=%Q", zConstant); |
+ }else{ |
+ zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant); |
+ sqlite3DbFree(db, zWhere); |
+ } |
+ return zNew; |
+} |
+ |
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) |
+/* |
+** Generate the text of a WHERE expression which can be used to select all |
+** tables that have foreign key constraints that refer to table pTab (i.e. |
+** constraints for which pTab is the parent table) from the sqlite_master |
+** table. |
+*/ |
+static char *whereForeignKeys(Parse *pParse, Table *pTab){ |
+ FKey *p; |
+ char *zWhere = 0; |
+ for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ |
+ zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName); |
+ } |
+ return zWhere; |
+} |
+#endif |
+ |
+/* |
** Generate the text of a WHERE expression which can be used to select all |
** temporary triggers on table pTab from the sqlite_temp_master table. If |
** table pTab has no temporary triggers, or is itself stored in the |
@@ -183,7 +298,6 @@ void sqlite3AlterFunctions(sqlite3 *db){ |
static char *whereTempTriggers(Parse *pParse, Table *pTab){ |
Trigger *pTrig; |
char *zWhere = 0; |
- char *tmp = 0; |
const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */ |
/* If the table is not located in the temp-db (in which case NULL is |
@@ -195,16 +309,15 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){ |
sqlite3 *db = pParse->db; |
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ |
if( pTrig->pSchema==pTempSchema ){ |
- if( !zWhere ){ |
- zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->zName); |
- }else{ |
- tmp = zWhere; |
- zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->zName); |
- sqlite3DbFree(db, tmp); |
- } |
+ zWhere = whereOrName(db, zWhere, pTrig->zName); |
} |
} |
} |
+ if( zWhere ){ |
+ char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere); |
+ sqlite3DbFree(pParse->db, zWhere); |
+ zWhere = zNew; |
+ } |
return zWhere; |
} |
@@ -239,7 +352,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ |
} |
#endif |
- /* Drop the table and index from the internal schema */ |
+ /* Drop the table and index from the internal schema. */ |
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); |
/* Reload the table, index and permanent trigger schemas. */ |
@@ -258,6 +371,22 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ |
} |
/* |
+** Parameter zName is the name of a table that is about to be altered |
+** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). |
+** If the table is a system table, this function leaves an error message |
+** in pParse->zErr (system tables may not be altered) and returns non-zero. |
+** |
+** Or, if zName is not a system table, zero is returned. |
+*/ |
+static int isSystemTable(Parse *pParse, const char *zName){ |
+ if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ |
+ sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); |
+ return 1; |
+ } |
+ return 0; |
+} |
+ |
+/* |
** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" |
** command. |
*/ |
@@ -278,7 +407,9 @@ void sqlite3AlterRenameTable( |
char *zWhere = 0; /* Where clause to locate temp triggers */ |
#endif |
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ |
- |
+ int savedDbFlags; /* Saved value of db->flags */ |
+ |
+ savedDbFlags = db->flags; |
if( NEVER(db->mallocFailed) ) goto exit_rename_table; |
assert( pSrc->nSrc==1 ); |
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); |
@@ -287,6 +418,7 @@ void sqlite3AlterRenameTable( |
if( !pTab ) goto exit_rename_table; |
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
zDb = db->aDb[iDb].zName; |
+ db->flags |= SQLITE_PreferBuiltin; |
/* Get a NULL terminated version of the new table name. */ |
zName = sqlite3NameFromToken(db, pName); |
@@ -304,14 +436,11 @@ void sqlite3AlterRenameTable( |
/* Make sure it is not a system table being altered, or a reserved name |
** that the table is being renamed to. |
*/ |
- if( sqlite3Strlen30(pTab->zName)>6 |
- && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) |
- ){ |
- sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); |
+ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ |
goto exit_rename_table; |
} |
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ |
- goto exit_rename_table; |
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto |
+ exit_rename_table; |
} |
#ifndef SQLITE_OMIT_VIEW |
@@ -370,6 +499,21 @@ 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, SCHEMA_TABLE(iDb), zTabName, zName, zWhere); |
+ sqlite3DbFree(db, zWhere); |
+ } |
+ } |
+#endif |
+ |
/* Modify the sqlite_master table to use the new table name. */ |
sqlite3NestedParse(pParse, |
"UPDATE %Q.%s SET " |
@@ -421,12 +565,25 @@ void sqlite3AlterRenameTable( |
} |
#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); |
exit_rename_table: |
sqlite3SrcListDelete(db, pSrc); |
sqlite3DbFree(db, zName); |
+ db->flags = savedDbFlags; |
} |
@@ -515,6 +672,11 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ |
sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); |
return; |
} |
+ if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ |
+ sqlite3ErrorMsg(pParse, |
+ "Cannot add a REFERENCES column with non-NULL default value"); |
+ return; |
+ } |
if( pCol->notNull && !pDflt ){ |
sqlite3ErrorMsg(pParse, |
"Cannot add a NOT NULL column with default value NULL"); |
@@ -541,9 +703,11 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ |
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
if( zCol ){ |
char *zEnd = &zCol[pColDef->n-1]; |
+ int savedDbFlags = db->flags; |
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ |
*zEnd-- = '\0'; |
} |
+ db->flags |= SQLITE_PreferBuiltin; |
sqlite3NestedParse(pParse, |
"UPDATE \"%w\".%s SET " |
"sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) " |
@@ -552,6 +716,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ |
zTab |
); |
sqlite3DbFree(db, zCol); |
+ db->flags = savedDbFlags; |
} |
/* If the default value of the new column is NULL, then set the file |
@@ -607,6 +772,9 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ |
sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); |
goto exit_begin_add_column; |
} |
+ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ |
+ goto exit_begin_add_column; |
+ } |
assert( pTab->addColOffset>0 ); |
iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
@@ -622,7 +790,6 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ |
if( !pNew ) goto exit_begin_add_column; |
pParse->pNewTable = pNew; |
pNew->nRef = 1; |
- pNew->dbMem = pTab->dbMem; |
pNew->nCol = pTab->nCol; |
assert( pNew->nCol>0 ); |
nAlloc = (((pNew->nCol-1)/8)*8)+8; |