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 | |
parent | d98f53249c363a48757500cf8918e160bc0ec8c1 (diff) | |
download | sqlite-cf8f2895424da7f73a96515abf3e42ef480eec2e.tar.gz sqlite-cf8f2895424da7f73a96515abf3e42ef480eec2e.zip |
Experimental implementation of ALTER TABLE ... RENAME COLUMN. Still buggy.
FossilOrigin-Name: fa0fc01eb48a864f0a3d43f9b805d5ed2e530846ee0c34fcbc2eabd9e5696277
-rw-r--r-- | manifest | 33 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-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 | ||||
-rw-r--r-- | test/altercol.test | 97 |
11 files changed, 595 insertions, 150 deletions
@@ -1,5 +1,5 @@ -C When\sa\scolumn\smust\sbe\sa\sconstant\sdue\sto\sWHERE\sclause\sand\sthe\svalue\sof\sthat\ncolumn\sis\sbeing\scoded\sas\sa\sconstant,\smake\ssure\sthe\saffinity\sis\scorrect. -D 2018-08-09T18:36:54.837 +C Experimental\simplementation\sof\sALTER\sTABLE\s...\sRENAME\sCOLUMN.\sStill\sbuggy. +D 2018-08-09T20:47:01.074 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,17 +432,17 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 6beb476095a4cfeb95ebedb2e5e17894d1687b24fddd5b8761a4de120e0392c6 +F src/alter.c 94baeaee1206dfed286b2a69c5e771519c9cf12253bfc3895ffa2198ce437bf0 F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a -F src/auth.c 8272da9ff761bf84f0357298ccc6e5924e3f7fac143c6587f06aa72b202445a2 +F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 F src/backup.c 78d3cecfbe28230a3a9a1793e2ead609f469be43e8f486ca996006be551857ab F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173 F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 -F src/build.c fe407be13d1201bf386d2c629424e5c97a07bcfc6ef21cf6e888e50b792a6191 +F src/build.c 1cdcd12f5df74f4545d9677d7beebe0281a648d0302fca6109e82ac12e13cfa2 F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387 @@ -487,7 +487,7 @@ F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 76d29b8a960dcb8b67210f095899d91e4a90673a6674ea58cfd1115b705a7fb9 F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388 -F src/parse.y 704c94624d41d7d46a5467574130e55aa8029a563f4df538f0121475eae46e34 +F src/parse.y b981ac4656a9289bd61d78705f238dc8d156a7feee4bf064d054de4e8f10ff53 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 @@ -496,14 +496,14 @@ F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 F src/prepare.c e966ecc97c3671ff0e96227c8c877b83f2d33ea371ee190bbf1698b36b5605c0 F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 -F src/resolve.c 797088662ed61102485e3070ba3b3f7828bd5ef6a588223ba6865d77d52f6cea +F src/resolve.c b51a48f33da36e0c2dd1ea5f0d10197c3e938a54086a69efce064ae41e2254e1 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04 F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h a5d212bb7ae5cfc0540af6fb09eee2092a45fe083fac4191ee64ff70e7d4d78a +F src/sqliteInt.h 4c9dc8cf5a231803c295558696077263c6f0c6f5aa505343f5a1e30b19dc59cf F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -561,7 +561,7 @@ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a9 F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c -F src/tokenize.c 01e96d1b639c3eb0b9ef90616e766d453935c554f1f7aa86b6db937b79554b97 +F src/tokenize.c 3177575b587f3d0491ca5c3e937663ca3e91bf86adf4f2e8e9f7d0ab98bc66d3 F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995 F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b @@ -578,7 +578,7 @@ F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c9419 F src/vdbemem.c 720df42ad8e5c7cb883573de40a185afef4a214903098a16f2bb14b62b2399b7 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392 -F src/vtab.c 678992ac8ec677a3f9b08126aaf891441083805e3b42574e3654d44538381c14 +F src/vtab.c 8665561f244c137a2d17b5c3e5910d7303054fe841c5d510e53f23beb0089594 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c c617d78715e85d81fe5719b276d32186a37eb47a49d07a3d55ddbc541de043c9 F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a @@ -599,6 +599,7 @@ F test/alter.test b820ab9dcf85f8e3a65bc8326accb2f0c7be64ef F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 4d79934d812eaeacc6f22781a080f8cfe012fdc3 F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b +F test/altercol.test 44fedc517b92f321418c0918a359456a1657614c6bcd22570c0368c2417a3a29 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1754,7 +1755,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 60bbca2b9a591800cd8e7b374e62d75b1df0e8fd2d2f71f9b4d5fd044da78be0 -R ef663a1df8c5f6cbefbb7dcd86b83b66 -U drh -Z 68dbd529c4e95246b96ffd22fa0b508e +P 7404ea83168e6c739ebe8fc5d65bbf0265432ccb35b3418bb0381d74362f7527 +R 0f3d0199e04ab490bfdcf0cb2303572a +T *branch * alter-table-rename-column +T *sym-alter-table-rename-column * +T +closed 4889866915b8b8fc28ca20cfc3f2ed4890eb1f0712b0537298fbdd498ce41510 +T -sym-trunk * +U dan +Z 92426cbf3686886dee1eff1497f8d0cc diff --git a/manifest.uuid b/manifest.uuid index e0abbb930..36a2d09de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7404ea83168e6c739ebe8fc5d65bbf0265432ccb35b3418bb0381d74362f7527
\ No newline at end of file +fa0fc01eb48a864f0a3d43f9b805d5ed2e530846ee0c34fcbc2eabd9e5696277
\ No newline at end of file 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); diff --git a/test/altercol.test b/test/altercol.test new file mode 100644 index 000000000..9727cb546 --- /dev/null +++ b/test/altercol.test @@ -0,0 +1,97 @@ +# 2009 February 2 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# This file implements regression tests for SQLite library. The +# focus of this script is testing that SQLite can handle a subtle +# file format change that may be used in the future to implement +# "ALTER TABLE ... RENAME COLUMN ... TO". +# +# $Id: alter4.test,v 1.1 2009/02/02 18:03:22 drh Exp $ +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix altercol + +# If SQLITE_OMIT_ALTERTABLE is defined, omit this file. +ifcapable !altertable { + finish_test + return +} + +foreach {tn before after} { + 1 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB)} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB)} + + 2 {CREATE TABLE t1(a INTEGER, x TEXT, "b" BLOB)} + {CREATE TABLE t1(a INTEGER, x TEXT, "d" BLOB)} + + 3 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(b!=''))} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(d!=''))} + + 4 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK(t1.b!=''))} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK(t1.d!=''))} + + 5 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, CHECK( coalesce(b,c) ))} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, CHECK( coalesce(d,c) ))} + + 6 {CREATE TABLE t1(a INTEGER, "b"TEXT, c BLOB, CHECK( coalesce(b,c) ))} + {CREATE TABLE t1(a INTEGER, "d"TEXT, c BLOB, CHECK( coalesce(d,c) ))} + + 7 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b, c))} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d, c))} + + 8 {CREATE TABLE t1(a INTEGER, b TEXT PRIMARY KEY, c BLOB)} + {CREATE TABLE t1(a INTEGER, d TEXT PRIMARY KEY, c BLOB)} + + 9 {CREATE TABLE t1(a, b TEXT, c, PRIMARY KEY(a, b), UNIQUE("B"))} + {CREATE TABLE t1(a, d TEXT, c, PRIMARY KEY(a, d), UNIQUE("d"))} + + 10 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(a, c)} + {{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(a, c)}} + + 11 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b, c)} + {{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d, c)}} + + 12 {CREATE TABLE t1(a, b, c); CREATE INDEX t1i ON t1(b+b+b+b, c) WHERE b>0} + {{CREATE TABLE t1(a, d, c)} {CREATE INDEX t1i ON t1(d+d+d+d, c) WHERE d>0}} + + 13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)} + {CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)} + +} { + reset_db + do_execsql_test 1.$tn.0 $before + + do_execsql_test 1.$tn.1 { + INSERT INTO t1 VALUES(1, 2, 3); + } + + do_execsql_test 1.$tn.2 { + ALTER TABLE t1 RENAME COLUMN b TO d; + } + + do_execsql_test 1.$tn.3 { + SELECT * FROM t1; + } {1 2 3} + + if {[string first INDEX $before]>0} { + set res $after + } else { + set res [list $after] + } + do_execsql_test 1.$tn.4 { + SELECT sql FROM sqlite_master WHERE tbl_name='t1' AND sql!='' + } $res +} + +finish_test + + |