| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #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 |
|
|