| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include "sqliteInt.h" |
| |
|
| | |
| | |
| | |
| | |
| | #ifndef SQLITE_OMIT_ALTERTABLE |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int isAlterableTable(Parse *pParse, Table *pTab){ |
| | if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) |
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | || (pTab->tabFlags & TF_Eponymous)!=0 |
| | || ( (pTab->tabFlags & TF_Shadow)!=0 |
| | && sqlite3ReadOnlyShadowTables(pParse->db) |
| | ) |
| | #endif |
| | ){ |
| | sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); |
| | return 1; |
| | } |
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameTestSchema( |
| | Parse *pParse, |
| | const char *zDb, |
| | int bTemp, |
| | const char *zWhen, |
| | int bNoDQS |
| | ){ |
| | pParse->colNamesSet = 1; |
| | sqlite3NestedParse(pParse, |
| | "SELECT 1 " |
| | "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " |
| | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| | " AND sql NOT LIKE 'create virtual%%'" |
| | " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ", |
| | zDb, |
| | zDb, bTemp, zWhen, bNoDQS |
| | ); |
| |
|
| | if( bTemp==0 ){ |
| | sqlite3NestedParse(pParse, |
| | "SELECT 1 " |
| | "FROM temp." LEGACY_SCHEMA_TABLE " " |
| | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| | " AND sql NOT LIKE 'create virtual%%'" |
| | " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ", |
| | zDb, zWhen, bNoDQS |
| | ); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){ |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE |
| | " SET sql = sqlite_rename_quotefix(%Q, sql)" |
| | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| | " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb |
| | ); |
| | if( bTemp==0 ){ |
| | sqlite3NestedParse(pParse, |
| | "UPDATE temp." LEGACY_SCHEMA_TABLE |
| | " SET sql = sqlite_rename_quotefix('temp', sql)" |
| | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| | " AND sql NOT LIKE 'create virtual%%'" |
| | ); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){ |
| | Vdbe *v = pParse->pVdbe; |
| | if( v ){ |
| | sqlite3ChangeCookie(pParse, iDb); |
| | sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5); |
| | if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | void sqlite3AlterRenameTable( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | Token *pName |
| | ){ |
| | int iDb; |
| | char *zDb; |
| | Table *pTab; |
| | char *zName = 0; |
| | sqlite3 *db = pParse->db; |
| | int nTabName; |
| | const char *zTabName; |
| | Vdbe *v; |
| | VTable *pVTab = 0; |
| |
|
| | if( NEVER(db->mallocFailed) ) goto exit_rename_table; |
| | assert( pSrc->nSrc==1 ); |
| | assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); |
| |
|
| | pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| | if( !pTab ) goto exit_rename_table; |
| | iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); |
| | zDb = db->aDb[iDb].zDbSName; |
| |
|
| | |
| | zName = sqlite3NameFromToken(db, pName); |
| | if( !zName ) goto exit_rename_table; |
| |
|
| | |
| | |
| | |
| | if( sqlite3FindTable(db, zName, zDb) |
| | || sqlite3FindIndex(db, zName, zDb) |
| | || sqlite3IsShadowTableOf(db, pTab, zName) |
| | ){ |
| | sqlite3ErrorMsg(pParse, |
| | "there is already another table or index with this name: %s", zName); |
| | goto exit_rename_table; |
| | } |
| |
|
| | |
| | |
| | |
| | if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ |
| | goto exit_rename_table; |
| | } |
| | if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){ |
| | goto exit_rename_table; |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_VIEW |
| | if( IsView(pTab) ){ |
| | sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); |
| | goto exit_rename_table; |
| | } |
| | #endif |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| | goto exit_rename_table; |
| | } |
| | #endif |
| |
|
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | if( sqlite3ViewGetColumnNames(pParse, pTab) ){ |
| | goto exit_rename_table; |
| | } |
| | if( IsVirtual(pTab) ){ |
| | pVTab = sqlite3GetVTable(db, pTab); |
| | if( pVTab->pVtab->pModule->xRename==0 ){ |
| | pVTab = 0; |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | v = sqlite3GetVdbe(pParse); |
| | if( v==0 ){ |
| | goto exit_rename_table; |
| | } |
| | sqlite3MayAbort(pParse); |
| |
|
| | |
| | zTabName = pTab->zName; |
| | nTabName = sqlite3Utf8CharLen(zTabName, -1); |
| |
|
| | |
| | |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) " |
| | "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)" |
| | "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'" |
| | , zDb, zDb, zTabName, zName, (iDb==1), zTabName |
| | ); |
| |
|
| | |
| | |
| | sqlite3NestedParse(pParse, |
| | "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET " |
| | "tbl_name = %Q, " |
| | "name = CASE " |
| | "WHEN type='table' THEN %Q " |
| | "WHEN name LIKE 'sqliteX_autoindex%%' ESCAPE 'X' " |
| | " AND type='index' THEN " |
| | "'sqlite_autoindex_' || %Q || substr(name,%d+18) " |
| | "ELSE name END " |
| | "WHERE tbl_name=%Q COLLATE nocase AND " |
| | "(type='table' OR type='index' OR type='trigger');", |
| | zDb, |
| | zName, zName, zName, |
| | nTabName, zTabName |
| | ); |
| |
|
| | #ifndef SQLITE_OMIT_AUTOINCREMENT |
| | |
| | |
| | |
| | if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){ |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\".sqlite_sequence set name = %Q WHERE name = %Q", |
| | zDb, zName, pTab->zName); |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | if( iDb!=1 ){ |
| | sqlite3NestedParse(pParse, |
| | "UPDATE sqlite_temp_schema SET " |
| | "sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), " |
| | "tbl_name = " |
| | "CASE WHEN tbl_name=%Q COLLATE nocase AND " |
| | " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) " |
| | "THEN %Q ELSE tbl_name END " |
| | "WHERE type IN ('view', 'trigger')" |
| | , zDb, zTabName, zName, zTabName, zDb, zName); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | if( pVTab ){ |
| | int i = ++pParse->nMem; |
| | sqlite3VdbeLoadString(v, i, zName); |
| | sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); |
| | } |
| | #endif |
| |
|
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); |
| | renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); |
| |
|
| | exit_rename_table: |
| | sqlite3SrcListDelete(db, pSrc); |
| | sqlite3DbFree(db, zName); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void sqlite3ErrorIfNotEmpty( |
| | Parse *pParse, |
| | const char *zDb, |
| | const char *zTab, |
| | const char *zErr |
| | ){ |
| | sqlite3NestedParse(pParse, |
| | "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"", |
| | zErr, zDb, zTab |
| | ); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ |
| | Table *pNew; |
| | Table *pTab; |
| | int iDb; |
| | const char *zDb; |
| | const char *zTab; |
| | char *zCol; |
| | Column *pCol; |
| | Expr *pDflt; |
| | sqlite3 *db; |
| | Vdbe *v; |
| | int r1; |
| |
|
| | db = pParse->db; |
| | assert( db->pParse==pParse ); |
| | if( pParse->nErr ) return; |
| | assert( db->mallocFailed==0 ); |
| | pNew = pParse->pNewTable; |
| | assert( pNew ); |
| |
|
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | iDb = sqlite3SchemaToIndex(db, pNew->pSchema); |
| | zDb = db->aDb[iDb].zDbSName; |
| | zTab = &pNew->zName[16]; |
| | pCol = &pNew->aCol[pNew->nCol-1]; |
| | pDflt = sqlite3ColumnExpr(pNew, pCol); |
| | pTab = sqlite3FindTable(db, zTab, zDb); |
| | assert( pTab ); |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| | return; |
| | } |
| | #endif |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | if( pCol->colFlags & COLFLAG_PRIMKEY ){ |
| | sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column"); |
| | return; |
| | } |
| | if( pNew->pIndex ){ |
| | sqlite3ErrorMsg(pParse, |
| | "Cannot add a UNIQUE column"); |
| | return; |
| | } |
| | if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){ |
| | |
| | |
| | |
| | |
| | assert( pDflt==0 || pDflt->op==TK_SPAN ); |
| | if( pDflt && pDflt->pLeft->op==TK_NULL ){ |
| | pDflt = 0; |
| | } |
| | assert( IsOrdinaryTable(pNew) ); |
| | if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){ |
| | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| | "Cannot add a REFERENCES column with non-NULL default value"); |
| | } |
| | if( pCol->notNull && !pDflt ){ |
| | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| | "Cannot add a NOT NULL column with default value NULL"); |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | if( pDflt ){ |
| | sqlite3_value *pVal = 0; |
| | int rc; |
| | rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal); |
| | assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); |
| | if( rc!=SQLITE_OK ){ |
| | assert( db->mallocFailed == 1 ); |
| | return; |
| | } |
| | if( !pVal ){ |
| | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, |
| | "Cannot add a column with non-constant default"); |
| | } |
| | sqlite3ValueFree(pVal); |
| | } |
| | }else if( pCol->colFlags & COLFLAG_STORED ){ |
| | sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column"); |
| | } |
| |
|
| |
|
| | |
| | zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n); |
| | if( zCol ){ |
| | char *zEnd = &zCol[pColDef->n-1]; |
| | while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){ |
| | *zEnd-- = '\0'; |
| | } |
| | |
| | |
| | assert( IsOrdinaryTable(pTab) ); |
| | assert( IsOrdinaryTable(pNew) ); |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = printf('%%.%ds, ',sql) || %Q" |
| | " || substr(sql,1+length(printf('%%.%ds',sql))) " |
| | "WHERE type = 'table' AND name = %Q", |
| | zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset, |
| | zTab |
| | ); |
| | sqlite3DbFree(db, zCol); |
| | } |
| |
|
| | v = sqlite3GetVdbe(pParse); |
| | if( v ){ |
| | |
| | |
| | |
| | |
| | r1 = sqlite3GetTempReg(pParse); |
| | sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); |
| | sqlite3VdbeUsesBtree(v, iDb); |
| | sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2); |
| | sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2); |
| | VdbeCoverage(v); |
| | sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); |
| | sqlite3ReleaseTempReg(pParse, r1); |
| |
|
| | |
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd); |
| |
|
| | |
| | if( pNew->pCheck!=0 |
| | || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) |
| | || (pTab->tabFlags & TF_Strict)!=0 |
| | ){ |
| | sqlite3NestedParse(pParse, |
| | "SELECT CASE WHEN quick_check GLOB 'CHECK*'" |
| | " THEN raise(ABORT,'CHECK constraint failed')" |
| | " WHEN quick_check GLOB 'non-* value in*'" |
| | " THEN raise(ABORT,'type mismatch on DEFAULT')" |
| | " ELSE raise(ABORT,'NOT NULL constraint failed')" |
| | " END" |
| | " FROM pragma_quick_check(%Q,%Q)" |
| | " WHERE quick_check GLOB 'CHECK*'" |
| | " OR quick_check GLOB 'NULL*'" |
| | " OR quick_check GLOB 'non-* value in*'", |
| | zTab, zDb |
| | ); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ |
| | Table *pNew; |
| | Table *pTab; |
| | int iDb; |
| | int i; |
| | int nAlloc; |
| | sqlite3 *db = pParse->db; |
| |
|
| | |
| | assert( pParse->pNewTable==0 ); |
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | if( NEVER(db->mallocFailed) ) goto exit_begin_add_column; |
| | pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| | if( !pTab ) goto exit_begin_add_column; |
| |
|
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | if( IsVirtual(pTab) ){ |
| | sqlite3ErrorMsg(pParse, "virtual tables may not be altered"); |
| | goto exit_begin_add_column; |
| | } |
| | #endif |
| |
|
| | |
| | if( IsView(pTab) ){ |
| | sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); |
| | goto exit_begin_add_column; |
| | } |
| | if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ |
| | goto exit_begin_add_column; |
| | } |
| |
|
| | sqlite3MayAbort(pParse); |
| | assert( IsOrdinaryTable(pTab) ); |
| | assert( pTab->u.tab.addColOffset>0 ); |
| | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table)); |
| | if( !pNew ) goto exit_begin_add_column; |
| | pParse->pNewTable = pNew; |
| | pNew->nTabRef = 1; |
| | pNew->nCol = pTab->nCol; |
| | assert( pNew->nCol>0 ); |
| | nAlloc = (((pNew->nCol-1)/8)*8)+8; |
| | assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); |
| | pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc); |
| | pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); |
| | if( !pNew->aCol || !pNew->zName ){ |
| | assert( db->mallocFailed ); |
| | goto exit_begin_add_column; |
| | } |
| | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol); |
| | for(i=0; i<pNew->nCol; i++){ |
| | Column *pCol = &pNew->aCol[i]; |
| | pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); |
| | pCol->hName = sqlite3StrIHash(pCol->zCnName); |
| | } |
| | assert( IsOrdinaryTable(pNew) ); |
| | pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0); |
| | pNew->pSchema = db->aDb[iDb].pSchema; |
| | pNew->u.tab.addColOffset = pTab->u.tab.addColOffset; |
| | assert( pNew->nTabRef==1 ); |
| |
|
| | exit_begin_add_column: |
| | sqlite3SrcListDelete(db, pSrc); |
| | return; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) |
| | static int isRealTable(Parse *pParse, Table *pTab, int iOp){ |
| | const char *zType = 0; |
| | #ifndef SQLITE_OMIT_VIEW |
| | if( IsView(pTab) ){ |
| | zType = "view"; |
| | } |
| | #endif |
| | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| | if( IsVirtual(pTab) ){ |
| | zType = "virtual table"; |
| | } |
| | #endif |
| | if( zType ){ |
| | const char *azMsg[] = { |
| | "rename columns of", "drop column from", "edit constraints of" |
| | }; |
| | assert( iOp>=0 && iOp<ArraySize(azMsg) ); |
| | sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"", |
| | azMsg[iOp], zType, pTab->zName |
| | ); |
| | return 1; |
| | } |
| | return 0; |
| | } |
| | #else |
| | # define isRealTable(x,y,z) (0) |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterRenameColumn( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | Token *pOld, |
| | Token *pNew |
| | ){ |
| | sqlite3 *db = pParse->db; |
| | Table *pTab; |
| | int iCol; |
| | char *zOld = 0; |
| | char *zNew = 0; |
| | const char *zDb; |
| | int iSchema; |
| | int bQuote; |
| |
|
| | |
| | pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| | if( !pTab ) goto exit_rename_column; |
| |
|
| | |
| | if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; |
| | if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column; |
| |
|
| | |
| | iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); |
| | assert( iSchema>=0 ); |
| | zDb = db->aDb[iSchema].zDbSName; |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, 0) ){ |
| | goto exit_rename_column; |
| | } |
| | #endif |
| |
|
| | |
| | |
| | zOld = sqlite3NameFromToken(db, pOld); |
| | if( !zOld ) goto exit_rename_column; |
| | iCol = sqlite3ColumnIndex(pTab, zOld); |
| | if( iCol<0 ){ |
| | sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld); |
| | goto exit_rename_column; |
| | } |
| |
|
| | |
| | renameTestSchema(pParse, zDb, iSchema==1, "", 0); |
| | renameFixQuotes(pParse, zDb, iSchema==1); |
| |
|
| | |
| | |
| | |
| | |
| | sqlite3MayAbort(pParse); |
| | zNew = sqlite3NameFromToken(db, pNew); |
| | if( !zNew ) goto exit_rename_column; |
| | assert( pNew->n>0 ); |
| | bQuote = sqlite3Isquote(pNew->z[0]); |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) " |
| | "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' " |
| | " AND (type != 'index' OR tbl_name = %Q)", |
| | zDb, |
| | zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1, |
| | pTab->zName |
| | ); |
| |
|
| | sqlite3NestedParse(pParse, |
| | "UPDATE temp." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) " |
| | "WHERE type IN ('trigger', 'view')", |
| | zDb, pTab->zName, iCol, zNew, bQuote |
| | ); |
| |
|
| | |
| | renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); |
| | renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); |
| |
|
| | exit_rename_column: |
| | sqlite3SrcListDelete(db, pSrc); |
| | sqlite3DbFree(db, zOld); |
| | sqlite3DbFree(db, zNew); |
| | return; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct RenameToken { |
| | const void *p; |
| | Token t; |
| | RenameToken *pNext; |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | typedef struct RenameCtx RenameCtx; |
| | struct RenameCtx { |
| | RenameToken *pList; |
| | int nList; |
| | int iCol; |
| | Table *pTab; |
| | const char *zOld; |
| | }; |
| |
|
| | #ifdef SQLITE_DEBUG |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ |
| | assert( pParse==pParse->db->pParse ); |
| | assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); |
| | if( pParse->nErr==0 ){ |
| | const RenameToken *p; |
| | u32 i = 1; |
| | for(p=pParse->pRename; p; p=p->pNext){ |
| | if( p->p ){ |
| | assert( p->p!=pPtr ); |
| | i += *(u8*)(p->p) | 1; |
| | } |
| | } |
| | assert( i>0 ); |
| | } |
| | } |
| | #else |
| | # define renameTokenCheckAll(x,y) |
| | #endif |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const void *sqlite3RenameTokenMap( |
| | Parse *pParse, |
| | const void *pPtr, |
| | const Token *pToken |
| | ){ |
| | RenameToken *pNew; |
| | assert( pPtr || pParse->db->mallocFailed ); |
| | renameTokenCheckAll(pParse, pPtr); |
| | if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){ |
| | pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); |
| | if( pNew ){ |
| | pNew->p = pPtr; |
| | pNew->t = *pToken; |
| | pNew->pNext = pParse->pRename; |
| | pParse->pRename = pNew; |
| | } |
| | } |
| |
|
| | return pPtr; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){ |
| | RenameToken *p; |
| | renameTokenCheckAll(pParse, pTo); |
| | for(p=pParse->pRename; p; p=p->pNext){ |
| | if( p->p==pFrom ){ |
| | p->p = pTo; |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){ |
| | Parse *pParse = pWalker->pParse; |
| | sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr); |
| | if( ExprUseYTab(pExpr) ){ |
| | sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab); |
| | } |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void renameWalkWith(Walker *pWalker, Select *pSelect){ |
| | With *pWith = pSelect->pWith; |
| | if( pWith ){ |
| | Parse *pParse = pWalker->pParse; |
| | int i; |
| | With *pCopy = 0; |
| | assert( pWith->nCte>0 ); |
| | if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){ |
| | |
| | |
| | |
| | |
| | |
| | pCopy = sqlite3WithDup(pParse->db, pWith); |
| | pCopy = sqlite3WithPush(pParse, pCopy, 1); |
| | } |
| | for(i=0; i<pWith->nCte; i++){ |
| | Select *p = pWith->a[i].pSelect; |
| | NameContext sNC; |
| | memset(&sNC, 0, sizeof(sNC)); |
| | sNC.pParse = pParse; |
| | if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC); |
| | if( sNC.pParse->db->mallocFailed ) return; |
| | sqlite3WalkSelect(pWalker, p); |
| | sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols); |
| | } |
| | if( pCopy && pParse->pWith==pCopy ){ |
| | pParse->pWith = pCopy->pOuter; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static void unmapColumnIdlistNames( |
| | Parse *pParse, |
| | const IdList *pIdList |
| | ){ |
| | int ii; |
| | assert( pIdList!=0 ); |
| | for(ii=0; ii<pIdList->nId; ii++){ |
| | sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static int renameUnmapSelectCb(Walker *pWalker, Select *p){ |
| | Parse *pParse = pWalker->pParse; |
| | int i; |
| | if( pParse->nErr ) return WRC_Abort; |
| | testcase( p->selFlags & SF_View ); |
| | testcase( p->selFlags & SF_CopyCte ); |
| | if( p->selFlags & (SF_View|SF_CopyCte) ){ |
| | return WRC_Prune; |
| | } |
| | if( ALWAYS(p->pEList) ){ |
| | ExprList *pList = p->pEList; |
| | for(i=0; i<pList->nExpr; i++){ |
| | if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){ |
| | sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName); |
| | } |
| | } |
| | } |
| | if( ALWAYS(p->pSrc) ){ |
| | SrcList *pSrc = p->pSrc; |
| | for(i=0; i<pSrc->nSrc; i++){ |
| | sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName); |
| | if( pSrc->a[i].fg.isUsing==0 ){ |
| | sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn); |
| | }else{ |
| | unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing); |
| | } |
| | } |
| | } |
| |
|
| | renameWalkWith(pWalker, p); |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){ |
| | u8 eMode = pParse->eParseMode; |
| | Walker sWalker; |
| | memset(&sWalker, 0, sizeof(Walker)); |
| | sWalker.pParse = pParse; |
| | sWalker.xExprCallback = renameUnmapExprCb; |
| | sWalker.xSelectCallback = renameUnmapSelectCb; |
| | pParse->eParseMode = PARSE_MODE_UNMAP; |
| | sqlite3WalkExpr(&sWalker, pExpr); |
| | pParse->eParseMode = eMode; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){ |
| | if( pEList ){ |
| | int i; |
| | Walker sWalker; |
| | memset(&sWalker, 0, sizeof(Walker)); |
| | sWalker.pParse = pParse; |
| | sWalker.xExprCallback = renameUnmapExprCb; |
| | sqlite3WalkExprList(&sWalker, pEList); |
| | for(i=0; i<pEList->nExpr; i++){ |
| | if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){ |
| | sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ |
| | RenameToken *pNext; |
| | RenameToken *p; |
| | for(p=pToken; p; p=pNext){ |
| | pNext = p->pNext; |
| | sqlite3DbFree(db, p); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static RenameToken *renameTokenFind( |
| | Parse *pParse, |
| | struct RenameCtx *pCtx, |
| | const void *pPtr |
| | ){ |
| | RenameToken **pp; |
| | if( NEVER(pPtr==0) ){ |
| | return 0; |
| | } |
| | for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ |
| | if( (*pp)->p==pPtr ){ |
| | RenameToken *pToken = *pp; |
| | if( pCtx ){ |
| | *pp = pToken->pNext; |
| | pToken->pNext = pCtx->pList; |
| | pCtx->pList = pToken; |
| | pCtx->nList++; |
| | } |
| | return pToken; |
| | } |
| | } |
| | return 0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static int renameColumnSelectCb(Walker *pWalker, Select *p){ |
| | if( p->selFlags & (SF_View|SF_CopyCte) ){ |
| | testcase( p->selFlags & SF_View ); |
| | testcase( p->selFlags & SF_CopyCte ); |
| | return WRC_Prune; |
| | } |
| | renameWalkWith(pWalker, p); |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ |
| | RenameCtx *p = pWalker->u.pRename; |
| | if( pExpr->op==TK_TRIGGER |
| | && pExpr->iColumn==p->iCol |
| | && pWalker->pParse->pTriggerTab==p->pTab |
| | ){ |
| | renameTokenFind(pWalker->pParse, p, (void*)pExpr); |
| | }else if( pExpr->op==TK_COLUMN |
| | && pExpr->iColumn==p->iCol |
| | && ALWAYS(ExprUseYTab(pExpr)) |
| | && p->pTab==pExpr->y.pTab |
| | ){ |
| | renameTokenFind(pWalker->pParse, p, (void*)pExpr); |
| | } |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ |
| | RenameToken *pBest = pCtx->pList; |
| | RenameToken *pToken; |
| | RenameToken **pp; |
| |
|
| | for(pToken=pBest->pNext; pToken; pToken=pToken->pNext){ |
| | if( pToken->t.z>pBest->t.z ) pBest = pToken; |
| | } |
| | for(pp=&pCtx->pList; *pp!=pBest; pp=&(*pp)->pNext); |
| | *pp = pBest->pNext; |
| |
|
| | return pBest; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...){ |
| | sqlite3 *db = sqlite3_context_db_handle(pCtx); |
| | char *zErr = 0; |
| | va_list ap; |
| | va_start(ap, zFmt); |
| | zErr = sqlite3VMPrintf(db, zFmt, ap); |
| | va_end(ap); |
| | if( zErr ){ |
| | sqlite3_result_error(pCtx, zErr, -1); |
| | sqlite3DbFree(db, zErr); |
| | }else{ |
| | sqlite3_result_error_nomem(pCtx); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameColumnParseError( |
| | sqlite3_context *pCtx, |
| | const char *zWhen, |
| | sqlite3_value *pType, |
| | sqlite3_value *pObject, |
| | Parse *pParse |
| | ){ |
| | const char *zT = (const char*)sqlite3_value_text(pType); |
| | const char *zN = (const char*)sqlite3_value_text(pObject); |
| | char *zErr; |
| |
|
| | zErr = sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s", |
| | zT, zN, (zWhen[0] ? " " : ""), zWhen, |
| | pParse->zErrMsg |
| | ); |
| | sqlite3_result_error(pCtx, zErr, -1); |
| | sqlite3DbFree(pParse->db, zErr); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameColumnElistNames( |
| | Parse *pParse, |
| | RenameCtx *pCtx, |
| | const ExprList *pEList, |
| | const char *zOld |
| | ){ |
| | if( pEList ){ |
| | int i; |
| | for(i=0; i<pEList->nExpr; i++){ |
| | const char *zName = pEList->a[i].zEName; |
| | if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) |
| | && ALWAYS(zName!=0) |
| | && 0==sqlite3_stricmp(zName, zOld) |
| | ){ |
| | renameTokenFind(pParse, pCtx, (const void*)zName); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void renameColumnIdlistNames( |
| | Parse *pParse, |
| | RenameCtx *pCtx, |
| | const IdList *pIdList, |
| | const char *zOld |
| | ){ |
| | if( pIdList ){ |
| | int i; |
| | for(i=0; i<pIdList->nId; i++){ |
| | const char *zName = pIdList->a[i].zName; |
| | if( 0==sqlite3_stricmp(zName, zOld) ){ |
| | renameTokenFind(pParse, pCtx, (const void*)zName); |
| | } |
| | } |
| | } |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | static int renameParseSql( |
| | Parse *p, |
| | const char *zDb, |
| | sqlite3 *db, |
| | const char *zSql, |
| | int bTemp |
| | ){ |
| | int rc; |
| | u64 flags; |
| |
|
| | sqlite3ParseObjectInit(p, db); |
| | if( zSql==0 ){ |
| | return SQLITE_NOMEM; |
| | } |
| | if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ |
| | return SQLITE_CORRUPT_BKPT; |
| | } |
| | if( bTemp ){ |
| | db->init.iDb = 1; |
| | }else{ |
| | int iDb = sqlite3FindDbName(db, zDb); |
| | assert( iDb>=0 && iDb<=0xff ); |
| | db->init.iDb = (u8)iDb; |
| | } |
| | p->eParseMode = PARSE_MODE_RENAME; |
| | p->db = db; |
| | p->nQueryLoop = 1; |
| | flags = db->flags; |
| | testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); |
| | db->flags |= SQLITE_Comments; |
| | rc = sqlite3RunParser(p, zSql); |
| | db->flags = flags; |
| | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| | if( rc==SQLITE_OK |
| | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| | ){ |
| | rc = SQLITE_CORRUPT_BKPT; |
| | } |
| |
|
| | #ifdef SQLITE_DEBUG |
| | |
| | |
| | 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, |
| | RenameCtx *pRename, |
| | const char *zSql, |
| | const char *zNew, |
| | int bQuote |
| | ){ |
| | i64 nNew = sqlite3Strlen30(zNew); |
| | i64 nSql = sqlite3Strlen30(zSql); |
| | sqlite3 *db = sqlite3_context_db_handle(pCtx); |
| | int rc = SQLITE_OK; |
| | char *zQuot = 0; |
| | char *zOut; |
| | i64 nQuot = 0; |
| | char *zBuf1 = 0; |
| | char *zBuf2 = 0; |
| |
|
| | if( zNew ){ |
| | |
| | |
| | |
| | |
| | |
| | zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew); |
| | if( zQuot==0 ){ |
| | return SQLITE_NOMEM; |
| | }else{ |
| | nQuot = sqlite3Strlen30(zQuot)-1; |
| | } |
| |
|
| | assert( nQuot>=nNew && nSql>=0 && nNew>=0 ); |
| | zOut = sqlite3DbMallocZero(db, (u64)nSql + pRename->nList*(u64)nQuot + 1); |
| | }else{ |
| | assert( nSql>0 ); |
| | zOut = (char*)sqlite3DbMallocZero(db, (2*(u64)nSql + 1) * 3); |
| | if( zOut ){ |
| | zBuf1 = &zOut[nSql*2+1]; |
| | zBuf2 = &zOut[nSql*4+2]; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | if( zOut ){ |
| | i64 nOut = nSql; |
| | assert( nSql>0 ); |
| | memcpy(zOut, zSql, (size_t)nSql); |
| | while( pRename->pList ){ |
| | int iOff; |
| | i64 nReplace; |
| | const char *zReplace; |
| | RenameToken *pBest = renameColumnTokenNext(pRename); |
| |
|
| | if( zNew ){ |
| | if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){ |
| | nReplace = nNew; |
| | zReplace = zNew; |
| | }else{ |
| | nReplace = nQuot; |
| | zReplace = zQuot; |
| | if( pBest->t.z[pBest->t.n]=='"' ) nReplace++; |
| | } |
| | }else{ |
| | |
| | |
| | |
| | |
| | |
| | |
| | memcpy(zBuf1, pBest->t.z, pBest->t.n); |
| | zBuf1[pBest->t.n] = 0; |
| | sqlite3Dequote(zBuf1); |
| | assert( nSql < 0x15555554 ); |
| | sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1, |
| | pBest->t.z[pBest->t.n]=='\'' ? " " : "" |
| | ); |
| | zReplace = zBuf2; |
| | nReplace = sqlite3Strlen30(zReplace); |
| | } |
| |
|
| | iOff = (int)(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 void renameSetENames(ExprList *pEList, int val){ |
| | assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN ); |
| | if( pEList ){ |
| | int i; |
| | for(i=0; i<pEList->nExpr; i++){ |
| | assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); |
| | pEList->a[i].fg.eEName = val&0x3; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int renameResolveTrigger(Parse *pParse){ |
| | sqlite3 *db = pParse->db; |
| | Trigger *pNew = pParse->pNewTrigger; |
| | TriggerStep *pStep; |
| | NameContext sNC; |
| | int rc = SQLITE_OK; |
| |
|
| | memset(&sNC, 0, sizeof(sNC)); |
| | sNC.pParse = pParse; |
| | assert( pNew->pTabSchema ); |
| | pParse->pTriggerTab = sqlite3FindTable(db, pNew->table, |
| | db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName |
| | ); |
| | pParse->eTriggerOp = pNew->op; |
| | |
| | |
| | if( ALWAYS(pParse->pTriggerTab) ){ |
| | rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab)!=0; |
| | } |
| |
|
| | |
| | if( rc==SQLITE_OK && pNew->pWhen ){ |
| | rc = sqlite3ResolveExprNames(&sNC, pNew->pWhen); |
| | } |
| |
|
| | for(pStep=pNew->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->pSrc ){ |
| | SrcList *pSrc = sqlite3SrcListDup(db, pStep->pSrc, 0); |
| | if( pSrc ){ |
| | Select *pSel = sqlite3SelectNew( |
| | pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 |
| | ); |
| | if( pSel==0 ){ |
| | pStep->pExprList = 0; |
| | pSrc = 0; |
| | rc = SQLITE_NOMEM; |
| | }else{ |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | renameSetENames(pStep->pExprList, ENAME_SPAN); |
| | sqlite3SelectPrep(pParse, pSel, 0); |
| | renameSetENames(pStep->pExprList, ENAME_NAME); |
| | rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; |
| | assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); |
| | assert( pSrc==pSel->pSrc ); |
| | if( pStep->pExprList ) pSel->pEList = 0; |
| | pSel->pSrc = 0; |
| | sqlite3SelectDelete(db, pSel); |
| | } |
| | if( ALWAYS(pStep->pSrc) ){ |
| | int i; |
| | for(i=0; i<pStep->pSrc->nSrc && rc==SQLITE_OK; i++){ |
| | SrcItem *p = &pStep->pSrc->a[i]; |
| | if( p->fg.isSubquery ){ |
| | assert( p->u4.pSubq!=0 ); |
| | sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); |
| | } |
| | } |
| | } |
| |
|
| | if( db->mallocFailed ){ |
| | rc = SQLITE_NOMEM; |
| | } |
| | sNC.pSrcList = pSrc; |
| | if( rc==SQLITE_OK && 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 && rc==SQLITE_OK ){ |
| | Upsert *pUpsert = pStep->pUpsert; |
| | pUpsert->pUpsertSrc = pSrc; |
| | 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; |
| | } |
| | sNC.pSrcList = 0; |
| | sqlite3SrcListDelete(db, pSrc); |
| | }else{ |
| | rc = SQLITE_NOMEM; |
| | } |
| | } |
| | } |
| | return rc; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ |
| | TriggerStep *pStep; |
| |
|
| | |
| | sqlite3WalkExpr(pWalker, pTrigger->pWhen); |
| |
|
| | |
| | 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); |
| | } |
| | if( pStep->pSrc ){ |
| | int i; |
| | SrcList *pSrc = pStep->pSrc; |
| | for(i=0; i<pSrc->nSrc; i++){ |
| | if( pSrc->a[i].fg.isSubquery ){ |
| | assert( pSrc->a[i].u4.pSubq!=0 ); |
| | sqlite3WalkSelect(pWalker, pSrc->a[i].u4.pSubq->pSelect); |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static void renameParseCleanup(Parse *pParse){ |
| | sqlite3 *db = pParse->db; |
| | Index *pIdx; |
| | if( pParse->pVdbe ){ |
| | sqlite3VdbeFinalize(pParse->pVdbe); |
| | } |
| | sqlite3DeleteTable(db, pParse->pNewTable); |
| | while( (pIdx = pParse->pNewIndex)!=0 ){ |
| | pParse->pNewIndex = pIdx->pNext; |
| | sqlite3FreeIndex(db, pIdx); |
| | } |
| | sqlite3DeleteTrigger(db, pParse->pNewTrigger); |
| | sqlite3DbFree(db, pParse->zErrMsg); |
| | renameTokenFree(db, pParse->pRename); |
| | sqlite3ParseObjectReset(pParse); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameColumnFunc( |
| | sqlite3_context *context, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | RenameCtx sCtx; |
| | const char *zSql = (const char*)sqlite3_value_text(argv[0]); |
| | const char *zDb = (const char*)sqlite3_value_text(argv[3]); |
| | const char *zTable = (const char*)sqlite3_value_text(argv[4]); |
| | int iCol = sqlite3_value_int(argv[5]); |
| | const char *zNew = (const char*)sqlite3_value_text(argv[6]); |
| | int bQuote = sqlite3_value_int(argv[7]); |
| | int bTemp = sqlite3_value_int(argv[8]); |
| | const char *zOld; |
| | int rc; |
| | Parse sParse; |
| | Walker sWalker; |
| | Index *pIdx; |
| | int i; |
| | Table *pTab; |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | sqlite3_xauth xAuth = db->xAuth; |
| | #endif |
| |
|
| | UNUSED_PARAMETER(NotUsed); |
| | if( zSql==0 ) return; |
| | if( zTable==0 ) return; |
| | if( zNew==0 ) return; |
| | if( iCol<0 ) return; |
| | sqlite3BtreeEnterAll(db); |
| | pTab = sqlite3FindTable(db, zTable, zDb); |
| | if( pTab==0 || iCol>=pTab->nCol ){ |
| | sqlite3BtreeLeaveAll(db); |
| | return; |
| | } |
| | zOld = pTab->aCol[iCol].zCnName; |
| | memset(&sCtx, 0, sizeof(sCtx)); |
| | sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = 0; |
| | #endif |
| | rc = renameParseSql(&sParse, zDb, db, zSql, bTemp); |
| |
|
| | |
| | memset(&sWalker, 0, sizeof(Walker)); |
| | sWalker.pParse = &sParse; |
| | sWalker.xExprCallback = renameColumnExprCb; |
| | sWalker.xSelectCallback = renameColumnSelectCb; |
| | sWalker.u.pRename = &sCtx; |
| |
|
| | sCtx.pTab = pTab; |
| | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| | if( sParse.pNewTable ){ |
| | if( IsView(sParse.pNewTable) ){ |
| | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| | pSelect->selFlags &= ~(u32)SF_View; |
| | sParse.rc = SQLITE_OK; |
| | sqlite3SelectPrep(&sParse, pSelect, 0); |
| | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| | if( rc==SQLITE_OK ){ |
| | sqlite3WalkSelect(&sWalker, pSelect); |
| | } |
| | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| | }else if( IsOrdinaryTable(sParse.pNewTable) ){ |
| | |
| | int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); |
| | FKey *pFKey; |
| | sCtx.pTab = sParse.pNewTable; |
| | if( bFKOnly==0 ){ |
| | if( iCol<sParse.pNewTable->nCol ){ |
| | renameTokenFind( |
| | &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName |
| | ); |
| | } |
| | if( sCtx.iCol<0 ){ |
| | renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); |
| | } |
| | sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); |
| | for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ |
| | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| | } |
| | for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ |
| | sqlite3WalkExprList(&sWalker, pIdx->aColExpr); |
| | } |
| | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| | for(i=0; i<sParse.pNewTable->nCol; i++){ |
| | Expr *pExpr = sqlite3ColumnExpr(sParse.pNewTable, |
| | &sParse.pNewTable->aCol[i]); |
| | sqlite3WalkExpr(&sWalker, pExpr); |
| | } |
| | #endif |
| | } |
| |
|
| | assert( IsOrdinaryTable(sParse.pNewTable) ); |
| | for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| | for(i=0; i<pFKey->nCol; i++){ |
| | if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){ |
| | renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); |
| | } |
| | if( 0==sqlite3_stricmp(pFKey->zTo, zTable) |
| | && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld) |
| | ){ |
| | renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol); |
| | } |
| | } |
| | } |
| | } |
| | }else if( sParse.pNewIndex ){ |
| | sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); |
| | sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); |
| | }else{ |
| | |
| | TriggerStep *pStep; |
| | rc = renameResolveTrigger(&sParse); |
| | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| |
|
| | for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ |
| | if( pStep->pSrc ){ |
| | Table *pTarget = sqlite3LocateTableItem(&sParse, 0, &pStep->pSrc->a[0]); |
| | if( pTarget==pTab ){ |
| | if( pStep->pUpsert ){ |
| | ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet; |
| | renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld); |
| | } |
| | renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld); |
| | renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld); |
| | } |
| | } |
| | } |
| |
|
| | |
| | if( sParse.pTriggerTab==pTab ){ |
| | renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld); |
| | } |
| |
|
| | |
| | renameWalkTrigger(&sWalker, sParse.pNewTrigger); |
| | } |
| |
|
| | assert( rc==SQLITE_OK ); |
| | rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote); |
| |
|
| | renameColumnFunc_done: |
| | if( rc!=SQLITE_OK ){ |
| | if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ |
| | sqlite3_result_value(context, argv[0]); |
| | }else if( sParse.zErrMsg ){ |
| | renameColumnParseError(context, "", argv[1], argv[2], &sParse); |
| | }else{ |
| | sqlite3_result_error_code(context, rc); |
| | } |
| | } |
| |
|
| | renameParseCleanup(&sParse); |
| | renameTokenFree(db, sCtx.pList); |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = xAuth; |
| | #endif |
| | sqlite3BtreeLeaveAll(db); |
| | } |
| |
|
| | |
| | |
| | |
| | static int renameTableExprCb(Walker *pWalker, Expr *pExpr){ |
| | RenameCtx *p = pWalker->u.pRename; |
| | if( pExpr->op==TK_COLUMN |
| | && ALWAYS(ExprUseYTab(pExpr)) |
| | && p->pTab==pExpr->y.pTab |
| | ){ |
| | renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab); |
| | } |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ |
| | int i; |
| | RenameCtx *p = pWalker->u.pRename; |
| | SrcList *pSrc = pSelect->pSrc; |
| | if( pSelect->selFlags & (SF_View|SF_CopyCte) ){ |
| | testcase( pSelect->selFlags & SF_View ); |
| | testcase( pSelect->selFlags & SF_CopyCte ); |
| | return WRC_Prune; |
| | } |
| | if( NEVER(pSrc==0) ){ |
| | assert( pWalker->pParse->db->mallocFailed ); |
| | return WRC_Abort; |
| | } |
| | for(i=0; i<pSrc->nSrc; i++){ |
| | SrcItem *pItem = &pSrc->a[i]; |
| | if( pItem->pSTab==p->pTab ){ |
| | renameTokenFind(pWalker->pParse, p, pItem->zName); |
| | } |
| | } |
| | renameWalkWith(pWalker, pSelect); |
| |
|
| | return WRC_Continue; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameTableFunc( |
| | sqlite3_context *context, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | const char *zDb = (const char*)sqlite3_value_text(argv[0]); |
| | const char *zInput = (const char*)sqlite3_value_text(argv[3]); |
| | const char *zOld = (const char*)sqlite3_value_text(argv[4]); |
| | const char *zNew = (const char*)sqlite3_value_text(argv[5]); |
| | int bTemp = sqlite3_value_int(argv[6]); |
| | UNUSED_PARAMETER(NotUsed); |
| |
|
| | if( zInput && zOld && zNew ){ |
| | Parse sParse; |
| | int rc; |
| | int bQuote = 1; |
| | RenameCtx sCtx; |
| | Walker sWalker; |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | sqlite3_xauth xAuth = db->xAuth; |
| | db->xAuth = 0; |
| | #endif |
| |
|
| | sqlite3BtreeEnterAll(db); |
| |
|
| | 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; |
| |
|
| | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| |
|
| | if( rc==SQLITE_OK ){ |
| | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| | if( sParse.pNewTable ){ |
| | Table *pTab = sParse.pNewTable; |
| |
|
| | if( IsView(pTab) ){ |
| | if( isLegacy==0 ){ |
| | Select *pSelect = pTab->u.view.pSelect; |
| | NameContext sNC; |
| | memset(&sNC, 0, sizeof(sNC)); |
| | sNC.pParse = &sParse; |
| |
|
| | assert( pSelect->selFlags & SF_View ); |
| | pSelect->selFlags &= ~(u32)SF_View; |
| | sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); |
| | if( sParse.nErr ){ |
| | rc = sParse.rc; |
| | }else{ |
| | sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); |
| | } |
| | } |
| | }else{ |
| | |
| | #ifndef SQLITE_OMIT_FOREIGN_KEY |
| | if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys)) |
| | && !IsVirtual(pTab) |
| | ){ |
| | FKey *pFKey; |
| | assert( IsOrdinaryTable(pTab) ); |
| | for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){ |
| | if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){ |
| | renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo); |
| | } |
| | } |
| | } |
| | #endif |
| |
|
| | |
| | |
| | |
| | if( sqlite3_stricmp(zOld, pTab->zName)==0 ){ |
| | sCtx.pTab = pTab; |
| | if( isLegacy==0 ){ |
| | sqlite3WalkExprList(&sWalker, pTab->pCheck); |
| | } |
| | renameTokenFind(&sParse, &sCtx, pTab->zName); |
| | } |
| | } |
| | } |
| |
|
| | else if( sParse.pNewIndex ){ |
| | renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName); |
| | if( isLegacy==0 ){ |
| | sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); |
| | } |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_TRIGGER |
| | else{ |
| | 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); |
| | } |
| |
|
| | if( isLegacy==0 ){ |
| | rc = renameResolveTrigger(&sParse); |
| | if( rc==SQLITE_OK ){ |
| | renameWalkTrigger(&sWalker, pTrigger); |
| | for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ |
| | if( pStep->pSrc ){ |
| | int i; |
| | for(i=0; i<pStep->pSrc->nSrc; i++){ |
| | SrcItem *pItem = &pStep->pSrc->a[i]; |
| | if( 0==sqlite3_stricmp(pItem->zName, zOld) ){ |
| | renameTokenFind(&sParse, &sCtx, pItem->zName); |
| | } |
| | } |
| | } |
| | } |
| | } |
| | } |
| | } |
| | #endif |
| | } |
| |
|
| | if( rc==SQLITE_OK ){ |
| | rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote); |
| | } |
| | if( rc!=SQLITE_OK ){ |
| | if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){ |
| | sqlite3_result_value(context, argv[3]); |
| | }else if( sParse.zErrMsg ){ |
| | renameColumnParseError(context, "", argv[1], argv[2], &sParse); |
| | }else{ |
| | sqlite3_result_error_code(context, rc); |
| | } |
| | } |
| |
|
| | renameParseCleanup(&sParse); |
| | renameTokenFree(db, sCtx.pList); |
| | sqlite3BtreeLeaveAll(db); |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = xAuth; |
| | #endif |
| | } |
| |
|
| | return; |
| | } |
| |
|
| | static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){ |
| | if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){ |
| | renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr); |
| | } |
| | return WRC_Continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameQuotefixFunc( |
| | sqlite3_context *context, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | char const *zDb = (const char*)sqlite3_value_text(argv[0]); |
| | char const *zInput = (const char*)sqlite3_value_text(argv[1]); |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | sqlite3_xauth xAuth = db->xAuth; |
| | db->xAuth = 0; |
| | #endif |
| |
|
| | sqlite3BtreeEnterAll(db); |
| |
|
| | UNUSED_PARAMETER(NotUsed); |
| | if( zDb && zInput ){ |
| | int rc; |
| | Parse sParse; |
| | rc = renameParseSql(&sParse, zDb, db, zInput, 0); |
| |
|
| | if( rc==SQLITE_OK ){ |
| | RenameCtx sCtx; |
| | Walker sWalker; |
| |
|
| | |
| | memset(&sCtx, 0, sizeof(RenameCtx)); |
| | memset(&sWalker, 0, sizeof(Walker)); |
| | sWalker.pParse = &sParse; |
| | sWalker.xExprCallback = renameQuotefixExprCb; |
| | sWalker.xSelectCallback = renameColumnSelectCb; |
| | sWalker.u.pRename = &sCtx; |
| |
|
| | if( sParse.pNewTable ){ |
| | if( IsView(sParse.pNewTable) ){ |
| | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| | pSelect->selFlags &= ~(u32)SF_View; |
| | sParse.rc = SQLITE_OK; |
| | sqlite3SelectPrep(&sParse, pSelect, 0); |
| | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| | if( rc==SQLITE_OK ){ |
| | sqlite3WalkSelect(&sWalker, pSelect); |
| | } |
| | }else{ |
| | int i; |
| | sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); |
| | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| | for(i=0; i<sParse.pNewTable->nCol; i++){ |
| | sqlite3WalkExpr(&sWalker, |
| | sqlite3ColumnExpr(sParse.pNewTable, |
| | &sParse.pNewTable->aCol[i])); |
| | } |
| | #endif |
| | } |
| | }else if( sParse.pNewIndex ){ |
| | sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); |
| | sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); |
| | }else{ |
| | #ifndef SQLITE_OMIT_TRIGGER |
| | rc = renameResolveTrigger(&sParse); |
| | if( rc==SQLITE_OK ){ |
| | renameWalkTrigger(&sWalker, sParse.pNewTrigger); |
| | } |
| | #endif |
| | } |
| |
|
| | if( rc==SQLITE_OK ){ |
| | rc = renameEditSql(context, &sCtx, zInput, 0, 0); |
| | } |
| | renameTokenFree(db, sCtx.pList); |
| | } |
| | if( rc!=SQLITE_OK ){ |
| | if( sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){ |
| | sqlite3_result_value(context, argv[1]); |
| | }else{ |
| | sqlite3_result_error_code(context, rc); |
| | } |
| | } |
| | renameParseCleanup(&sParse); |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = xAuth; |
| | #endif |
| |
|
| | sqlite3BtreeLeaveAll(db); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void renameTableTest( |
| | sqlite3_context *context, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | char const *zDb = (const char*)sqlite3_value_text(argv[0]); |
| | char const *zInput = (const char*)sqlite3_value_text(argv[1]); |
| | int bTemp = sqlite3_value_int(argv[4]); |
| | int isLegacy = (db->flags & SQLITE_LegacyAlter); |
| | char const *zWhen = (const char*)sqlite3_value_text(argv[5]); |
| | int bNoDQS = sqlite3_value_int(argv[6]); |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | sqlite3_xauth xAuth = db->xAuth; |
| | db->xAuth = 0; |
| | #endif |
| |
|
| | UNUSED_PARAMETER(NotUsed); |
| |
|
| | if( zDb && zInput ){ |
| | int rc; |
| | Parse sParse; |
| | u64 flags = db->flags; |
| | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| | db->flags = flags; |
| | if( rc==SQLITE_OK ){ |
| | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| | NameContext sNC; |
| | memset(&sNC, 0, sizeof(sNC)); |
| | sNC.pParse = &sParse; |
| | sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC); |
| | if( sParse.nErr ) rc = sParse.rc; |
| | } |
| |
|
| | else if( sParse.pNewTrigger ){ |
| | if( isLegacy==0 ){ |
| | rc = renameResolveTrigger(&sParse); |
| | } |
| | if( rc==SQLITE_OK ){ |
| | int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema); |
| | int i2 = sqlite3FindDbName(db, zDb); |
| | if( i1==i2 ){ |
| | |
| | sqlite3_result_int(context, 1); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | if( rc!=SQLITE_OK && zWhen && !sqlite3WritableSchema(db) ){ |
| | |
| | renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse); |
| | } |
| | renameParseCleanup(&sParse); |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = xAuth; |
| | #endif |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int getConstraintToken(const u8 *z, int *piToken){ |
| | int iOff = 0; |
| | int t = 0; |
| | do { |
| | iOff += sqlite3GetToken(&z[iOff], &t); |
| | }while( t==TK_SPACE || t==TK_COMMENT ); |
| |
|
| | *piToken = t; |
| |
|
| | if( t==TK_LP ){ |
| | int nNest = 1; |
| | while( nNest>0 ){ |
| | iOff += sqlite3GetToken(&z[iOff], &t); |
| | if( t==TK_LP ){ |
| | nNest++; |
| | }else if( t==TK_RP ){ |
| | t = TK_LP; |
| | nNest--; |
| | }else if( t==TK_ILLEGAL ){ |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | *piToken = t; |
| | return iOff; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void dropColumnFunc( |
| | sqlite3_context *context, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | sqlite3 *db = sqlite3_context_db_handle(context); |
| | int iSchema = sqlite3_value_int(argv[0]); |
| | const char *zSql = (const char*)sqlite3_value_text(argv[1]); |
| | int iCol = sqlite3_value_int(argv[2]); |
| | const char *zDb = db->aDb[iSchema].zDbSName; |
| | int rc; |
| | Parse sParse; |
| | RenameToken *pCol; |
| | Table *pTab; |
| | const char *zEnd; |
| | char *zNew = 0; |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | sqlite3_xauth xAuth = db->xAuth; |
| | db->xAuth = 0; |
| | #endif |
| |
|
| | UNUSED_PARAMETER(NotUsed); |
| | rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1); |
| | if( rc!=SQLITE_OK ) goto drop_column_done; |
| | pTab = sParse.pNewTable; |
| | if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){ |
| | |
| | rc = SQLITE_CORRUPT_BKPT; |
| | goto drop_column_done; |
| | } |
| |
|
| | if( iCol<pTab->nCol-1 ){ |
| | RenameToken *pEnd; |
| | pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); |
| | pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); |
| | zEnd = (const char*)pEnd->t.z; |
| | }else{ |
| | int eTok; |
| | assert( IsOrdinaryTable(pTab) ); |
| | assert( iCol!=0 ); |
| | |
| | |
| | |
| | pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol-1].zCnName); |
| | do { |
| | pCol->t.z += getConstraintToken((const u8*)pCol->t.z, &eTok); |
| | }while( eTok!=TK_COMMA ); |
| | pCol->t.z--; |
| | zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; |
| | } |
| |
|
| | zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); |
| | sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT); |
| | sqlite3_free(zNew); |
| |
|
| | drop_column_done: |
| | renameParseCleanup(&sParse); |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | db->xAuth = xAuth; |
| | #endif |
| | if( rc!=SQLITE_OK ){ |
| | sqlite3_result_error_code(context, rc); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ |
| | sqlite3 *db = pParse->db; |
| | Table *pTab; |
| | int iDb; |
| | const char *zDb; |
| | char *zCol = 0; |
| | int iCol; |
| |
|
| | |
| | assert( pParse->pNewTable==0 ); |
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | if( NEVER(db->mallocFailed) ) goto exit_drop_column; |
| | pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| | if( !pTab ) goto exit_drop_column; |
| |
|
| | |
| | |
| | if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column; |
| | if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column; |
| |
|
| | |
| | zCol = sqlite3NameFromToken(db, pName); |
| | if( zCol==0 ){ |
| | assert( db->mallocFailed ); |
| | goto exit_drop_column; |
| | } |
| | iCol = sqlite3ColumnIndex(pTab, zCol); |
| | if( iCol<0 ){ |
| | sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName); |
| | goto exit_drop_column; |
| | } |
| |
|
| | |
| | |
| | if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){ |
| | sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"", |
| | (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE", |
| | zCol |
| | ); |
| | goto exit_drop_column; |
| | } |
| |
|
| | |
| | if( pTab->nCol<=1 ){ |
| | sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol); |
| | goto exit_drop_column; |
| | } |
| |
|
| | |
| | iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| | assert( iDb>=0 ); |
| | zDb = db->aDb[iDb].zDbSName; |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ |
| | goto exit_drop_column; |
| | } |
| | #endif |
| | renameTestSchema(pParse, zDb, iDb==1, "", 0); |
| | renameFixQuotes(pParse, zDb, iDb==1); |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_drop_column(%d, sql, %d) " |
| | "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)" |
| | , zDb, iDb, iCol, pTab->zName |
| | ); |
| |
|
| | |
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop); |
| | renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1); |
| |
|
| | |
| | if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){ |
| | int i; |
| | int addr; |
| | int reg; |
| | int regRec; |
| | Index *pPk = 0; |
| | int nField = 0; |
| | int iCur; |
| | Vdbe *v = sqlite3GetVdbe(pParse); |
| | iCur = pParse->nTab++; |
| | sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); |
| | addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v); |
| | reg = ++pParse->nMem; |
| | if( HasRowid(pTab) ){ |
| | sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg); |
| | pParse->nMem += pTab->nCol; |
| | }else{ |
| | pPk = sqlite3PrimaryKeyIndex(pTab); |
| | pParse->nMem += pPk->nColumn; |
| | for(i=0; i<pPk->nKeyCol; i++){ |
| | sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1); |
| | } |
| | nField = pPk->nKeyCol; |
| | } |
| | regRec = ++pParse->nMem; |
| | for(i=0; i<pTab->nCol; i++){ |
| | if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ |
| | int regOut; |
| | if( pPk ){ |
| | int iPos = sqlite3TableColumnToIndex(pPk, i); |
| | int iColPos = sqlite3TableColumnToIndex(pPk, iCol); |
| | if( iPos<pPk->nKeyCol ) continue; |
| | regOut = reg+1+iPos-(iPos>iColPos); |
| | }else{ |
| | regOut = reg+1+nField; |
| | } |
| | if( i==pTab->iPKey ){ |
| | sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); |
| | }else{ |
| | char aff = pTab->aCol[i].affinity; |
| | if( aff==SQLITE_AFF_REAL ){ |
| | pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; |
| | } |
| | sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); |
| | pTab->aCol[i].affinity = aff; |
| | } |
| | nField++; |
| | } |
| | } |
| | if( nField==0 ){ |
| | |
| | pParse->nMem++; |
| | sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1); |
| | nField = 1; |
| | } |
| | sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec); |
| | if( pPk ){ |
| | sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol); |
| | }else{ |
| | sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg); |
| | } |
| | sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); |
| |
|
| | sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); |
| | sqlite3VdbeJumpHere(v, addr); |
| | } |
| |
|
| | exit_drop_column: |
| | sqlite3DbFree(db, zCol); |
| | sqlite3SrcListDelete(db, pSrc); |
| | } |
| |
|
| | |
| | |
| | |
| | static int getWhitespace(const u8 *z){ |
| | int nRet = 0; |
| | while( 1 ){ |
| | int t = 0; |
| | int n = sqlite3GetToken(&z[nRet], &t); |
| | if( t!=TK_SPACE && t!=TK_COMMENT ) break; |
| | nRet += n; |
| | } |
| | return nRet; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int getConstraint(const u8 *z){ |
| | int iOff = 0; |
| | int t = 0; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | while( 1 ){ |
| | int n = getConstraintToken(&z[iOff], &t); |
| | if( t==TK_CONSTRAINT || t==TK_PRIMARY || t==TK_NOT || t==TK_UNIQUE |
| | || t==TK_CHECK || t==TK_DEFAULT || t==TK_COLLATE || t==TK_REFERENCES |
| | || t==TK_FOREIGN || t==TK_RP || t==TK_COMMA || t==TK_ILLEGAL |
| | || t==TK_AS || t==TK_GENERATED |
| | ){ |
| | break; |
| | } |
| | iOff += n; |
| | } |
| | |
| | return iOff; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int quotedCompare( |
| | sqlite3_context *ctx, |
| | int t, |
| | const u8 *zQuote, |
| | int nQuote, |
| | const u8 *zCmp, |
| | int *pRes |
| | ){ |
| | char *zCopy = 0; |
| |
|
| | if( t==TK_ILLEGAL ){ |
| | *pRes = 1; |
| | return SQLITE_OK; |
| | } |
| | zCopy = sqlite3MallocZero(nQuote+1); |
| | if( zCopy==0 ){ |
| | sqlite3_result_error_nomem(ctx); |
| | return SQLITE_NOMEM_BKPT; |
| | } |
| | memcpy(zCopy, zQuote, nQuote); |
| | sqlite3Dequote(zCopy); |
| | *pRes = sqlite3_stricmp((const char*)zCopy, (const char*)zCmp); |
| | sqlite3_free(zCopy); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff){ |
| | int iOff = 0; |
| |
|
| | if( zSql==0 ) return SQLITE_ERROR; |
| |
|
| | |
| | while( 1 ){ |
| | int t = 0; |
| | iOff += sqlite3GetToken(&zSql[iOff], &t); |
| | if( t==TK_LP ) break; |
| | if( t==TK_ILLEGAL ){ |
| | sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT); |
| | return SQLITE_ERROR; |
| | } |
| | } |
| |
|
| | *piOff = iOff; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void dropConstraintFunc( |
| | sqlite3_context *ctx, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | const u8 *zSql = sqlite3_value_text(argv[0]); |
| | const u8 *zCons = 0; |
| | int iNotNull = -1; |
| | int ii; |
| | int iOff = 0; |
| | int iStart = 0; |
| | int iEnd = 0; |
| | char *zNew = 0; |
| | int t = 0; |
| | sqlite3 *db; |
| | UNUSED_PARAMETER(NotUsed); |
| |
|
| | if( zSql==0 ) return; |
| |
|
| | |
| | if( skipCreateTable(ctx, zSql, &iOff) ) return; |
| |
|
| | if( sqlite3_value_type(argv[1])==SQLITE_INTEGER ){ |
| | iNotNull = sqlite3_value_int(argv[1]); |
| | }else{ |
| | zCons = sqlite3_value_text(argv[1]); |
| | } |
| |
|
| | |
| | for(ii=0; iEnd==0; ii++){ |
| | |
| | |
| | |
| | |
| | while( 1 ){ |
| | iStart = iOff; |
| | iOff += getConstraintToken(&zSql[iOff], &t); |
| | if( t==TK_CONSTRAINT && (zCons || iNotNull==ii) ){ |
| | |
| | int nTok = 0; |
| | int cmp = 1; |
| |
|
| | |
| | iOff += getWhitespace(&zSql[iOff]); |
| |
|
| | |
| | |
| | nTok = getConstraintToken(&zSql[iOff], &t); |
| | if( zCons ){ |
| | if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return; |
| | } |
| | iOff += nTok; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | nTok = getConstraintToken(&zSql[iOff], &t); |
| | if( t==TK_CONSTRAINT || t==TK_DEFAULT || t==TK_COLLATE |
| | || t==TK_COMMA || t==TK_RP || t==TK_GENERATED || t==TK_AS |
| | ){ |
| | t = TK_CHECK; |
| | }else{ |
| | iOff += nTok; |
| | iOff += getConstraint(&zSql[iOff]); |
| | } |
| |
|
| | if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){ |
| | if( t!=TK_NOT && t!=TK_CHECK ){ |
| | errorMPrintf(ctx, "constraint may not be dropped: %s", zCons); |
| | return; |
| | } |
| | iEnd = iOff; |
| | break; |
| | } |
| |
|
| | }else if( t==TK_NOT && iNotNull==ii ){ |
| | iEnd = iOff + getConstraint(&zSql[iOff]); |
| | break; |
| | }else if( t==TK_RP || t==TK_ILLEGAL ){ |
| | iEnd = -1; |
| | break; |
| | }else if( t==TK_COMMA ){ |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | |
| | if( iEnd<=0 ){ |
| | if( zCons ){ |
| | errorMPrintf(ctx, "no such constraint: %s", zCons); |
| | }else{ |
| | |
| | |
| | sqlite3_result_text(ctx, (const char*)zSql, -1, SQLITE_TRANSIENT); |
| | } |
| | }else{ |
| |
|
| | |
| | |
| | |
| | const char *zSpace = " "; |
| | iEnd += getWhitespace(&zSql[iEnd]); |
| | sqlite3GetToken(&zSql[iEnd], &t); |
| | if( t==TK_RP || t==TK_COMMA ){ |
| | zSpace = ""; |
| | if( zSql[iStart-1]==',' ) iStart--; |
| | } |
| |
|
| | db = sqlite3_context_db_handle(ctx); |
| | zNew = sqlite3MPrintf(db, "%.*s%s%s", iStart, zSql, zSpace, &zSql[iEnd]); |
| | sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void addConstraintFunc( |
| | sqlite3_context *ctx, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | const u8 *zSql = sqlite3_value_text(argv[0]); |
| | const char *zCons = (const char*)sqlite3_value_text(argv[1]); |
| | int iCol = sqlite3_value_int(argv[2]); |
| | int iOff = 0; |
| | int ii; |
| | char *zNew = 0; |
| | int t = 0; |
| | sqlite3 *db; |
| | UNUSED_PARAMETER(NotUsed); |
| |
|
| | if( skipCreateTable(ctx, zSql, &iOff) ) return; |
| | |
| | for(ii=0; ii<=iCol || (iCol<0 && t!=TK_RP); ii++){ |
| | iOff += getConstraintToken(&zSql[iOff], &t); |
| | while( 1 ){ |
| | int nTok = getConstraintToken(&zSql[iOff], &t); |
| | if( t==TK_COMMA || t==TK_RP ) break; |
| | if( t==TK_ILLEGAL ){ |
| | sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT); |
| | return; |
| | } |
| | iOff += nTok; |
| | } |
| | } |
| |
|
| | iOff += getWhitespace(&zSql[iOff]); |
| |
|
| | db = sqlite3_context_db_handle(ctx); |
| | if( iCol<0 ){ |
| | zNew = sqlite3MPrintf(db, "%.*s, %s%s", iOff, zSql, zCons, &zSql[iOff]); |
| | }else{ |
| | zNew = sqlite3MPrintf(db, "%.*s %s%s", iOff, zSql, zCons, &zSql[iOff]); |
| | } |
| | sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int alterFindCol(Parse *pParse, Table *pTab, Token *pCol, int *piCol){ |
| | sqlite3 *db = pParse->db; |
| | char *zName = sqlite3NameFromToken(db, pCol); |
| | int rc = SQLITE_NOMEM; |
| | int iCol = -1; |
| |
|
| | if( zName ){ |
| | iCol = sqlite3ColumnIndex(pTab, zName); |
| | if( iCol<0 ){ |
| | sqlite3ErrorMsg(pParse, "no such column: %s", zName); |
| | rc = SQLITE_ERROR; |
| | }else{ |
| | rc = SQLITE_OK; |
| | } |
| | } |
| |
|
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | if( rc==SQLITE_OK ){ |
| | const char *zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; |
| | const char *zCol = pTab->aCol[iCol].zCnName; |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ |
| | pTab = 0; |
| | } |
| | } |
| | #endif |
| |
|
| | sqlite3DbFree(db, zName); |
| | *piCol = iCol; |
| | return rc; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static Table *alterFindTable( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | int *piDb, |
| | const char **pzDb, |
| | int bAuth |
| | ){ |
| | sqlite3 *db = pParse->db; |
| | Table *pTab = 0; |
| | assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| | pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); |
| | if( pTab ){ |
| | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| | *pzDb = db->aDb[iDb].zDbSName; |
| | *piDb = iDb; |
| |
|
| | if( SQLITE_OK!=isRealTable(pParse, pTab, 2) |
| | || SQLITE_OK!=isAlterableTable(pParse, pTab) |
| | ){ |
| | pTab = 0; |
| | } |
| | } |
| | #ifndef SQLITE_OMIT_AUTHORIZATION |
| | if( pTab && bAuth ){ |
| | if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, *pzDb, pTab->zName, 0) ){ |
| | pTab = 0; |
| | } |
| | } |
| | #endif |
| | sqlite3SrcListDelete(db, pSrc); |
| | return pTab; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterDropConstraint( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | Token *pCons, |
| | Token *pCol |
| | ){ |
| | sqlite3 *db = pParse->db; |
| | Table *pTab = 0; |
| | int iDb = 0; |
| | const char *zDb = 0; |
| | char *zArg = 0; |
| |
|
| | assert( (pCol==0)!=(pCons==0) ); |
| | assert( pSrc->nSrc==1 ); |
| | pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, pCons!=0); |
| | if( !pTab ) return; |
| |
|
| | if( pCons ){ |
| | zArg = sqlite3MPrintf(db, "%.*Q", pCons->n, pCons->z); |
| | }else{ |
| | int iCol; |
| | if( alterFindCol(pParse, pTab, pCol, &iCol) ) return; |
| | zArg = sqlite3MPrintf(db, "%d", iCol); |
| | } |
| |
|
| | |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_drop_constraint(sql, %s) " |
| | "WHERE type='table' AND tbl_name=%Q COLLATE nocase" |
| | , zDb, zArg, pTab->zName |
| | ); |
| | sqlite3DbFree(db, zArg); |
| |
|
| | |
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static void failConstraintFunc( |
| | sqlite3_context *ctx, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | const char *zText = (const char*)sqlite3_value_text(argv[0]); |
| | int err = sqlite3_value_int(argv[1]); |
| | (void)NotUsed; |
| | sqlite3_result_error(ctx, zText, -1); |
| | sqlite3_result_error_code(ctx, err); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int alterRtrimConstraint( |
| | sqlite3 *db, |
| | const char *pCons, |
| | int nCons |
| | ){ |
| | u8 *zTmp = (u8*)sqlite3MPrintf(db, "%.*s", nCons, pCons); |
| | int iOff = 0; |
| | int iEnd = 0; |
| |
|
| | if( zTmp==0 ) return 0; |
| |
|
| | while( 1 ){ |
| | int t = 0; |
| | int nToken = sqlite3GetToken(&zTmp[iOff], &t); |
| | if( t==TK_ILLEGAL ) break; |
| | if( t!=TK_SPACE && (t!=TK_COMMENT || zTmp[iOff]!='-') ){ |
| | iEnd = iOff+nToken; |
| | } |
| | iOff += nToken; |
| | } |
| |
|
| | sqlite3DbFree(db, zTmp); |
| | return iEnd; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterSetNotNull( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | Token *pCol, |
| | Token *pFirst |
| | ){ |
| | Table *pTab = 0; |
| | int iCol = 0; |
| | int iDb = 0; |
| | const char *zDb = 0; |
| | const char *pCons = 0; |
| | int nCons = 0; |
| |
|
| | |
| | assert( pSrc->nSrc==1 ); |
| | pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 0); |
| | if( !pTab ) return; |
| |
|
| | |
| | if( alterFindCol(pParse, pTab, pCol, &iCol) ){ |
| | return; |
| | } |
| |
|
| | |
| | pCons = pFirst->z; |
| | nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons); |
| |
|
| | |
| | sqlite3NestedParse(pParse, |
| | "SELECT sqlite_fail('constraint failed', %d) " |
| | "FROM %Q.%Q AS x WHERE x.%.*s IS NULL", |
| | SQLITE_CONSTRAINT, zDb, pTab->zName, (int)pCol->n, pCol->z |
| | ); |
| |
|
| | |
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_add_constraint(sqlite_drop_constraint(sql, %d), %.*Q, %d) " |
| | "WHERE type='table' AND tbl_name=%Q COLLATE nocase" |
| | , zDb, iCol, nCons, pCons, iCol, pTab->zName |
| | ); |
| |
|
| | |
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void findConstraintFunc( |
| | sqlite3_context *ctx, |
| | int NotUsed, |
| | sqlite3_value **argv |
| | ){ |
| | const u8 *zSql = 0; |
| | const u8 *zCons = 0; |
| | int iOff = 0; |
| | int t = 0; |
| |
|
| | (void)NotUsed; |
| | zSql = sqlite3_value_text(argv[0]); |
| | zCons = sqlite3_value_text(argv[1]); |
| |
|
| | if( zSql==0 || zCons==0 ) return; |
| | while( t!=TK_LP && t!=TK_ILLEGAL ){ |
| | iOff += sqlite3GetToken(&zSql[iOff], &t); |
| | } |
| |
|
| | while( 1 ){ |
| | iOff += getConstraintToken(&zSql[iOff], &t); |
| | if( t==TK_CONSTRAINT ){ |
| | int nTok = 0; |
| | int cmp = 0; |
| | iOff += getWhitespace(&zSql[iOff]); |
| | nTok = getConstraintToken(&zSql[iOff], &t); |
| | if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return; |
| | if( cmp==0 ){ |
| | sqlite3_result_int(ctx, 1); |
| | return; |
| | } |
| | }else if( t==TK_ILLEGAL ){ |
| | break; |
| | } |
| | } |
| |
|
| | sqlite3_result_int(ctx, 0); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | void sqlite3AlterAddConstraint( |
| | Parse *pParse, |
| | SrcList *pSrc, |
| | Token *pFirst, |
| | Token *pName, |
| | const char *pExpr, |
| | int nExpr |
| | ){ |
| | Table *pTab = 0; |
| | int iDb = 0; |
| | const char *zDb = 0; |
| | const char *pCons = 0; |
| | int nCons; |
| |
|
| | |
| | assert( pSrc->nSrc==1 ); |
| | pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 1); |
| | if( !pTab ) return; |
| |
|
| | |
| | |
| | if( pName ){ |
| | char *zName = sqlite3NameFromToken(pParse->db, pName); |
| |
|
| | sqlite3NestedParse(pParse, |
| | "SELECT sqlite_fail('constraint %q already exists', %d) " |
| | "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " |
| | "WHERE type='table' AND tbl_name=%Q COLLATE nocase " |
| | "AND sqlite_find_constraint(sql, %Q)", |
| | zName, SQLITE_ERROR, zDb, pTab->zName, zName |
| | ); |
| | sqlite3DbFree(pParse->db, zName); |
| | } |
| |
|
| | |
| | sqlite3NestedParse(pParse, |
| | "SELECT sqlite_fail('constraint failed', %d) " |
| | "FROM %Q.%Q WHERE (%.*s) IS NOT TRUE", |
| | SQLITE_CONSTRAINT, zDb, pTab->zName, nExpr, pExpr |
| | ); |
| |
|
| | |
| | pCons = pFirst->z; |
| | nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons); |
| |
|
| | sqlite3NestedParse(pParse, |
| | "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " |
| | "sql = sqlite_add_constraint(sql, %.*Q, -1) " |
| | "WHERE type='table' AND tbl_name=%Q COLLATE nocase" |
| | , zDb, nCons, pCons, pTab->zName |
| | ); |
| |
|
| | |
| | renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); |
| | } |
| |
|
| | |
| | |
| | |
| | void sqlite3AlterFunctions(void){ |
| | static FuncDef aAlterTableFuncs[] = { |
| | INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), |
| | INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), |
| | INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), |
| | INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), |
| | INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), |
| | INTERNAL_FUNCTION(sqlite_drop_constraint,2, dropConstraintFunc), |
| | INTERNAL_FUNCTION(sqlite_fail, 2, failConstraintFunc), |
| | INTERNAL_FUNCTION(sqlite_add_constraint, 3, addConstraintFunc), |
| | INTERNAL_FUNCTION(sqlite_find_constraint,2, findConstraintFunc), |
| | }; |
| | sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); |
| | } |
| | #endif |
| |
|