diff options
author | dan <dan@noemail.net> | 2018-08-09 20:47:01 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2018-08-09 20:47:01 +0000 |
commit | cf8f2895424da7f73a96515abf3e42ef480eec2e (patch) | |
tree | a420a500591a0264e081a5f78f555d23b0717aa8 /src | |
parent | d98f53249c363a48757500cf8918e160bc0ec8c1 (diff) | |
download | sqlite-cf8f2895424da7f73a96515abf3e42ef480eec2e.tar.gz sqlite-cf8f2895424da7f73a96515abf3e42ef480eec2e.zip |
Experimental implementation of ALTER TABLE ... RENAME COLUMN. Still buggy.
FossilOrigin-Name: fa0fc01eb48a864f0a3d43f9b805d5ed2e530846ee0c34fcbc2eabd9e5696277
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 318 | ||||
-rw-r--r-- | src/auth.c | 2 | ||||
-rw-r--r-- | src/build.c | 237 | ||||
-rw-r--r-- | src/parse.y | 11 | ||||
-rw-r--r-- | src/resolve.c | 7 | ||||
-rw-r--r-- | src/sqliteInt.h | 32 | ||||
-rw-r--r-- | src/tokenize.c | 2 | ||||
-rw-r--r-- | src/vtab.c | 4 |
8 files changed, 478 insertions, 135 deletions
diff --git a/src/alter.c b/src/alter.c index 2d7a5d659..c4dc00bd2 100644 --- a/src/alter.c +++ b/src/alter.c @@ -226,22 +226,6 @@ static void renameTriggerFunc( #endif /* !SQLITE_OMIT_TRIGGER */ /* -** Register built-in functions used to help implement ALTER TABLE -*/ -void sqlite3AlterFunctions(void){ - static FuncDef aAlterTableFuncs[] = { - FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), -#ifndef SQLITE_OMIT_TRIGGER - FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), -#endif -#ifndef SQLITE_OMIT_FOREIGN_KEY - FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), -#endif - }; - sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); -} - -/* ** This function is used to create the text of expressions of the form: ** ** name=<constant1> OR name=<constant2> OR ... @@ -805,4 +789,306 @@ exit_begin_add_column: sqlite3SrcListDelete(db, pSrc); return; } + +void sqlite3AlterRenameColumn( + Parse *pParse, + SrcList *pSrc, + Token *pOld, + Token *pNew +){ + sqlite3 *db = pParse->db; + Table *pTab; /* Table being updated */ + int iCol; /* Index of column being renamed */ + char *zOld = 0; + char *zNew = 0; + const char *zDb; + int iSchema; + + pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); + if( !pTab ) goto exit_rename_column; + iSchema = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iSchema>=0 ); + zDb = db->aDb[iSchema].zDbSName; + + zOld = sqlite3NameFromToken(db, pOld); + if( !zOld ) goto exit_rename_column; + for(iCol=0; iCol<pTab->nCol; iCol++){ + if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break; + } + if( iCol==pTab->nCol ){ + sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld); + goto exit_rename_column; + } + + zNew = sqlite3NameFromToken(db, pNew); + if( !zNew ) goto exit_rename_column; + + sqlite3NestedParse(pParse, + "UPDATE \"%w\".%s SET " + "sql = sqlite_rename_column(sql, %d, %Q) " + "WHERE type IN ('table', 'index') AND tbl_name = %Q AND sql!=''", + zDb, MASTER_NAME, iCol, zNew, pTab->zName + ); + + /* Drop and reload the internal table schema. */ + reloadTableSchema(pParse, pTab, pTab->zName); + + exit_rename_column: + sqlite3SrcListDelete(db, pSrc); + sqlite3DbFree(db, zOld); + sqlite3DbFree(db, zNew); + return; +} + +struct RenameToken { + void *p; + Token t; + RenameToken *pNext; +}; + +struct RenameCtx { + RenameToken *pList; /* List of tokens to overwrite */ + int nList; /* Number of tokens in pList */ + int iCol; /* Index of column being renamed */ +}; + +void sqlite3RenameToken(Parse *pParse, void *pPtr, Token *pToken){ + RenameToken *pNew; + pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken)); + if( pNew ){ + pNew->p = pPtr; + pNew->t = *pToken; + pNew->pNext = pParse->pRename; + pParse->pRename = pNew; + } +} + +void sqlite3MoveRenameToken(Parse *pParse, void *pTo, void *pFrom){ + RenameToken *p; + for(p=pParse->pRename; p; p=p->pNext){ + if( p->p==pFrom ){ + p->p = pTo; + break; + } + } + assert( p ); +} + +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, void *pPtr){ + RenameToken **pp; + for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ + if( (*pp)->p==pPtr ){ + RenameToken *pToken = *pp; + *pp = pToken->pNext; + pToken->pNext = 0; + return pToken; + } + } + return 0; +} + +static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ + struct RenameCtx *p = pWalker->u.pRename; + if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){ + RenameToken *pTok = renameTokenFind(pWalker->pParse, (void*)pExpr); + if( pTok ){ + pTok->pNext = p->pList; + p->pList = pTok; + p->nList++; + } + } + return WRC_Continue; +} + +static RenameToken *renameColumnTokenNext(struct 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 renameColumnFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + struct RenameCtx sCtx; + const char *zSql = sqlite3_value_text(argv[0]); + int nSql = sqlite3_value_bytes(argv[0]); + const char *zNew = sqlite3_value_text(argv[2]); + int nNew = sqlite3_value_bytes(argv[2]); + int rc; + char *zErr = 0; + Parse sParse; + Walker sWalker; + Table *pTab; + Index *pIdx; + char *zOut = 0; + + char *zQuot = 0; /* Quoted version of zNew */ + int nQuot = 0; /* Length of zQuot in bytes */ + int i; + + memset(&sCtx, 0, sizeof(sCtx)); + sCtx.iCol = sqlite3_value_int(argv[1]); + + memset(&sParse, 0, sizeof(sParse)); + sParse.eParseMode = PARSE_MODE_RENAME_COLUMN; + sParse.db = db; + sParse.nQueryLoop = 1; + rc = sqlite3RunParser(&sParse, zSql, &zErr); + assert( sParse.pNewTable==0 || sParse.pNewIndex==0 ); + if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 ){ + rc = SQLITE_CORRUPT_BKPT; + } + + if( rc==SQLITE_OK ){ + zQuot = sqlite3_mprintf("\"%w\"", zNew); + if( zQuot==0 ){ + rc = SQLITE_NOMEM; + }else{ + nQuot = sqlite3Strlen30(zQuot); + } + } + + if( rc!=SQLITE_OK ){ + if( zErr ){ + sqlite3_result_error(context, zErr, -1); + }else{ + sqlite3_result_error_code(context, rc); + } + sqlite3DbFree(db, zErr); + sqlite3_free(zQuot); + return; + } + + for(i=0; i<nNew; i++){ + if( sqlite3IsIdChar(zNew[i])==0 ){ + zNew = zQuot; + nNew = nQuot; + break; + } + } + +#ifdef SQLITE_DEBUG + assert( sqlite3Strlen30(zSql)==nSql ); + { + RenameToken *pToken; + for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){ + assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] ); + } + } +#endif + + /* Find tokens that need to be replaced. */ + memset(&sWalker, 0, sizeof(Walker)); + sWalker.pParse = &sParse; + sWalker.xExprCallback = renameColumnExprCb; + sWalker.u.pRename = &sCtx; + + if( sParse.pNewTable ){ + FKey *pFKey; + sCtx.pList = renameTokenFind( + &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName + ); + sCtx.nList = 1; + sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck); + for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ + sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + } + + for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + for(i=0; i<pFKey->nCol; i++){ + if( pFKey->aCol[i].iFrom==sCtx.iCol ){ + RenameToken *pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]); + if( pTok ){ + pTok->pNext = sCtx.pList; + sCtx.pList = pTok; + sCtx.nList++; + } + } + } + } + }else{ + sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); + sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + } + + zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nNew + 1); + if( zOut ){ + int nOut = nSql; + memcpy(zOut, zSql, nSql); + while( sCtx.pList ){ + int iOff; /* Offset of token to replace in zOut */ + RenameToken *pBest = renameColumnTokenNext(&sCtx); + + int nReplace; + const char *zReplace; + if( sqlite3IsIdChar(*pBest->t.z) ){ + nReplace = nNew; + zReplace = zNew; + }else{ + nReplace = nQuot; + zReplace = zQuot; + } + + iOff = 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(context, zOut, -1, SQLITE_TRANSIENT); + sqlite3DbFree(db, zOut); + } + + if( sParse.pVdbe ){ + sqlite3VdbeFinalize(sParse.pVdbe); + } + sqlite3DeleteTable(db, sParse.pNewTable); + if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex); + renameTokenFree(db, sParse.pRename); + sqlite3ParserReset(&sParse); + sqlite3_free(zQuot); +} + +/* +** Register built-in functions used to help implement ALTER TABLE +*/ +void sqlite3AlterFunctions(void){ + static FuncDef aAlterTableFuncs[] = { + FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), + FUNCTION(sqlite_rename_column, 3, 0, 0, renameColumnFunc), +#ifndef SQLITE_OMIT_TRIGGER + FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), +#endif +#ifndef SQLITE_OMIT_FOREIGN_KEY + FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), +#endif + }; + sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); +} #endif /* SQLITE_ALTER_TABLE */ diff --git a/src/auth.c b/src/auth.c index a708d0c24..918ff46c3 100644 --- a/src/auth.c +++ b/src/auth.c @@ -207,7 +207,7 @@ int sqlite3AuthCheck( /* Don't do any authorization checks if the database is initialising ** or if the parser is being invoked from within sqlite3_declare_vtab. */ - if( db->init.busy || IN_DECLARE_VTAB ){ + if( db->init.busy || IN_SPECIAL_PARSE ){ return SQLITE_OK; } diff --git a/src/build.c b/src/build.c index 62d0ff14d..eab9d441d 100644 --- a/src/build.c +++ b/src/build.c @@ -439,7 +439,7 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ /* ** Reclaim the memory used by an index */ -static void freeIndex(sqlite3 *db, Index *p){ +void sqlite3FreeIndex(sqlite3 *db, Index *p){ #ifndef SQLITE_OMIT_ANALYZE sqlite3DeleteIndexSamples(db, p); #endif @@ -479,7 +479,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ p->pNext = pIndex->pNext; } } - freeIndex(db, pIndex); + sqlite3FreeIndex(db, pIndex); } db->mDbFlags |= DBFLAG_SchemaChange; } @@ -625,7 +625,7 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); assert( pOld==pIndex || pOld==0 ); } - freeIndex(db, pIndex); + sqlite3FreeIndex(db, pIndex); } /* Delete any foreign keys attached to this table. */ @@ -913,7 +913,7 @@ void sqlite3StartTable( ** and types will be used, so there is no need to test for namespace ** collisions. */ - if( !IN_DECLARE_VTAB ){ + if( !IN_SPECIAL_PARSE ){ char *zDb = db->aDb[iDb].zDbSName; if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto begin_table_error; @@ -1072,6 +1072,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){ } z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2); if( z==0 ) return; + if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)z, pName); memcpy(z, pName->z, pName->n); z[pName->n] = 0; sqlite3Dequote(z); @@ -2761,6 +2762,9 @@ void sqlite3CreateForeignKey( pFromCol->a[i].zName); goto fk_end; } + if( IN_RENAME_COLUMN ){ + sqlite3MoveRenameToken(pParse, &pFKey->aCol[i], &pFromCol->a[i]); + } } } if( pToCol ){ @@ -3099,20 +3103,22 @@ void sqlite3CreateIndex( if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto exit_create_index; } - if( !db->init.busy ){ - if( sqlite3FindTable(db, zName, 0)!=0 ){ - sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); - goto exit_create_index; + if( !IN_RENAME_COLUMN ){ + if( !db->init.busy ){ + if( sqlite3FindTable(db, zName, 0)!=0 ){ + sqlite3ErrorMsg(pParse, "there is already a table named %s", zName); + goto exit_create_index; + } } - } - if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){ - if( !ifNotExist ){ - sqlite3ErrorMsg(pParse, "index %s already exists", zName); - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); + if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){ + if( !ifNotExist ){ + sqlite3ErrorMsg(pParse, "index %s already exists", zName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); + } + goto exit_create_index; } - goto exit_create_index; } }else{ int n; @@ -3128,13 +3134,13 @@ void sqlite3CreateIndex( ** The following statement converts "sqlite3_autoindex..." into ** "sqlite3_butoindex..." in order to make the names distinct. ** The "vtab_err.test" test demonstrates the need of this statement. */ - if( IN_DECLARE_VTAB ) zName[7]++; + if( IN_SPECIAL_PARSE ) zName[7]++; } /* Check for authorization to create an index. */ #ifndef SQLITE_OMIT_AUTHORIZATION - { + if( !IN_RENAME_COLUMN ){ const char *zDb = pDb->zDbSName; if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ goto exit_create_index; @@ -3221,7 +3227,12 @@ void sqlite3CreateIndex( ** TODO: Issue a warning if the table primary key is used as part of the ** index key. */ - for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){ + pListItem = pList->a; + if( IN_RENAME_COLUMN ){ + pIndex->aColExpr = pList; + pList = 0; + } + for(i=0; i<pIndex->nKeyCol; i++, pListItem++){ Expr *pCExpr; /* The i-th index expression */ int requestedSortOrder; /* ASC or DESC on the i-th expression */ const char *zColl; /* Collation sequence name */ @@ -3237,12 +3248,8 @@ void sqlite3CreateIndex( goto exit_create_index; } if( pIndex->aColExpr==0 ){ - ExprList *pCopy = sqlite3ExprListDup(db, pList, 0); - pIndex->aColExpr = pCopy; - if( !db->mallocFailed ){ - assert( pCopy!=0 ); - pListItem = &pCopy->a[i]; - } + pIndex->aColExpr = pList; + pList = 0; } j = XN_EXPR; pIndex->aiColumn[i] = XN_EXPR; @@ -3381,98 +3388,101 @@ void sqlite3CreateIndex( } } - /* Link the new Index structure to its table and to the other - ** in-memory database structures. - */ - assert( pParse->nErr==0 ); - if( db->init.busy ){ - Index *p; - assert( !IN_DECLARE_VTAB ); - assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); - p = sqlite3HashInsert(&pIndex->pSchema->idxHash, - pIndex->zName, pIndex); - if( p ){ - assert( p==pIndex ); /* Malloc must have failed */ - sqlite3OomFault(db); - goto exit_create_index; - } - db->mDbFlags |= DBFLAG_SchemaChange; - if( pTblName!=0 ){ - pIndex->tnum = db->init.newTnum; - } - } - - /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the - ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then - ** emit code to allocate the index rootpage on disk and make an entry for - ** the index in the sqlite_master table and populate the index with - ** content. But, do not do this if we are simply reading the sqlite_master - ** table to parse the schema, or if this index is the PRIMARY KEY index - ** of a WITHOUT ROWID table. - ** - ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY - ** or UNIQUE index in a CREATE TABLE statement. Since the table - ** has just been created, it contains no data and the index initialization - ** step can be skipped. - */ - else if( HasRowid(pTab) || pTblName!=0 ){ - Vdbe *v; - char *zStmt; - int iMem = ++pParse->nMem; - - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto exit_create_index; + if( !IN_RENAME_COLUMN ){ - sqlite3BeginWriteOperation(pParse, 1, iDb); - - /* Create the rootpage for the index using CreateIndex. But before - ** doing so, code a Noop instruction and store its address in - ** Index.tnum. This is required in case this index is actually a - ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In - ** that case the convertToWithoutRowidTable() routine will replace - ** the Noop with a Goto to jump over the VDBE code generated below. */ - pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); - sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); - - /* Gather the complete text of the CREATE INDEX statement into - ** the zStmt variable + /* Link the new Index structure to its table and to the other + ** in-memory database structures. */ - if( pStart ){ - int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n; - if( pName->z[n-1]==';' ) n--; - /* A named index with an explicit CREATE INDEX statement */ - zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s", - onError==OE_None ? "" : " UNIQUE", n, pName->z); - }else{ - /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ - /* zStmt = sqlite3MPrintf(""); */ - zStmt = 0; + assert( pParse->nErr==0 ); + if( db->init.busy ){ + Index *p; + assert( !IN_SPECIAL_PARSE ); + assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); + p = sqlite3HashInsert(&pIndex->pSchema->idxHash, + pIndex->zName, pIndex); + if( p ){ + assert( p==pIndex ); /* Malloc must have failed */ + sqlite3OomFault(db); + goto exit_create_index; + } + db->mDbFlags |= DBFLAG_SchemaChange; + if( pTblName!=0 ){ + pIndex->tnum = db->init.newTnum; + } } - /* Add an entry in sqlite_master for this index + /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the + ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then + ** emit code to allocate the index rootpage on disk and make an entry for + ** the index in the sqlite_master table and populate the index with + ** content. But, do not do this if we are simply reading the sqlite_master + ** table to parse the schema, or if this index is the PRIMARY KEY index + ** of a WITHOUT ROWID table. + ** + ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY + ** or UNIQUE index in a CREATE TABLE statement. Since the table + ** has just been created, it contains no data and the index initialization + ** step can be skipped. */ - sqlite3NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", - db->aDb[iDb].zDbSName, MASTER_NAME, - pIndex->zName, - pTab->zName, - iMem, - zStmt - ); - sqlite3DbFree(db, zStmt); + else if( HasRowid(pTab) || pTblName!=0 ){ + Vdbe *v; + char *zStmt; + int iMem = ++pParse->nMem; + + v = sqlite3GetVdbe(pParse); + if( v==0 ) goto exit_create_index; + + sqlite3BeginWriteOperation(pParse, 1, iDb); + + /* Create the rootpage for the index using CreateIndex. But before + ** doing so, code a Noop instruction and store its address in + ** Index.tnum. This is required in case this index is actually a + ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In + ** that case the convertToWithoutRowidTable() routine will replace + ** the Noop with a Goto to jump over the VDBE code generated below. */ + pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop); + sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY); + + /* Gather the complete text of the CREATE INDEX statement into + ** the zStmt variable + */ + if( pStart ){ + int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n; + if( pName->z[n-1]==';' ) n--; + /* A named index with an explicit CREATE INDEX statement */ + zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s", + onError==OE_None ? "" : " UNIQUE", n, pName->z); + }else{ + /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ + /* zStmt = sqlite3MPrintf(""); */ + zStmt = 0; + } - /* Fill the index with data and reparse the schema. Code an OP_Expire - ** to invalidate all pre-compiled statements. - */ - if( pTblName ){ - sqlite3RefillIndex(pParse, pIndex, iMem); - sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddParseSchemaOp(v, iDb, - sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); - sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); - } + /* Add an entry in sqlite_master for this index + */ + sqlite3NestedParse(pParse, + "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", + db->aDb[iDb].zDbSName, MASTER_NAME, + pIndex->zName, + pTab->zName, + iMem, + zStmt + ); + sqlite3DbFree(db, zStmt); + + /* Fill the index with data and reparse the schema. Code an OP_Expire + ** to invalidate all pre-compiled statements. + */ + if( pTblName ){ + sqlite3RefillIndex(pParse, pIndex, iMem); + sqlite3ChangeCookie(pParse, iDb); + sqlite3VdbeAddParseSchemaOp(v, iDb, + sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); + sqlite3VdbeAddOp2(v, OP_Expire, 0, 1); + } - sqlite3VdbeJumpHere(v, pIndex->tnum); + sqlite3VdbeJumpHere(v, pIndex->tnum); + } } /* When adding an index to the list of indices for a table, make @@ -3496,10 +3506,15 @@ void sqlite3CreateIndex( } pIndex = 0; } + else if( IN_RENAME_COLUMN ){ + assert( pParse->pNewIndex==0 ); + pParse->pNewIndex = pIndex; + pIndex = 0; + } /* Clean up before exiting */ exit_create_index: - if( pIndex ) freeIndex(db, pIndex); + if( pIndex ) sqlite3FreeIndex(db, pIndex); sqlite3ExprDelete(db, pPIWhere); sqlite3ExprListDelete(db, pList); sqlite3SrcListDelete(db, pTblName); diff --git a/src/parse.y b/src/parse.y index ea2052934..752f30a77 100644 --- a/src/parse.y +++ b/src/parse.y @@ -940,6 +940,7 @@ idlist(A) ::= nm(Y). if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted; sqlite3Dequote(p->u.zToken); } + if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)p, &t); #if SQLITE_MAX_EXPR_DEPTH>0 p->nHeight = 1; #endif @@ -955,6 +956,7 @@ expr(A) ::= JOIN_KW(X). {A=tokenExpr(pParse,TK_ID,X); /*A-overwrites-X*/} expr(A) ::= nm(X) DOT nm(Y). { Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1); Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); + if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)temp2, &Y); A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { @@ -962,6 +964,7 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1); Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3); + if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)temp3, &Z); A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/} @@ -1308,6 +1311,9 @@ uniqueflag(A) ::= . {A = OE_None;} pIdToken->n, pIdToken->z); } sqlite3ExprListSetName(pParse, p, pIdToken, 1); + if( IN_RENAME_COLUMN ){ + sqlite3RenameToken(pParse, (void*)&(p->a[p->nExpr-1]), pIdToken); + } return p; } } // end %include @@ -1532,8 +1538,13 @@ add_column_fullname ::= fullname(X). { disableLookaside(pParse); sqlite3AlterBeginAddColumn(pParse, X); } +cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). { + sqlite3AlterRenameColumn(pParse, X, &Y, &Z); +} + kwcolumn_opt ::= . kwcolumn_opt ::= COLUMNKW. + %endif SQLITE_OMIT_ALTERTABLE //////////////////////// CREATE VIRTUAL TABLE ... ///////////////////////////// diff --git a/src/resolve.c b/src/resolve.c index 5c1dd09be..fc815eed6 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -668,12 +668,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( pRight->op==TK_ID ){ zDb = 0; zTable = pExpr->pLeft->u.zToken; - zColumn = pRight->u.zToken; }else{ assert( pRight->op==TK_DOT ); zDb = pExpr->pLeft->u.zToken; zTable = pRight->pLeft->u.zToken; - zColumn = pRight->pRight->u.zToken; + pRight = pRight->pRight; + } + zColumn = pRight->u.zToken; + if( IN_RENAME_COLUMN ){ + sqlite3MoveRenameToken(pParse, (void*)pExpr, (void*)pRight); } } return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6304680ab..12812f042 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1087,6 +1087,7 @@ typedef struct NameContext NameContext; typedef struct Parse Parse; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; +typedef struct RenameToken RenameToken; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; @@ -3087,8 +3088,10 @@ struct Parse { ynVar nVar; /* Number of '?' variables seen in the SQL so far */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ u8 explain; /* True if the EXPLAIN flag is found on the query */ +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE) + u8 eParseMode; /* PARSE_MODE_XXX constant */ +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE - u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ #endif int nHeight; /* Expression tree height of current sub-select */ @@ -3099,6 +3102,7 @@ struct Parse { Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ + Index *pNewIndex; /* An index being constructed by CREATE INDEX */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -3109,8 +3113,15 @@ struct Parse { TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ With *pWithToFree; /* Free this WITH object at the end of the parse */ +#ifndef SQLITE_OMIT_ALTERTABLE + RenameToken *pRename; +#endif }; +#define PARSE_MODE_NORMAL 0 +#define PARSE_MODE_DECLARE_VTAB 1 +#define PARSE_MODE_RENAME_COLUMN 2 + /* ** Sizes and pointers of various parts of the Parse object. */ @@ -3125,7 +3136,19 @@ struct Parse { #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else - #define IN_DECLARE_VTAB (pParse->declareVtab) + #define IN_DECLARE_VTAB (pParse->eParseMode==PARSE_MODE_DECLARE_VTAB) +#endif + +#if defined(SQLITE_OMIT_ALTERTABLE) + #define IN_RENAME_COLUMN 0 +#else + #define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN) +#endif + +#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE) + #define IN_SPECIAL_PARSE 0 +#else + #define IN_SPECIAL_PARSE (pParse->eParseMode!=PARSE_MODE_NORMAL) #endif /* @@ -3409,6 +3432,7 @@ struct Walker { Select *pSelect; /* HAVING to WHERE clause ctx */ struct WindowRewrite *pRewrite; /* Window rewrite context */ struct WhereConst *pConst; /* WHERE clause constants */ + struct RenameCtx *pRename; /* RENAME COLUMN context */ } u; }; @@ -3844,6 +3868,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int); void sqlite3DropTable(Parse*, SrcList*, int, int); void sqlite3CodeDropTable(Parse*, Table*, int, int); void sqlite3DeleteTable(sqlite3*, Table*); +void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT void sqlite3AutoincrementBegin(Parse *pParse); void sqlite3AutoincrementEnd(Parse *pParse); @@ -4188,6 +4213,7 @@ void sqlite3RootPageMoved(sqlite3*, int, int, int); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); +void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*, int); @@ -4203,6 +4229,8 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); +void sqlite3RenameToken(Parse*, void*, Token*); +void sqlite3MoveRenameToken(Parse*, void *pTo, void *pFrom); CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); char sqlite3AffinityType(const char*, Column*); void sqlite3Analyze(Parse*, Token*, Token*); diff --git a/src/tokenize.c b/src/tokenize.c index 6cdff74d3..685d10b25 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -690,7 +690,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ sqlite3_free(pParse->apVtabLock); #endif - if( !IN_DECLARE_VTAB ){ + if( !IN_SPECIAL_PARSE ){ /* If the pParse->declareVtab flag is set, do not delete any table ** structure built up in pParse->pNewTable. The calling code (see vtab.c) ** will take responsibility for freeing the Table structure. diff --git a/src/vtab.c b/src/vtab.c index c9ce83890..7b459be7b 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -758,7 +758,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ assert( IsVirtual(pTab) ); memset(&sParse, 0, sizeof(sParse)); - sParse.declareVtab = 1; + sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; sParse.db = db; sParse.nQueryLoop = 1; if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) @@ -799,7 +799,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; } - sParse.declareVtab = 0; + sParse.eParseMode = PARSE_MODE_NORMAL; if( sParse.pVdbe ){ sqlite3VdbeFinalize(sParse.pVdbe); |