From 5496d6a25aed8101ec0949307b8711e2df8c67ab Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 13 Aug 2018 17:14:26 +0000 Subject: [PATCH 1/7] Edit the WHEN and UPDATE OF clauses of trigger programs as part of ALTER TABLE RENAME COLUMN. FossilOrigin-Name: 5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326 --- manifest | 30 ++++++++++--------- manifest.uuid | 2 +- src/alter.c | 73 +++++++++++++++++++++++++++------------------- src/build.c | 6 +++- src/parse.y | 4 +-- src/sqliteInt.h | 2 +- src/tokenize.c | 4 ++- src/trigger.c | 38 +++++++++++++++++------- test/altercol.test | 29 +++++++++++++++++- 9 files changed, 127 insertions(+), 61 deletions(-) diff --git a/manifest b/manifest index d6c9346159..e874f7babd 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\slegacy\scomments\son\sToken.\s\sBegin\scommenting\sthe\snew\sALTER\sTABLE\sRENAME\nCOLUMN\scode.\s\sFix\sa\smemory\sleak\sin\sthe\ssqlite_rename_column()\sSQL\sfunction. -D 2018-08-13T15:09:48.567 +C Edit\sthe\sWHEN\sand\sUPDATE\sOF\sclauses\sof\strigger\sprograms\sas\spart\sof\sALTER\sTABLE\nRENAME\sCOLUMN. +D 2018-08-13T17:14:26.913 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 7a8e8f14b07063973b772b07921bcc54db36e26e8aae14d2584446ed5af9e513 +F src/alter.c 9f8231841d00ef09d328ecb39af46e0cd85ce4e39d416319d458f8aff7af10dd F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173 F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 -F src/build.c a8e71a3cf77ddf953a85d9d2d4c7fbc0a0f70ef4e4515915ad6ff1cdff9c9808 +F src/build.c 49cad074fcc2218f45e126375b4d8f7c3960d2fe2c3398bad862e1bfd640d7c4 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 035b397bf1acbd3cb8293812b07c8aefce9f275d4de1442e113dbd0d2f733dce +F src/parse.y 1cdfb5179649b0cde2459d0e76b5acb7007bb10f4744ae804b2326ed98c52060 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 @@ -503,7 +503,7 @@ F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1 F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 6cce5956d82972f3e03444c15636222e97938eb9f3dad399df038052c51a04a6 +F src/sqliteInt.h 6a5d65117068808e2f37035d97a4cea6d55d679e02f9673b2b71ae6d5a0b1c8d F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -561,9 +561,9 @@ 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 3177575b587f3d0491ca5c3e937663ca3e91bf86adf4f2e8e9f7d0ab98bc66d3 +F src/tokenize.c fe35cb947ec54fe9a96895359b777a9305356c2f3c2917a1d83a57603108e66c F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f -F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995 +F src/trigger.c f3ba315cd590a3db88fdfd259c8084e567746f6e0614efb3c806b205d7122276 F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 @@ -599,7 +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 a64f69f67a8ac2ab7fd671b3166ad3454e0fea6bf8c22fdc3630a4b0cdeb57e8 +F test/altercol.test b3263a3d663e57e35fcd888614cb2256df42ac1be07d40282c5c6023a47a431b F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,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 0b28dd5c2e4908d5e49eaedd359492e46de8af3bf84120f4683b3ea906882fbf -R 8e4aad63439996e323e3c103c6b55b1d -U drh -Z 82791a60e4da34a95b2d48254121d988 +P 32edc8920376aabb84ebe1900eaa9512d23f1b44d6459e4916dc6b07db66e27c +R 4f99214f849ec616e294c625bebc2173 +T *branch * edit-trigger-wrapper +T *sym-edit-trigger-wrapper * +T +closed 43c3d2d707a7c54dcb75660be9347d6bd79c4844f81f6ed73446570876f5f72b +T -sym-alter-table-rename-column * +U dan +Z e89da0f8ef0add6d1c95dbb889527ec9 diff --git a/manifest.uuid b/manifest.uuid index aa113d45ed..d9332dde87 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -32edc8920376aabb84ebe1900eaa9512d23f1b44d6459e4916dc6b07db66e27c \ No newline at end of file +5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index c19eb67a67..5dbd42a28c 100644 --- a/src/alter.c +++ b/src/alter.c @@ -846,7 +846,7 @@ void sqlite3AlterRenameColumn( "UPDATE \"%w\".%s SET " "sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) " "WHERE name NOT LIKE 'sqlite_%%' AND (" - " type = 'table' OR (type='index' AND tbl_name = %Q)" + " type = 'table' OR (type IN ('index', 'trigger') AND tbl_name = %Q)" ")", zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName ); @@ -891,10 +891,12 @@ struct RenameToken { ** The context of an ALTER TABLE RENAME COLUMN operation that gets passed ** down into the Walker. */ +typedef struct RenameCtx RenameCtx; struct RenameCtx { RenameToken *pList; /* List of tokens to overwrite */ int nList; /* Number of tokens in pList */ int iCol; /* Index of column being renamed */ + const char *zOld; /* Old column name */ }; /* @@ -940,33 +942,40 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ } } -static RenameToken *renameTokenFind(Parse *pParse, void *pPtr){ +static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, 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; + pToken->pNext = pCtx->pList; + pCtx->pList = pToken; + pCtx->nList++; + break; } } - 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++; + RenameCtx *p = pWalker->u.pRename; + if( p->zOld && pExpr->op==TK_DOT ){ + Expr *pLeft = pExpr->pLeft; + Expr *pRight = pExpr->pRight; + assert( pLeft->op==TK_ID && pRight->op==TK_ID ); + if( 0==sqlite3_stricmp(pLeft->u.zToken, "old") + || 0==sqlite3_stricmp(pLeft->u.zToken, "new") + ){ + if( 0==sqlite3_stricmp(pRight->u.zToken, p->zOld) ){ + renameTokenFind(pWalker->pParse, p, (void*)pRight); + } } + }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){ + renameTokenFind(pWalker->pParse, p, (void*)pExpr); } return WRC_Continue; } -static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){ +static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ RenameToken *pBest = pCtx->pList; RenameToken *pToken; RenameToken **pp; @@ -989,7 +998,7 @@ static void renameColumnFunc( sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); - struct RenameCtx sCtx; + RenameCtx sCtx; const char *zSql = (const char*)sqlite3_value_text(argv[0]); int nSql = sqlite3_value_bytes(argv[0]); int bQuote = sqlite3_value_int(argv[2]); @@ -1019,7 +1028,9 @@ static void renameColumnFunc( rc = sqlite3RunParser(&sParse, zSql, &zErr); assert( sParse.pNewTable==0 || sParse.pNewIndex==0 ); if( db->mallocFailed ) rc = SQLITE_NOMEM; - if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 ){ + if( rc==SQLITE_OK + && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0 + ){ rc = SQLITE_CORRUPT_BKPT; } @@ -1067,10 +1078,9 @@ static void renameColumnFunc( int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; if( bFKOnly==0 ){ - sCtx.pList = renameTokenFind( - &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName + renameTokenFind( + &sParse, &sCtx, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName ); - sCtx.nList = 1; assert( sCtx.iCol>=0 ); if( sParse.pNewTable->iPKey==sCtx.iCol ){ sCtx.iCol = -1; @@ -1083,28 +1093,30 @@ static void renameColumnFunc( for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ for(i=0; inCol; i++){ - RenameToken *pTok = 0; if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){ - pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]); - if( pTok ){ - pTok->pNext = sCtx.pList; - sCtx.pList = pTok; - sCtx.nList++; - } + renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); } if( 0==sqlite3_stricmp(pFKey->zTo, zTable) && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld) ){ - pTok = renameTokenFind(&sParse, (void*)pFKey->aCol[i].zCol); - pTok->pNext = sCtx.pList; - sCtx.pList = pTok; - sCtx.nList++; + renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol); } } } - }else{ + }else if( sParse.pNewIndex ){ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); + }else{ + sCtx.zOld = zOld; + sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); + if( sParse.pNewTrigger->pColumns ){ + for(i=0; ipColumns->nId; i++){ + char *zName = sParse.pNewTrigger->pColumns->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } } assert( nQuot>=nNew ); @@ -1148,6 +1160,7 @@ renameColumnFunc_done: } sqlite3DeleteTable(db, sParse.pNewTable); if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex); + sqlite3DeleteTrigger(db, sParse.pNewTrigger); renameTokenFree(db, sParse.pRename); renameTokenFree(db, sCtx.pList); sqlite3ParserReset(&sParse); diff --git a/src/build.c b/src/build.c index f7a91025bb..66004294e1 100644 --- a/src/build.c +++ b/src/build.c @@ -3686,7 +3686,8 @@ void *sqlite3ArrayAllocate( ** ** A new IdList is returned, or NULL if malloc() fails. */ -IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){ +IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ + sqlite3 *db = pParse->db; int i; if( pList==0 ){ pList = sqlite3DbMallocZero(db, sizeof(IdList) ); @@ -3704,6 +3705,9 @@ IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){ return 0; } pList->a[i].zName = sqlite3NameFromToken(db, pToken); + if( IN_RENAME_COLUMN && pList->a[i].zName ){ + sqlite3RenameToken(pParse, (void*)pList->a[i].zName, pToken); + } return pList; } diff --git a/src/parse.y b/src/parse.y index 47e73feb8c..76e966a4da 100644 --- a/src/parse.y +++ b/src/parse.y @@ -908,9 +908,9 @@ insert_cmd(A) ::= REPLACE. {A = OE_Replace;} idlist_opt(A) ::= . {A = 0;} idlist_opt(A) ::= LP idlist(X) RP. {A = X;} idlist(A) ::= idlist(A) COMMA nm(Y). - {A = sqlite3IdListAppend(pParse->db,A,&Y);} + {A = sqlite3IdListAppend(pParse,A,&Y);} idlist(A) ::= nm(Y). - {A = sqlite3IdListAppend(pParse->db,0,&Y); /*A-overwrites-Y*/} + {A = sqlite3IdListAppend(pParse,0,&Y); /*A-overwrites-Y*/} /////////////////////////// Expression Processing ///////////////////////////// // diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7b01bb27f0..1c7ae4ddd2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3881,7 +3881,7 @@ void sqlite3FreeIndex(sqlite3*, Index*); #endif void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*); void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); -IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); +IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); diff --git a/src/tokenize.c b/src/tokenize.c index 685d10b252..02d21f1f58 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -697,9 +697,11 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ */ sqlite3DeleteTable(db, pParse->pNewTable); } + if( !IN_RENAME_COLUMN ){ + sqlite3DeleteTrigger(db, pParse->pNewTrigger); + } if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); - sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->pVList); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; diff --git a/src/trigger.c b/src/trigger.c index 044f256aeb..1df88fd809 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -181,14 +181,16 @@ void sqlite3BeginTrigger( goto trigger_cleanup; } assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){ - if( !noErr ){ - sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); - }else{ - assert( !db->init.busy ); - sqlite3CodeVerifySchema(pParse, iDb); + if( !IN_RENAME_COLUMN ){ + if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){ + if( !noErr ){ + sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); + } + goto trigger_cleanup; } - goto trigger_cleanup; } /* Do not create a trigger on a system table */ @@ -212,7 +214,7 @@ void sqlite3BeginTrigger( } #ifndef SQLITE_OMIT_AUTHORIZATION - { + if( !IN_RENAME_COLUMN ){ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int code = SQLITE_CREATE_TRIGGER; const char *zDb = db->aDb[iTabDb].zDbSName; @@ -246,8 +248,14 @@ void sqlite3BeginTrigger( pTrigger->pTabSchema = pTab->pSchema; pTrigger->op = (u8)op; pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; - pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); - pTrigger->pColumns = sqlite3IdListDup(db, pColumns); + if( IN_RENAME_COLUMN ){ + pTrigger->pWhen = pWhen; + pWhen = 0; + }else{ + pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); + } + pTrigger->pColumns = pColumns; + pColumns = 0; assert( pParse->pNewTrigger==0 ); pParse->pNewTrigger = pTrigger; @@ -296,6 +304,14 @@ void sqlite3FinishTrigger( goto triggerfinish_cleanup; } +#ifndef SQLITE_OMIT_ALTERTABLE + if( IN_RENAME_COLUMN ){ + assert( !db->init.busy ); + pParse->pNewTrigger = pTrig; + pTrig = 0; + }else +#endif + /* if we are not initializing, ** build the sqlite_master entry */ @@ -337,7 +353,7 @@ void sqlite3FinishTrigger( triggerfinish_cleanup: sqlite3DeleteTrigger(db, pTrig); - assert( !pParse->pNewTrigger ); + assert( IN_RENAME_COLUMN || !pParse->pNewTrigger ); sqlite3DeleteTriggerStep(db, pStepList); } diff --git a/test/altercol.test b/test/altercol.test index c2553fa505..b146ef9ef5 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -130,7 +130,7 @@ do_execsql_test 3.2 { SELECT * FROM t4; } {3 2 1} -# do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {} +do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {} #------------------------------------------------------------------------- # @@ -208,5 +208,32 @@ do_execsql_test 6.3 { SELECT "where" FROM blob; } {} +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 7.0 { + CREATE TABLE c(x); + INSERT INTO c VALUES(0); + CREATE TABLE t6("col a", "col b", "col c"); + CREATE TRIGGER zzz AFTER UPDATE OF "col a", "col c" ON t6 BEGIN + UPDATE c SET x=x+1; + END; +} + +do_execsql_test 7.1 { + INSERT INTO t6 VALUES(0, 0, 0); + UPDATE t6 SET "col c" = 1; + SELECT * FROM c; +} {1} + +do_execsql_test 7.2 { + ALTER TABLE t6 RENAME "col c" TO "col 3"; +} + +do_execsql_test 7.3 { + UPDATE t6 SET "col 3" = 0; + SELECT * FROM c; +} {2} + finish_test From 987db767d2adf3d6fcf8a3dff310cb59712b6be3 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 14 Aug 2018 20:18:50 +0000 Subject: [PATCH 2/7] Have ALTER TABLE RENAME edit column references in CREATE VIEW statements. FossilOrigin-Name: db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50 --- manifest | 39 +++++----- manifest.uuid | 2 +- src/alter.c | 183 +++++++++++++++++++++++++++++++++------------ src/build.c | 10 ++- src/insert.c | 117 +++++++++-------------------- src/os_unix.c | 104 +++++++++++++++++++------- src/prepare.c | 21 ++++-- src/sqliteInt.h | 8 +- src/vdbe.c | 7 +- src/vdbe.h | 3 - src/vdbeaux.c | 13 ---- test/altercol.test | 91 +++++++++++++++++++++- test/trigger7.test | 2 +- test/upsert1.test | 83 ++++++++++++++++++++ 14 files changed, 479 insertions(+), 204 deletions(-) diff --git a/manifest b/manifest index e874f7babd..f9fbb7fc81 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Edit\sthe\sWHEN\sand\sUPDATE\sOF\sclauses\sof\strigger\sprograms\sas\spart\sof\sALTER\sTABLE\nRENAME\sCOLUMN. -D 2018-08-13T17:14:26.913 +C Have\sALTER\sTABLE\sRENAME\sedit\scolumn\sreferences\sin\sCREATE\sVIEW\sstatements. +D 2018-08-14T20:18:50.875 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 9f8231841d00ef09d328ecb39af46e0cd85ce4e39d416319d458f8aff7af10dd +F src/alter.c 03a90ab39849f7f7ba3c637cc8fa663e2e0464b7fcb30c689db594b0a0db694e F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -442,7 +442,7 @@ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c 3f5e1a03db871e627bf5da21092bf7434ecfc5c5980bbd7d45eba13341340173 F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2 F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96 -F src/build.c 49cad074fcc2218f45e126375b4d8f7c3960d2fe2c3398bad862e1bfd640d7c4 +F src/build.c a2e61e716e7d90e382d71818404472207024ecb94a44431ac9fcf1ac3b8c3066 F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c b157b01081f92442f8b0218ddb93ddce8ebddad36dbddeecfdd771561dd4f387 @@ -459,7 +459,7 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 894594952bcda1dc6e1549871e4022517563545ffc7a3f4e9e5f3faa788893fd +F src/insert.c c723716f0de7aa0a679300f7d3541c89645f4a9882161cecdb3093fc07f8cc4b F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b F src/main.c df233667bbb6f05a8492ea93e0995abbeb816eab53e51e638a0dece1de0e83a3 @@ -482,7 +482,7 @@ F src/os.c 8aeb0b0f40f8f5b0da03fe49706695adaf42d2f516ab95abc72e86c245e119de F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 -F src/os_unix.c d9cf5ae0c79f31019d8325e8736c83914aeed64d8327a8d91a62b6439b748948 +F src/os_unix.c e681b2a3ab1085be3eb2e81254449782ca0bd0c38b73c48cb0c2480b8f2f25b9 F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 76d29b8a960dcb8b67210f095899d91e4a90673a6674ea58cfd1115b705a7fb9 @@ -493,7 +493,7 @@ F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 F src/pragma.c 873b767f233932e97cbffd094aa61928be90aca03f946a94bb29ce5695e4885b F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 -F src/prepare.c 540a24af1b579d9c3f9d37c381f84a1a91fbac656d16c88496a0b73c3e064a11 +F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9 F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c b51a48f33da36e0c2dd1ea5f0d10197c3e938a54086a69efce064ae41e2254e1 @@ -503,7 +503,7 @@ F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1 F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h 6a5d65117068808e2f37035d97a4cea6d55d679e02f9673b2b71ae6d5a0b1c8d +F src/sqliteInt.h c3bca346f053c0d2fe08f0813b0521597bbab11ff8195348372005606c309f84 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -569,11 +569,11 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157 F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855 -F src/vdbe.c a8dbb4cea2582818f8e253c904fec13ef1f1b6ff50517f7b8962a2ce9e5aad02 -F src/vdbe.h d93abdc8bc9295e0a256e582c19f548c545dc498319d108bbc9dd29de31c48a2 +F src/vdbe.c 18482b9a1ac73c62487dd034da342d464ab9b59cae2abfb4cb234ba8fa7ba16f +F src/vdbe.h 5081dcc497777efe5e9ebe7330d283a044a005e4bdda2e2e984f03bf89a0d907 F src/vdbeInt.h 8ea493d994c6697cf7bccc60583a80a0222560490410f60f1113e90d36643ce0 F src/vdbeapi.c 2ba821c5929a2769e4b217dd85843479c718b8989d414723ec8af0616a83d611 -F src/vdbeaux.c b610cef3d8d381c9287d02c2e61590acc0a1b4de1cc0188d560f5ef345527a24 +F src/vdbeaux.c f03d4a1961ec282abaec5dbf7e5576ddb1eb01e6157335a232d8d9e57fd5eca1 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191 F src/vdbemem.c 720df42ad8e5c7cb883573de40a185afef4a214903098a16f2bb14b62b2399b7 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f @@ -599,7 +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 b3263a3d663e57e35fcd888614cb2256df42ac1be07d40282c5c6023a47a431b +F test/altercol.test 98f1c88a9012ce2dbb8765f55557cac12473731a3bce31a8016e133407d235f5 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1502,7 +1502,7 @@ F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83 F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9 -F test/trigger7.test 799c9d2561facef70340cc9e0dee98aee9b5bdfe +F test/trigger7.test 93cfa9b48ab9104b2b3d87bc544ac8021405643e36f23ee84635fbfaf9b8fef5 F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4 F test/trigger9.test 2226ec795a33b0460ab5cf8891e9054cc7edef41 F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332 @@ -1528,7 +1528,7 @@ F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981 F test/update.test 1148de8d913e9817717990603aadeca07aab9ddbb10a30f167cbfd8d3a3ccb60 F test/update2.test 5e67667e1c54017d964e626db765cf8bedcf87483c184f4c575bdb8c1dd2313e -F test/upsert1.test ecd8f69e20183f298464992762fee24750508b3cbefc5badba8a259a3fc887ec +F test/upsert1.test 994bde41800bb77dbe32fcd2e1f6c4b49cc9f2c6cd345731c774dff02b51c110 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5 @@ -1756,11 +1756,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 32edc8920376aabb84ebe1900eaa9512d23f1b44d6459e4916dc6b07db66e27c -R 4f99214f849ec616e294c625bebc2173 -T *branch * edit-trigger-wrapper -T *sym-edit-trigger-wrapper * -T +closed 43c3d2d707a7c54dcb75660be9347d6bd79c4844f81f6ed73446570876f5f72b -T -sym-alter-table-rename-column * +P 5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326 +R 9599d9a5f29aa1850ea700a8d90d37c9 +T +closed 4b575b8050cdb703df26d28228a60b88f60e5279453ba71c65e0739575609a0b U dan -Z e89da0f8ef0add6d1c95dbb889527ec9 +Z 1ded9b508d80d9b5a23b7f7d1204945e diff --git a/manifest.uuid b/manifest.uuid index d9332dde87..7b219479fc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5fdb6b0aafba727139e1937ef5950e4434a77f95a10fc46f8010ca2de3922326 \ No newline at end of file +db829dc1a2d7afa49798a2fd32d1f070185b23e513416e65d8144fda24f23b50 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 5dbd42a28c..29ef9e602e 100644 --- a/src/alter.c +++ b/src/alter.c @@ -844,11 +844,14 @@ void sqlite3AlterRenameColumn( bQuote = sqlite3Isquote(pNew->z[0]); sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " - "sql = sqlite_rename_column(sql, %d, %d, %Q, %Q, %Q) " + "sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) " "WHERE name NOT LIKE 'sqlite_%%' AND (" - " type = 'table' OR (type IN ('index', 'trigger') AND tbl_name = %Q)" + " type IN ('table', 'view') " + " OR (type IN ('index', 'trigger') AND tbl_name = %Q)" ")", - zDb, MASTER_NAME, iCol, bQuote, zNew, pTab->zName, zOld, pTab->zName + zDb, MASTER_NAME, + zDb, pTab->zName, iCol, zNew, bQuote, + pTab->zName ); /* Drop and reload the database schema. */ @@ -896,6 +899,7 @@ struct RenameCtx { RenameToken *pList; /* List of tokens to overwrite */ int nList; /* Number of tokens in pList */ int iCol; /* Index of column being renamed */ + Table *pTab; /* Table being ALTERed */ const char *zOld; /* Old column name */ }; @@ -942,7 +946,13 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){ } } -static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, void *pPtr){ +/* +** Search the Parse object passed as the first argument for a RenameToken +** object associated with parse tree element pPtr. If found, remove it +** from the Parse object and add it to the list maintained by the +** RenameCtx object passed as the second argument. +*/ +static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ RenameToken **pp; for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){ if( (*pp)->p==pPtr ){ @@ -956,6 +966,20 @@ static void renameTokenFind(Parse *pParse, RenameCtx *pCtx, void *pPtr){ } } +static int renameColumnSelectCb(Walker *pWalker, Select *p){ + return WRC_Continue; +} + + +/* +** This is a Walker expression callback. +** +** For every TK_COLUMN node in the expression tree, search to see +** if the column being references is the column being renamed by an +** ALTER TABLE statement. If it is, then attach its associated +** RenameToken object to the list of RenameToken objects being +** constructed in RenameCtx object at pWalker->u.pRename. +*/ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; if( p->zOld && pExpr->op==TK_DOT ){ @@ -969,12 +993,23 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ renameTokenFind(pWalker->pParse, p, (void*)pRight); } } - }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){ + }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol + && (p->pTab==0 || p->pTab==pExpr->pTab) + ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); } return WRC_Continue; } +/* +** The RenameCtx contains a list of tokens that reference a column that +** is being renamed by an ALTER TABLE statement. Return the "first" +** RenameToken in the RenameCtx and remove that RenameToken from the +** RenameContext. "First" means the first RenameToken encountered when +** the input SQL from left to right. Repeated calls to this routine +** return all column name tokens in the order that they are encountered +** in the SQL statement. +*/ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ RenameToken *pBest = pCtx->pList; RenameToken *pToken; @@ -990,7 +1025,31 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ } /* -** sqlite_rename_column(SQL, iCol, bQuote, zNew, zTable, zOld) +** SQL function: +** +** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) +** +** 0. zSql: SQL statement to rewrite +** 1. Database: Database name (e.g. "main") +** 2. Table: Table name +** 3. iCol: Index of column to rename +** 4. zNew: New column name +** 5. bQuote: True if the new column name should be quoted +** +** Do a column rename operation on the CREATE statement given in zSql. +** The iCol-th column (left-most is 0) of table zTable is renamed from zCol +** into zNew. The name should be quoted if bQuote is true. +** +** This function is used internally by the ALTER TABLE RENAME COLUMN command. +** Though accessible to application code, it is not intended for use by +** applications. The existance of this function, and the way it works, +** is subject to change without notice. +** +** If any of the parameters are out-of-bounds, then simply return NULL. +** An out-of-bounds parameter can only occur when the application calls +** this function directly. The parameters will always be well-formed when +** this routine is invoked by the bytecode for a legitimate ALTER TABLE +** statement. */ static void renameColumnFunc( sqlite3_context *context, @@ -1001,11 +1060,13 @@ static void renameColumnFunc( RenameCtx sCtx; const char *zSql = (const char*)sqlite3_value_text(argv[0]); int nSql = sqlite3_value_bytes(argv[0]); - int bQuote = sqlite3_value_int(argv[2]); - const char *zNew = (const char*)sqlite3_value_text(argv[3]); - int nNew = sqlite3_value_bytes(argv[3]); - const char *zTable = (const char*)sqlite3_value_text(argv[4]); - const char *zOld = (const char*)sqlite3_value_text(argv[5]); + const char *zDb = (const char*)sqlite3_value_text(argv[1]); + const char *zTable = (const char*)sqlite3_value_text(argv[2]); + int iCol = sqlite3_value_int(argv[3]); + const char *zNew = (const char*)sqlite3_value_text(argv[4]); + int nNew = sqlite3_value_bytes(argv[4]); + int bQuote = sqlite3_value_int(argv[5]); + const char *zOld; int rc; char *zErr = 0; @@ -1017,9 +1078,17 @@ static void renameColumnFunc( char *zQuot = 0; /* Quoted version of zNew */ int nQuot = 0; /* Length of zQuot in bytes */ int i; + Table *pTab; + if( zSql==0 ) return; + if( zNew==0 ) return; + if( zTable==0 ) return; + if( iCol<0 ) return; + pTab = sqlite3FindTable(db, zTable, zDb); + if( pTab==0 || iCol>=pTab->nCol ) return; + zOld = pTab->aCol[iCol].zName; memset(&sCtx, 0, sizeof(sCtx)); - sCtx.iCol = sqlite3_value_int(argv[1]); + sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); memset(&sParse, 0, sizeof(sParse)); sParse.eParseMode = PARSE_MODE_RENAME_COLUMN; @@ -1043,16 +1112,6 @@ static void renameColumnFunc( } } - if( rc!=SQLITE_OK ){ - if( zErr ){ - sqlite3_result_error(context, zErr, -1); - }else{ - sqlite3_result_error_code(context, rc); - } - sqlite3DbFree(db, zErr); - goto renameColumnFunc_done; - } - if( bQuote ){ zNew = zQuot; nNew = nQuot; @@ -1060,7 +1119,7 @@ static void renameColumnFunc( #ifdef SQLITE_DEBUG assert( sqlite3Strlen30(zSql)==nSql ); - { + if( rc==SQLITE_OK ){ RenameToken *pToken; for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){ assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] ); @@ -1072,34 +1131,55 @@ static void renameColumnFunc( memset(&sWalker, 0, sizeof(Walker)); sWalker.pParse = &sParse; sWalker.xExprCallback = renameColumnExprCb; + sWalker.xSelectCallback = renameColumnSelectCb; sWalker.u.pRename = &sCtx; + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; if( sParse.pNewTable ){ - int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); - FKey *pFKey; - if( bFKOnly==0 ){ - renameTokenFind( - &sParse, &sCtx, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName - ); - assert( sCtx.iCol>=0 ); - if( sParse.pNewTable->iPKey==sCtx.iCol ){ - sCtx.iCol = -1; + Select *pSelect = sParse.pNewTable->pSelect; + if( pSelect ){ + sCtx.pTab = pTab; + sParse.rc = SQLITE_OK; + sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0); + rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); + if( rc==SQLITE_OK ){ + sqlite3WalkSelect(&sWalker, pSelect); + }else if( rc==SQLITE_ERROR ){ + /* Failed to resolve all symboles in the view. This is not an + ** error, but it will not be edited. */ + sqlite3DbFree(db, sParse.zErrMsg); + sParse.zErrMsg = 0; + rc = SQLITE_OK; } - 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; inCol; i++){ - if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.iCol ){ - renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]); + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; + }else{ + /* A regular table */ + int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); + FKey *pFKey; + assert( sParse.pNewTable->pSelect==0 ); + if( bFKOnly==0 ){ + renameTokenFind( + &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName + ); + if( sCtx.iCol<0 ){ + renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey); } - if( 0==sqlite3_stricmp(pFKey->zTo, zTable) - && 0==sqlite3_stricmp(pFKey->aCol[i].zCol, zOld) - ){ - renameTokenFind(&sParse, &sCtx, (void*)pFKey->aCol[i].zCol); + 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; inCol; i++){ + if( bFKOnly==0 && pFKey->aCol[i].iFrom==sCtx.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); + } } } } @@ -1119,6 +1199,7 @@ static void renameColumnFunc( } } + assert( rc==SQLITE_OK ); assert( nQuot>=nNew ); zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1); if( zOut ){ @@ -1152,9 +1233,19 @@ static void renameColumnFunc( sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); sqlite3DbFree(db, zOut); + }else{ + rc = SQLITE_NOMEM; } renameColumnFunc_done: + if( rc!=SQLITE_OK ){ + if( zErr ){ + sqlite3_result_error(context, zErr, -1); + }else{ + sqlite3_result_error_code(context, rc); + } + } + if( sParse.pVdbe ){ sqlite3VdbeFinalize(sParse.pVdbe); } diff --git a/src/build.c b/src/build.c index 66004294e1..68fecd1039 100644 --- a/src/build.c +++ b/src/build.c @@ -1370,6 +1370,9 @@ void sqlite3AddPrimaryKey( && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0 && sortOrder!=SQLITE_SO_DESC ){ + if( IN_RENAME_COLUMN && pList ){ + sqlite3MoveRenameToken(pParse, &pTab->iPKey, pList->a[0].pExpr); + } pTab->iPKey = iCol; pTab->keyConf = (u8)onError; assert( autoInc==0 || autoInc==1 ); @@ -2172,7 +2175,12 @@ void sqlite3CreateView( ** allocated rather than point to the input string - which means that ** they will persist after the current sqlite3_exec() call returns. */ - p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + p->pSelect = pSelect; + pSelect = 0; + }else{ + p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + } p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE); if( db->mallocFailed ) goto create_view_fail; diff --git a/src/insert.c b/src/insert.c index 16971a044b..12a6bb805e 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1178,44 +1178,6 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ return !w.eCode; } -/* -** An instance of the ConstraintAddr object remembers the byte-code addresses -** for sections of the constraint checks that deal with uniqueness constraints -** on the rowid and on the upsert constraint. -** -** This information is passed into checkReorderConstraintChecks() to insert -** some OP_Goto operations so that the rowid and upsert constraints occur -** in the correct order relative to other constraints. -*/ -typedef struct ConstraintAddr ConstraintAddr; -struct ConstraintAddr { - int ipkTop; /* Subroutine for rowid constraint check */ - int upsertTop; /* Label for upsert constraint check subroutine */ - int upsertTop2; /* Copy of upsertTop not cleared by the call */ - int upsertBtm; /* upsert constraint returns to this label */ - int ipkBtm; /* Return opcode rowid constraint check */ -}; - -/* -** Generate any OP_Goto operations needed to cause constraints to be -** run that haven't already been run. -*/ -static void reorderConstraintChecks(Vdbe *v, ConstraintAddr *p){ - if( p->upsertTop ){ - testcase( sqlite3VdbeLabelHasBeenResolved(v, p->upsertTop) ); - sqlite3VdbeGoto(v, p->upsertTop); - VdbeComment((v, "call upsert subroutine")); - sqlite3VdbeResolveLabel(v, p->upsertBtm); - p->upsertTop = 0; - } - if( p->ipkTop ){ - sqlite3VdbeGoto(v, p->ipkTop); - VdbeComment((v, "call rowid unique-check subroutine")); - sqlite3VdbeJumpHere(v, p->ipkBtm); - p->ipkTop = 0; - } -} - /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE ** on table pTab. @@ -1325,11 +1287,13 @@ void sqlite3GenerateConstraintChecks( int addr1; /* Address of jump instruction */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */ - ConstraintAddr sAddr;/* Address information for constraint reordering */ Index *pUpIdx = 0; /* Index to which to apply the upsert */ u8 isUpdate; /* True if this is an UPDATE operation */ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */ + int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */ + int ipkTop = 0; /* Top of the IPK uniqueness check */ + int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */ isUpdate = regOldData!=0; db = pParse->db; @@ -1337,7 +1301,6 @@ void sqlite3GenerateConstraintChecks( assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; - memset(&sAddr, 0, sizeof(sAddr)); /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for ** normal rowid tables. nPkField is the number of key fields in the @@ -1441,8 +1404,8 @@ void sqlite3GenerateConstraintChecks( /* UNIQUE and PRIMARY KEY constraints should be handled in the following ** order: ** - ** (1) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore - ** (2) OE_Update + ** (1) OE_Update + ** (2) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore ** (3) OE_Replace ** ** OE_Fail and OE_Ignore must happen before any changes are made. @@ -1451,6 +1414,11 @@ void sqlite3GenerateConstraintChecks( ** could happen in any order, but they are grouped up front for ** convenience. ** + ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43 + ** The order of constraints used to have OE_Update as (2) and OE_Abort + ** and so forth as (1). But apparently PostgreSQL checks the OE_Update + ** constraint before any others, so it had to be moved. + ** ** Constraint checking code is generated in this order: ** (A) The rowid constraint ** (B) Unique index constraints that do not have OE_Replace as their @@ -1470,11 +1438,10 @@ void sqlite3GenerateConstraintChecks( overrideError = OE_Ignore; pUpsert = 0; }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){ - /* If the constraint-target is on some column other than - ** then ROWID, then we might need to move the UPSERT around - ** so that it occurs in the correct order. */ - sAddr.upsertTop = sAddr.upsertTop2 = sqlite3VdbeMakeLabel(v); - sAddr.upsertBtm = sqlite3VdbeMakeLabel(v); + /* If the constraint-target uniqueness check must be run first. + ** Jump to that uniqueness check now */ + upsertJump = sqlite3VdbeAddOp0(v, OP_Goto); + VdbeComment((v, "UPSERT constraint goes first")); } } @@ -1506,16 +1473,12 @@ void sqlite3GenerateConstraintChecks( ** to defer the running of the rowid conflict checking until after ** the UNIQUE constraints have run. */ - assert( OE_Update>OE_Replace ); - assert( OE_Ignore=OE_Replace - && (pUpsert || onError!=overrideError) - && pTab->pIndex + if( onError==OE_Replace /* IPK rule is REPLACE */ + && onError!=overrideError /* Rules for other contraints are different */ + && pTab->pIndex /* There exist other constraints */ ){ - sAddr.ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; + ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; + VdbeComment((v, "defer IPK REPLACE until last")); } if( isUpdate ){ @@ -1610,9 +1573,9 @@ void sqlite3GenerateConstraintChecks( } } sqlite3VdbeResolveLabel(v, addrRowidOk); - if( sAddr.ipkTop ){ - sAddr.ipkBtm = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, sAddr.ipkTop-1); + if( ipkTop ){ + ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); + sqlite3VdbeJumpHere(v, ipkTop-1); } } @@ -1630,18 +1593,18 @@ void sqlite3GenerateConstraintChecks( int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ - if( bAffinityDone==0 ){ - sqlite3TableAffinity(v, pTab, regNewData+1); - bAffinityDone = 1; - } if( pUpIdx==pIdx ){ - addrUniqueOk = sAddr.upsertBtm; + addrUniqueOk = upsertJump+1; upsertBypass = sqlite3VdbeGoto(v, 0); VdbeComment((v, "Skip upsert subroutine")); - sqlite3VdbeResolveLabel(v, sAddr.upsertTop2); + sqlite3VdbeJumpHere(v, upsertJump); }else{ addrUniqueOk = sqlite3VdbeMakeLabel(v); } + if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ + sqlite3TableAffinity(v, pTab, regNewData+1); + bAffinityDone = 1; + } VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName)); iThisCur = iIdxCur+ix; @@ -1713,15 +1676,6 @@ void sqlite3GenerateConstraintChecks( } } - /* Invoke subroutines to handle IPK replace and upsert prior to running - ** the first REPLACE constraint check. */ - if( onError==OE_Replace ){ - testcase( sAddr.ipkTop ); - testcase( sAddr.upsertTop - && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) ); - reorderConstraintChecks(v, &sAddr); - } - /* Collision detection may be omitted if all of the following are true: ** (1) The conflict resolution algorithm is REPLACE ** (2) The table is a WITHOUT ROWID table @@ -1843,18 +1797,21 @@ void sqlite3GenerateConstraintChecks( } } if( pUpIdx==pIdx ){ + sqlite3VdbeGoto(v, upsertJump+1); sqlite3VdbeJumpHere(v, upsertBypass); }else{ sqlite3VdbeResolveLabel(v, addrUniqueOk); } if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); - } - testcase( sAddr.ipkTop!=0 ); - testcase( sAddr.upsertTop - && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) ); - reorderConstraintChecks(v, &sAddr); - + + /* If the IPK constraint is a REPLACE, run it last */ + if( ipkTop ){ + sqlite3VdbeGoto(v, ipkTop+1); + VdbeComment((v, "Do IPK REPLACE")); + sqlite3VdbeJumpHere(v, ipkBottom); + } + *pbMayReplace = seenReplace; VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace)); } diff --git a/src/os_unix.c b/src/os_unix.c index aacca93d14..ab0a5d919a 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -702,12 +702,25 @@ static int robust_open(const char *z, int f, mode_t m){ ** unixEnterMutex() ** assert( unixMutexHeld() ); ** unixEnterLeave() +** +** To prevent deadlock, the global unixBigLock must must be acquired +** before the unixInodeInfo.pLockMutex mutex, if both are held. It is +** OK to get the pLockMutex without holding unixBigLock first, but if +** that happens, the unixBigLock mutex must not be acquired until after +** pLockMutex is released. +** +** OK: enter(unixBigLock), enter(pLockInfo) +** OK: enter(unixBigLock) +** OK: enter(pLockInfo) +** ERROR: enter(pLockInfo), enter(unixBigLock) */ static sqlite3_mutex *unixBigLock = 0; static void unixEnterMutex(void){ + assert( sqlite3_mutex_notheld(unixBigLock) ); /* Not a recursive mutex */ sqlite3_mutex_enter(unixBigLock); } static void unixLeaveMutex(void){ + assert( sqlite3_mutex_held(unixBigLock) ); sqlite3_mutex_leave(unixBigLock); } #ifdef SQLITE_DEBUG @@ -1111,9 +1124,9 @@ struct unixFileId { ** ** Mutex rules: ** -** (1) The pLockMutex mutex must be held in order to read or write +** (1) Only the pLockMutex mutex must be held in order to read or write ** any of the locking fields: -** nShared, nLock, eFileLock, or bProcessLock +** nShared, nLock, eFileLock, bProcessLock, pUnused ** ** (2) When nRef>0, then the following fields are unchanging and can ** be read (but not written) without holding any mutex: @@ -1121,6 +1134,10 @@ struct unixFileId { ** ** (3) With the exceptions above, all the fields may only be read ** or written while holding the global unixBigLock mutex. +** +** Deadlock prevention: The global unixBigLock mutex may not +** be acquired while holding the pLockMutex mutex. If both unixBigLock +** and pLockMutex are needed, then unixBigLock must be acquired first. */ struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ @@ -1129,9 +1146,9 @@ struct unixInodeInfo { int nLock; /* Number of outstanding file locks */ unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ unsigned char bProcessLock; /* An exclusive process lock is held */ + UnixUnusedFd *pUnused; /* Unused file descriptors to close */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ - UnixUnusedFd *pUnused; /* Unused file descriptors to close */ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ unixInodeInfo *pPrev; /* .... doubly linked */ #if SQLITE_ENABLE_LOCKING_STYLE @@ -1147,7 +1164,21 @@ struct unixInodeInfo { ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* All unixInodeInfo objects */ -static unsigned int nUnusedFd = 0; /* Total unused file descriptors */ + +#ifdef SQLITE_DEBUG +/* +** True if the inode mutex is held, or not. Used only within assert() +** to help verify correct mutex usage. +*/ +int unixFileMutexHeld(unixFile *pFile){ + assert( pFile->pInode ); + return sqlite3_mutex_held(pFile->pInode->pLockMutex); +} +int unixFileMutexNotheld(unixFile *pFile){ + assert( pFile->pInode ); + return sqlite3_mutex_notheld(pFile->pInode->pLockMutex); +} +#endif /* ** @@ -1253,11 +1284,11 @@ static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p; UnixUnusedFd *pNext; + assert( unixFileMutexHeld(pFile) ); for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; robust_close(pFile, p->fd, __LINE__); sqlite3_free(p); - nUnusedFd--; } pInode->pUnused = 0; } @@ -1271,11 +1302,14 @@ static void closePendingFds(unixFile *pFile){ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); + assert( unixFileMutexNotheld(pFile) ); if( ALWAYS(pInode) ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); + sqlite3_mutex_enter(pInode->pLockMutex); closePendingFds(pFile); + sqlite3_mutex_leave(pInode->pLockMutex); if( pInode->pPrev ){ assert( pInode->pPrev->pNext==pInode ); pInode->pPrev->pNext = pInode->pNext; @@ -1291,7 +1325,6 @@ static void releaseInodeInfo(unixFile *pFile){ sqlite3_free(pInode); } } - assert( inodeList!=0 || nUnusedFd==0 ); } /* @@ -1361,7 +1394,6 @@ static int findInodeInfo( #else fileId.ino = (u64)statbuf.st_ino; #endif - assert( inodeList!=0 || nUnusedFd==0 ); pInode = inodeList; while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ pInode = pInode->pNext; @@ -1826,11 +1858,11 @@ end_lock: static void setPendingFd(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p = pFile->pPreallocatedUnused; + assert( unixFileMutexHeld(pFile) ); p->pNext = pInode->pUnused; pInode->pUnused = p; pFile->h = -1; pFile->pPreallocatedUnused = 0; - nUnusedFd++; } /* @@ -1988,14 +2020,14 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ */ pInode->nLock--; assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ){ - closePendingFds(pFile); - } + if( pInode->nLock==0 ) closePendingFds(pFile); } end_unlock: sqlite3_mutex_leave(pInode->pLockMutex); - if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; + } return rc; } @@ -2066,15 +2098,20 @@ static int closeUnixFile(sqlite3_file *id){ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; unixFile *pFile = (unixFile *)id; + unixInodeInfo *pInode = pFile->pInode; + + assert( pInode!=0 ); verifyDbFile(pFile); unixUnlock(id, NO_LOCK); + assert( unixFileMutexNotheld(pFile) ); unixEnterMutex(); /* unixFile.pInode is always valid here. Otherwise, a different close ** routine (e.g. nolockClose()) would be called instead. */ assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); - if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ + sqlite3_mutex_enter(pInode->pLockMutex); + if( pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->pUnused list. It will be automatically closed @@ -2082,6 +2119,7 @@ static int unixClose(sqlite3_file *id){ */ setPendingFd(pFile); } + sqlite3_mutex_leave(pInode->pLockMutex); releaseInodeInfo(pFile); rc = closeUnixFile(id); unixLeaveMutex(); @@ -2679,6 +2717,7 @@ static int semXClose(sqlite3_file *id) { unixFile *pFile = (unixFile*)id; semXUnlock(id, NO_LOCK); assert( pFile ); + assert( unixFileMutexNotheld(pFile) ); unixEnterMutex(); releaseInodeInfo(pFile); unixLeaveMutex(); @@ -3119,14 +3158,14 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { if( rc==SQLITE_OK ){ pInode->nLock--; assert( pInode->nLock>=0 ); - if( pInode->nLock==0 ){ - closePendingFds(pFile); - } + if( pInode->nLock==0 ) closePendingFds(pFile); } } sqlite3_mutex_leave(pInode->pLockMutex); - if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; + if( rc==SQLITE_OK ){ + pFile->eFileLock = eFileLock; + } return rc; } @@ -3138,14 +3177,20 @@ static int afpClose(sqlite3_file *id) { unixFile *pFile = (unixFile*)id; assert( id!=0 ); afpUnlock(id, NO_LOCK); + assert( unixFileMutexNotheld(pFile) ); unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->aPending. It will be automatically closed when - ** the last lock is cleared. - */ - setPendingFd(pFile); + if( pFile->pInode ){ + unixInodeInfo *pInode = pFile->pInode; + sqlite3_mutex_enter(pInode->pLockMutex); + if( pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->aPending. It will be automatically closed when + ** the last lock is cleared. + */ + setPendingFd(pFile); + } + sqlite3_mutex_leave(pInode->pLockMutex); } releaseInodeInfo(pFile); sqlite3_free(pFile->lockingContext); @@ -4451,6 +4496,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ /* Check to see if a unixShmNode object already exists. Reuse an existing ** one if present. Create a new one if necessary. */ + assert( unixFileMutexNotheld(pDbFd) ); unixEnterMutex(); pInode = pDbFd->pInode; pShmNode = pInode->pShmNode; @@ -4833,6 +4879,7 @@ static void unixShmBarrier( ){ UNUSED_PARAMETER(fd); sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ + assert( unixFileMutexNotheld((unixFile*)fd) ); unixEnterMutex(); /* Also mutex, for redundancy */ unixLeaveMutex(); } @@ -4874,6 +4921,7 @@ static int unixShmUnmap( /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ + assert( unixFileMutexNotheld(pDbFd) ); unixEnterMutex(); assert( pShmNode->nRef>0 ); pShmNode->nRef--; @@ -5200,7 +5248,7 @@ IOMETHODS( IOMETHODS( nolockIoFinder, /* Finder function name */ nolockIoMethods, /* sqlite3_io_methods object name */ - 3, /* shared memory is disabled */ + 3, /* shared memory and mmap are enabled */ nolockClose, /* xClose method */ nolockLock, /* xLock method */ nolockUnlock, /* xUnlock method */ @@ -5696,7 +5744,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ ** ** Even if a subsequent open() call does succeed, the consequences of ** not searching for a reusable file descriptor are not dire. */ - if( nUnusedFd>0 && 0==osStat(zPath, &sStat) ){ + if( inodeList!=0 && 0==osStat(zPath, &sStat) ){ unixInodeInfo *pInode; pInode = inodeList; @@ -5706,12 +5754,14 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ } if( pInode ){ UnixUnusedFd **pp; + assert( sqlite3_mutex_notheld(pInode->pLockMutex) ); + sqlite3_mutex_enter(pInode->pLockMutex); for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); pUnused = *pp; if( pUnused ){ - nUnusedFd--; *pp = pUnused->pNext; } + sqlite3_mutex_leave(pInode->pLockMutex); } } unixLeaveMutex(); diff --git a/src/prepare.c b/src/prepare.c index 14017d8792..4d4b98d8a9 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -25,15 +25,23 @@ static void corruptSchema( const char *zExtra /* Error information */ ){ sqlite3 *db = pData->db; - if( !db->mallocFailed && (db->flags & SQLITE_WriteSchema)==0 ){ + if( db->mallocFailed ){ + pData->rc = SQLITE_NOMEM_BKPT; + }else if( pData->pzErrMsg[0]!=0 ){ + /* A error message has already been generated. Do not overwrite it */ + }else if( pData->mInitFlags & INITFLAG_AlterTable ){ + *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra); + pData->rc = SQLITE_ERROR; + }else if( db->flags & SQLITE_WriteSchema ){ + pData->rc = SQLITE_CORRUPT_BKPT; + }else{ char *z; if( zObj==0 ) zObj = "?"; z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj); if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra); - sqlite3DbFree(db, *pData->pzErrMsg); *pData->pzErrMsg = z; + pData->rc = SQLITE_CORRUPT_BKPT; } - pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT; } /* @@ -132,7 +140,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){ ** auxiliary databases. Return one of the SQLITE_ error codes to ** indicate success or failure. */ -int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ +int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ int rc; int i; #ifndef SQLITE_OMIT_DEPRECATED @@ -167,6 +175,7 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ initData.iDb = iDb; initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; + initData.mInitFlags = mFlags; sqlite3InitCallback(&initData, 3, (char **)azArg, 0); if( initData.rc ){ rc = initData.rc; @@ -373,14 +382,14 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ assert( db->nDb>0 ); /* Do the main schema first */ if( !DbHasProperty(db, 0, DB_SchemaLoaded) ){ - rc = sqlite3InitOne(db, 0, pzErrMsg); + rc = sqlite3InitOne(db, 0, pzErrMsg, 0); if( rc ) return rc; } /* All other schemas after the main schema. The "temp" schema must be last */ for(i=db->nDb-1; i>0; i--){ assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) ); if( !DbHasProperty(db, i, DB_SchemaLoaded) ){ - rc = sqlite3InitOne(db, i, pzErrMsg); + rc = sqlite3InitOne(db, i, pzErrMsg, 0); if( rc ) return rc; } } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1c7ae4ddd2..f2e6121de8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3329,8 +3329,14 @@ typedef struct { char **pzErrMsg; /* Error message stored here */ int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ + u32 mInitFlags; /* Flags controlling error messages */ } InitData; +/* +** Allowed values for mInitFlags +*/ +#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */ + /* ** Structure containing global configuration data for the SQLite library. ** @@ -3801,7 +3807,7 @@ void sqlite3ExprListDelete(sqlite3*, ExprList*); u32 sqlite3ExprListFlags(const ExprList*); int sqlite3Init(sqlite3*, char**); int sqlite3InitCallback(void*, int, char**, char**); -int sqlite3InitOne(sqlite3*, int, char**); +int sqlite3InitOne(sqlite3*, int, char**, u32); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); #ifndef SQLITE_OMIT_VIRTUALTABLE Module *sqlite3PragmaVtabRegister(sqlite3*,const char *zName); diff --git a/src/vdbe.c b/src/vdbe.c index 62abaa3013..c8ee9860f8 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -5722,7 +5722,8 @@ case OP_SqlExec: { /* Opcode: ParseSchema P1 * * P4 * ** ** Read and parse all entries from the SQLITE_MASTER table of database P1 -** that match the WHERE clause P4. +** that match the WHERE clause P4. If P4 is a NULL pointer, then the +** entire schema for P1 is reparsed. ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. @@ -5751,11 +5752,11 @@ case OP_ParseSchema: { if( pOp->p4.z==0 ){ sqlite3SchemaClear(db->aDb[iDb].pSchema); db->mDbFlags &= ~DBFLAG_SchemaKnownOk; - rc = sqlite3InitOne(db, iDb, &p->zErrMsg); + rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); db->mDbFlags |= DBFLAG_SchemaChange; }else #endif - /* Used to be a conditional */ { + { zMaster = MASTER_NAME; initData.db = db; initData.iDb = pOp->p1; diff --git a/src/vdbe.h b/src/vdbe.h index 183242a7be..d42acc0cca 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -238,9 +238,6 @@ void sqlite3VdbeClearObject(sqlite3*,Vdbe*); void sqlite3VdbeMakeReady(Vdbe*,Parse*); int sqlite3VdbeFinalize(Vdbe*); void sqlite3VdbeResolveLabel(Vdbe*, int); -#ifdef SQLITE_COVERAGE_TEST - int sqlite3VdbeLabelHasBeenResolved(Vdbe*,int); -#endif int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG int sqlite3VdbeAssertMayAbort(Vdbe *, int); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index c8b61ba222..68784d5fa9 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -437,19 +437,6 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){ } } -#ifdef SQLITE_COVERAGE_TEST -/* -** Return TRUE if and only if the label x has already been resolved. -** Return FALSE (zero) if label x is still unresolved. -** -** This routine is only used inside of testcase() macros, and so it -** only exists when measuring test coverage. -*/ -int sqlite3VdbeLabelHasBeenResolved(Vdbe *v, int x){ - return v->pParse->aLabel && v->pParse->aLabel[ADDR(x)]>=0; -} -#endif /* SQLITE_COVERAGE_TEST */ - /* ** Mark the VDBE as one that can only be run one time. */ diff --git a/test/altercol.test b/test/altercol.test index b146ef9ef5..bd8e245cbd 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -66,6 +66,21 @@ foreach {tn before after} { 13 {CREATE TABLE t1(a, b, c, FOREIGN KEY (b) REFERENCES t2)} {CREATE TABLE t1(a, d, c, FOREIGN KEY (d) REFERENCES t2)} + 15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))} + {CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))} + + 16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)} + {CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)} + + 14 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB, PRIMARY KEY(b))} + {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB, PRIMARY KEY(d))} + + 15 {CREATE TABLE t1(a INTEGER, b INTEGER, c BLOB, PRIMARY KEY(b))} + {CREATE TABLE t1(a INTEGER, d INTEGER, c BLOB, PRIMARY KEY(d))} + + 16 {CREATE TABLE t1(a INTEGER, b INTEGER PRIMARY KEY, c BLOB)} + {CREATE TABLE t1(a INTEGER, d INTEGER PRIMARY KEY, c BLOB)} + } { reset_db do_execsql_test 1.$tn.0 $before @@ -209,6 +224,7 @@ do_execsql_test 6.3 { } {} #------------------------------------------------------------------------- +# Triggers. # reset_db do_execsql_test 7.0 { @@ -235,5 +251,78 @@ do_execsql_test 7.3 { SELECT * FROM c; } {2} -finish_test +#------------------------------------------------------------------------- +# Views. +# +reset_db +do_execsql_test 8.0 { + CREATE TABLE a1(x INTEGER, y TEXT, z BLOB, PRIMARY KEY(x)); + CREATE TABLE a2(a, b, c); + CREATE VIEW v1 AS SELECT x, y, z FROM a1; +} +do_execsql_test 8.1 { + ALTER TABLE a1 RENAME y TO yyy; + SELECT sql FROM sqlite_master WHERE type='view'; +} {{CREATE VIEW v1 AS SELECT x, yyy, z FROM a1}} + +do_execsql_test 8.2.1 { + DROP VIEW v1; + CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2; +} {} +do_execsql_test 8.2.2 { + ALTER TABLE a1 RENAME x TO xxx; +} +do_execsql_test 8.2.3 { + SELECT sql FROM sqlite_master WHERE type='view'; +} {{CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2}} + +do_execsql_test 8.3.1 { + DROP TABLE a2; + DROP VIEW v2; + CREATE TABLE a2(a INTEGER PRIMARY KEY, b, c); + CREATE VIEW v2 AS SELECT xxx, xxx+xxx, a, a+a FROM a1, a2; +} {} +do_execsql_test 8.3.2 { + ALTER TABLE a1 RENAME xxx TO x; +} +do_execsql_test 8.3.3 { + SELECT sql FROM sqlite_master WHERE type='view'; +} {{CREATE VIEW v2 AS SELECT x, x+x, a, a+a FROM a1, a2}} + +do_execsql_test 8.4.0 { + CREATE TABLE b1(a, b, c); + CREATE TABLE b2(x, y, z); +} + +do_execsql_test 8.4.1 { + CREATE VIEW vvv AS SELECT c+c || coalesce(c, c) FROM b1, b2 WHERE x=c GROUP BY c HAVING c>0; + ALTER TABLE b1 RENAME c TO "a;b"; + SELECT sql FROM sqlite_master WHERE name='vvv'; +} {{CREATE VIEW vvv AS SELECT "a;b"+"a;b" || coalesce("a;b", "a;b") FROM b1, b2 WHERE x="a;b" GROUP BY "a;b" HAVING "a;b">0}} + +do_execsql_test 8.4.2 { + CREATE VIEW www AS SELECT b FROM b1 UNION ALL SELECT y FROM b2; + ALTER TABLE b1 RENAME b TO bbb; + SELECT sql FROM sqlite_master WHERE name='www'; +} {{CREATE VIEW www AS SELECT bbb FROM b1 UNION ALL SELECT y FROM b2}} + +db collate nocase {string compare} + +do_execsql_test 8.4.3 { + CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT x FROM b2 ORDER BY 1 COLLATE nocase; +} + +do_execsql_test 8.4.4 { + ALTER TABLE b2 RENAME x TO hello; + SELECT sql FROM sqlite_master WHERE name='xxx'; +} {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}} + +do_execsql_test 8.4.5 { + CREATE VIEW zzz AS SELECT george, ringo FROM b1; + ALTER TABLE b1 RENAME a TO aaa; + SELECT sql FROM sqlite_master WHERE name = 'zzz' +} {{CREATE VIEW zzz AS SELECT george, ringo FROM b1}} + + +finish_test diff --git a/test/trigger7.test b/test/trigger7.test index 847d78cd7a..5fa7638f8e 100644 --- a/test/trigger7.test +++ b/test/trigger7.test @@ -113,6 +113,6 @@ do_test trigger7-99.1 { db close catch { sqlite3 db test.db } catchsql { DROP TRIGGER t2r5 } -} {1 {malformed database schema (t2r12)}} +} {/1 {malformed database schema .*}/} finish_test diff --git a/test/upsert1.test b/test/upsert1.test index 13bb2f81b4..0d13cd8543 100644 --- a/test/upsert1.test +++ b/test/upsert1.test @@ -128,4 +128,87 @@ do_execsql_test upsert1-610 { PRAGMA integrity_check; } {ok} +# 2018-08-14 +# Ticket https://www.sqlite.org/src/info/908f001483982c43 +# If there are multiple uniqueness contraints, the UPSERT should fire +# if the one constraint it targets fails, regardless of whether or not +# the other constraints pass or fail. In other words, the UPSERT constraint +# should be tested first. +# +do_execsql_test upsert1-700 { + DROP TABLE t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT, e INT); + CREATE UNIQUE INDEX t1b ON t1(b); + CREATE UNIQUE INDEX t1e ON t1(e); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(e) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-710 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(a) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-720 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(b) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-730 { + DROP TABLE t1; + CREATE TABLE t1(a INT, b INT, c INT, d INT, e INT); + CREATE UNIQUE INDEX t1a ON t1(a); + CREATE UNIQUE INDEX t1b ON t1(b); + CREATE UNIQUE INDEX t1e ON t1(e); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(e) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-740 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(a) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-750 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(b) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-760 { + DROP TABLE t1; + CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT, d INT, e INT) WITHOUT ROWID; + CREATE UNIQUE INDEX t1a ON t1(a); + CREATE UNIQUE INDEX t1b ON t1(b); + CREATE UNIQUE INDEX t1e ON t1(e); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(e) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-770 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(a) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} +do_execsql_test upsert1-780 { + DELETE FROM t1; + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); + INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5) + ON CONFLICT(b) DO UPDATE SET c=excluded.c; + SELECT * FROM t1; +} {1 2 33 4 5} + + finish_test From 5be60c55e8bd51a95d2198df98f30cebfa742b33 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 15 Aug 2018 20:28:39 +0000 Subject: [PATCH 3/7] (no comment) FossilOrigin-Name: e272dc2b1c0edab59a40f32c77c81a3e636937280524161eff5669cb0046ad84 --- manifest | 22 ++++++------- manifest.uuid | 2 +- src/alter.c | 82 +++++++++++++++++++++++++++++++++++++++++++--- src/expr.c | 3 ++ src/parse.y | 6 ++-- src/sqliteInt.h | 6 ++-- src/trigger.c | 34 +++++++++++++++---- test/altercol.test | 24 ++++++++++---- 8 files changed, 143 insertions(+), 36 deletions(-) diff --git a/manifest b/manifest index 0c22ccf95e..071c01ee2b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sproblem\swhen\srenaming\san\sIPK\scolumn\sthat\sis\salso\spart\sof\sa\schild\skey. -D 2018-08-14T21:05:35.866 +C (no\scomment) +D 2018-08-15T20:28:39.044 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c e1f3a71924a5248fb105baba9b9099b2187c20ebf5af5cb0992cc271b22def46 +F src/alter.c 1452a35d1660b0c9786f93d6e5e6db3fe28ef6ec5bbeb1f51d116ac984f65cab F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -450,7 +450,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957 F src/dbpage.c 4aa7f26198934dbd002e69418220eae3dbc71b010bbac32bd78faf86b52ce6c3 F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91 F src/delete.c 107e28d3ef8bd72fd11953374ca9107cd74e8b09c3ded076a6048742d26ce7d2 -F src/expr.c 4a555ff68084360c133c5b9d985ae05d2cf914125d4c8e5614496dc071d318dd +F src/expr.c 4c1e40cdb1717b42e848835caf3e2b881f748cdcfabe3498e83634d1d2db5e26 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c f59253c0be4b1e9dfcb073b6d6d6ab83090ae50c08b5c113b76013c4b157cd6a F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f @@ -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 1cdfb5179649b0cde2459d0e76b5acb7007bb10f4744ae804b2326ed98c52060 +F src/parse.y 57f533353a4945370a60f66aa7be284c8a627509baa02d707982f906e4851f7d F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880 @@ -503,7 +503,7 @@ F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1 F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 -F src/sqliteInt.h c3bca346f053c0d2fe08f0813b0521597bbab11ff8195348372005606c309f84 +F src/sqliteInt.h 78e9b483adbdf928923a175f2c8470da89024b973b4b790486b6e8736b4c876f F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -563,7 +563,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c fe35cb947ec54fe9a96895359b777a9305356c2f3c2917a1d83a57603108e66c F src/treeview.c e7a7f90552bb418533cdd0309b5eb71d4effa50165b880fc8c2001e613577e5f -F src/trigger.c f3ba315cd590a3db88fdfd259c8084e567746f6e0614efb3c806b205d7122276 +F src/trigger.c f6760d78be400164c95732b4aabccab27072ec66e9db2ee33baf70e65e467af0 F src/update.c 345ce35eb1332eb4829857aa8b1f65a614b07dae91d0346c0dc2baacafbcc51b F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5 @@ -599,7 +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 a0b57120e68c7aedad302fd9fd515d7c69c7d3547eccc95a846351dc55296981 +F test/altercol.test c57633752895b10b2ca488de648b415e95c45272f37e05e4ad110f7b787c83eb F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 7a45802dafaed3c058f59d1487fe8091e1f309cc5f0a5bcf2579f0bdd8e92d89 ad15486022209205c65fb5ffdbe30a7b99379170451e6aff4bab6e90b549d6c7 -R 1d8b409ed4f5c3f244c9277896b51070 +P 6e6a2bfdbbfd4ffc14be88962ff97bf3c626fef9d1a337715d2a4be1a657fb0d +R 50934f42996c56e5e11553d2ba6b9958 U dan -Z 642c2e3d8e8cc78036c1fba818a3d8b0 +Z 5c4bbda1010d90157de824a7c5d29ecd diff --git a/manifest.uuid b/manifest.uuid index 0249abf0f9..98b330ff90 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6e6a2bfdbbfd4ffc14be88962ff97bf3c626fef9d1a337715d2a4be1a657fb0d \ No newline at end of file +e272dc2b1c0edab59a40f32c77c81a3e636937280524161eff5669cb0046ad84 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 89be503fc1..c0696400f5 100644 --- a/src/alter.c +++ b/src/alter.c @@ -982,6 +982,10 @@ static int renameColumnSelectCb(Walker *pWalker, Select *p){ */ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; + if( pExpr->op==TK_TRIGGER && pExpr->iColumn==p->iCol ){ + renameTokenFind(pWalker->pParse, p, (void*)pExpr); + }else + if( p->zOld && pExpr->op==TK_DOT ){ Expr *pLeft = pExpr->pLeft; Expr *pRight = pExpr->pRight; @@ -1145,7 +1149,7 @@ static void renameColumnFunc( if( rc==SQLITE_OK ){ sqlite3WalkSelect(&sWalker, pSelect); }else if( rc==SQLITE_ERROR ){ - /* Failed to resolve all symboles in the view. This is not an + /* Failed to resolve all symbols in the view. This is not an ** error, but it will not be edited. */ sqlite3DbFree(db, sParse.zErrMsg); sParse.zErrMsg = 0; @@ -1187,9 +1191,69 @@ static void renameColumnFunc( sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr); sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere); }else{ - sCtx.zOld = zOld; - sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); - if( sParse.pNewTrigger->pColumns ){ + /* A trigger */ + TriggerStep *pStep; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; + sParse.pTriggerTab = pTab; + sParse.eTriggerOp = sParse.pNewTrigger->op; + + /* Resolve symbols in WHEN clause */ + if( sParse.pTriggerTab==pTab && sParse.pNewTrigger->pWhen ){ + rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); + } + + for(pStep=sParse.pNewTrigger->step_list; + rc==SQLITE_OK && pStep; + pStep=pStep->pNext + ){ + if( pStep->pSelect ) sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC); + if( pStep->zTarget ){ + Table *pTarget = sqlite3FindTable(db, pStep->zTarget, zDb); + if( pTarget==0 ){ + rc = SQLITE_ERROR; + }else{ + SrcList sSrc; + memset(&sSrc, 0, sizeof(sSrc)); + sSrc.nSrc = 1; + sSrc.a[0].zName = pStep->zTarget; + sSrc.a[0].pTab = pTarget; + sNC.pSrcList = &sSrc; + if( pStep->pWhere ){ + rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); + } + + if( rc==SQLITE_OK && pTarget==pTab ){ + if( pStep->pIdList ){ + for(i=0; ipIdList->nId; i++){ + char *zName = pStep->pIdList->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } + if( pStep->op==TK_UPDATE ){ + assert( pStep->pExprList ); + for(i=0; ipExprList->nExpr; i++){ + char *zName = pStep->pExprList->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } + } + } + } + } + + if( rc!=SQLITE_OK ) goto renameColumnFunc_done; + + /* Find tokens to edit in UPDATE OF clause */ + if( sParse.pTriggerTab==pTab && sParse.pNewTrigger->pColumns ){ for(i=0; ipColumns->nId; i++){ char *zName = sParse.pNewTrigger->pColumns->a[i].zName; if( 0==sqlite3_stricmp(zName, zOld) ){ @@ -1197,6 +1261,16 @@ static void renameColumnFunc( } } } + + /* Find tokens to edit in WHEN clause */ + sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen); + + /* Find tokens to edit in trigger steps */ + for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ + sqlite3WalkSelect(&sWalker, pStep->pSelect); + sqlite3WalkExpr(&sWalker, pStep->pWhere); + sqlite3WalkExprList(&sWalker, pStep->pExprList); + } } assert( rc==SQLITE_OK ); diff --git a/src/expr.c b/src/expr.c index 769d198c22..e9b63cba83 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1666,6 +1666,9 @@ void sqlite3ExprListSetName( assert( pItem->zName==0 ); pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n); if( dequote ) sqlite3Dequote(pItem->zName); + if( IN_RENAME_COLUMN ){ + sqlite3RenameToken(pParse, (void*)pItem->zName, pName); + } } } diff --git a/src/parse.y b/src/parse.y index 76e966a4da..50bbd0207f 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1451,16 +1451,16 @@ tridxby ::= NOT INDEXED. { // UPDATE trigger_cmd(A) ::= UPDATE(B) orconf(R) trnm(X) tridxby SET setlist(Y) where_opt(Z) scanpt(E). - {A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R, B.z, E);} + {A = sqlite3TriggerUpdateStep(pParse, &X, Y, Z, R, B.z, E);} // INSERT trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). { - A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,U,B,Z);/*A-overwrites-R*/ + A = sqlite3TriggerInsertStep(pParse,&X,F,S,R,U,B,Z);/*A-overwrites-R*/ } // DELETE trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E). - {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y, B.z, E);} + {A = sqlite3TriggerDeleteStep(pParse, &X, Y, B.z, E);} // SELECT trigger_cmd(A) ::= scanpt(B) select(X) scanpt(E). diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f2e6121de8..9158a4ca36 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4049,12 +4049,12 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*, const char*,const char*); - TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, + TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*, Select*,u8,Upsert*, const char*,const char*); - TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8, + TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8, const char*,const char*); - TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*, + TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*, const char*,const char*); void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); diff --git a/src/trigger.c b/src/trigger.c index 1df88fd809..330e14ea72 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -428,7 +428,7 @@ static TriggerStep *triggerStepAllocate( ** body of a trigger. */ TriggerStep *sqlite3TriggerInsertStep( - sqlite3 *db, /* The database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table into which we insert */ IdList *pColumn, /* List of columns in pTableName to insert into */ Select *pSelect, /* A SELECT statement that supplies values */ @@ -437,13 +437,19 @@ TriggerStep *sqlite3TriggerInsertStep( const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; assert(pSelect != 0 || db->mallocFailed); pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pSelect = pSelect; + pSelect = 0; + }else{ + pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + } pTriggerStep->pIdList = pColumn; pTriggerStep->pUpsert = pUpsert; pTriggerStep->orconf = orconf; @@ -464,7 +470,7 @@ TriggerStep *sqlite3TriggerInsertStep( ** sees an UPDATE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerUpdateStep( - sqlite3 *db, /* The database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ @@ -472,12 +478,20 @@ TriggerStep *sqlite3TriggerUpdateStep( const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pExprList = pEList; + pTriggerStep->pWhere = pWhere; + pEList = 0; + pWhere = 0; + }else{ + pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); + pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + } pTriggerStep->orconf = orconf; } sqlite3ExprListDelete(db, pEList); @@ -491,17 +505,23 @@ TriggerStep *sqlite3TriggerUpdateStep( ** sees a DELETE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerDeleteStep( - sqlite3 *db, /* Database connection */ + Parse *pParse, /* Parser */ Token *pTableName, /* The table from which rows are deleted */ Expr *pWhere, /* The WHERE clause */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd); if( pTriggerStep ){ - pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + if( IN_RENAME_COLUMN ){ + pTriggerStep->pWhere = pWhere; + pWhere = 0; + }else{ + pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + } pTriggerStep->orconf = OE_Default; } sqlite3ExprDelete(db, pWhere); diff --git a/test/altercol.test b/test/altercol.test index f6ead99244..d5fc8f1505 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -125,25 +125,35 @@ do_execsql_test -db db2 2.3 { SELECT biglongname FROM t3 } do_execsql_test 3.0 { CREATE TABLE t4(x, y, z); CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.y<0 BEGIN - SELECT 1, 2, 3, 4; + SELECT x, y, z FROM t4; + DELETE FROM t4 WHERE y=32; + UPDATE t4 SET x=y+1, y=0 WHERE y=32; + INSERT INTO t4(x, y, z) SELECT 4, 5, 6 WHERE 0; END; INSERT INTO t4 VALUES(3, 2, 1); } +breakpoint do_execsql_test 3.1 { ALTER TABLE t4 RENAME y TO abc; SELECT sql FROM sqlite_master WHERE name='t4'; } {{CREATE TABLE t4(x, abc, z)}} -db close -sqlite3 db test.db - do_execsql_test 3.2 { SELECT * FROM t4; } {3 2 1} do_execsql_test 3.3 { INSERT INTO t4 VALUES(6, 5, 4); } {} +do_execsql_test 3.4 { SELECT sql FROM sqlite_master WHERE type='trigger' } { +{CREATE TRIGGER ttt AFTER INSERT ON t4 WHEN new.abc<0 BEGIN + SELECT x, abc, z FROM t4; + DELETE FROM t4 WHERE abc=32; + UPDATE t4 SET x=abc+1, abc=0 WHERE abc=32; + INSERT INTO t4(x, abc, z) SELECT 4, 5, 6 WHERE 0; + END} +} + #------------------------------------------------------------------------- # do_execsql_test 4.0 { @@ -233,17 +243,17 @@ do_execsql_test 7.0 { END; } -do_execsql_test 7.1 { +do_execsql_test 7.1.1 { INSERT INTO t6 VALUES(0, 0, 0); UPDATE t6 SET "col c" = 1; SELECT * FROM c; } {1} -do_execsql_test 7.2 { +do_execsql_test 7.1.2 { ALTER TABLE t6 RENAME "col c" TO "col 3"; } -do_execsql_test 7.3 { +do_execsql_test 7.1.3 { UPDATE t6 SET "col 3" = 0; SELECT * FROM c; } {2} From 0cbb0b11f1dbe8d6be549602d0c2b890a184b34d Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 16 Aug 2018 19:49:16 +0000 Subject: [PATCH 4/7] Further progress on updating trigger programs as part of ALTER TABLE RENAME COLUMN. FossilOrigin-Name: 3f47222b6e20e1c1ef41c0f7391c8b5c7648e2a2d6013303d155ad59ce27143a --- manifest | 14 +++++----- manifest.uuid | 2 +- src/alter.c | 68 ++++++++++++++++++++++++++++++---------------- test/altercol.test | 57 +++++++++++++++++++++++++++++++++++++- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index 071c01ee2b..6eb44a6d00 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C (no\scomment) -D 2018-08-15T20:28:39.044 +C Further\sprogress\son\supdating\strigger\sprograms\sas\spart\sof\sALTER\sTABLE\sRENAME\nCOLUMN. +D 2018-08-16T19:49:16.440 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 1452a35d1660b0c9786f93d6e5e6db3fe28ef6ec5bbeb1f51d116ac984f65cab +F src/alter.c c16b42be9a598c48d6869082f74c8391641dc31a4ba85811997b8b80c0e3ce4c F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -599,7 +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 c57633752895b10b2ca488de648b415e95c45272f37e05e4ad110f7b787c83eb +F test/altercol.test c60161856f21a447220643c9c7cb447cf766d0c6a115247a58628c2355b66716 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6e6a2bfdbbfd4ffc14be88962ff97bf3c626fef9d1a337715d2a4be1a657fb0d -R 50934f42996c56e5e11553d2ba6b9958 +P e272dc2b1c0edab59a40f32c77c81a3e636937280524161eff5669cb0046ad84 +R a4f7901353039cfd23087ece73e91338 U dan -Z 5c4bbda1010d90157de824a7c5d29ecd +Z edd8747b185ed4a97732b3af8e23ffdd diff --git a/manifest.uuid b/manifest.uuid index 98b330ff90..d26f00346b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e272dc2b1c0edab59a40f32c77c81a3e636937280524161eff5669cb0046ad84 \ No newline at end of file +3f47222b6e20e1c1ef41c0f7391c8b5c7648e2a2d6013303d155ad59ce27143a \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index c0696400f5..5b36a4b106 100644 --- a/src/alter.c +++ b/src/alter.c @@ -845,10 +845,7 @@ void sqlite3AlterRenameColumn( sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) " - "WHERE name NOT LIKE 'sqlite_%%' AND (" - " type IN ('table', 'view') " - " OR (type IN ('index', 'trigger') AND tbl_name = %Q)" - ")", + "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)", zDb, MASTER_NAME, zDb, pTab->zName, iCol, zNew, bQuote, pTab->zName @@ -982,23 +979,14 @@ static int renameColumnSelectCb(Walker *pWalker, Select *p){ */ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ RenameCtx *p = pWalker->u.pRename; - if( pExpr->op==TK_TRIGGER && pExpr->iColumn==p->iCol ){ + if( pExpr->op==TK_TRIGGER + && pExpr->iColumn==p->iCol + && pWalker->pParse->pTriggerTab==p->pTab + ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); - }else - - if( p->zOld && pExpr->op==TK_DOT ){ - Expr *pLeft = pExpr->pLeft; - Expr *pRight = pExpr->pRight; - assert( pLeft->op==TK_ID && pRight->op==TK_ID ); - if( 0==sqlite3_stricmp(pLeft->u.zToken, "old") - || 0==sqlite3_stricmp(pLeft->u.zToken, "new") - ){ - if( 0==sqlite3_stricmp(pRight->u.zToken, p->zOld) ){ - renameTokenFind(pWalker->pParse, p, (void*)pRight); - } - } - }else if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol - && (p->pTab==0 || p->pTab==pExpr->pTab) + }else if( pExpr->op==TK_COLUMN + && pExpr->iColumn==p->iCol + && p->pTab==pExpr->pTab ){ renameTokenFind(pWalker->pParse, p, (void*)pExpr); } @@ -1138,11 +1126,11 @@ static void renameColumnFunc( sWalker.xSelectCallback = renameColumnSelectCb; sWalker.u.pRename = &sCtx; + sCtx.pTab = pTab; if( rc!=SQLITE_OK ) goto renameColumnFunc_done; if( sParse.pNewTable ){ Select *pSelect = sParse.pNewTable->pSelect; if( pSelect ){ - sCtx.pTab = pTab; sParse.rc = SQLITE_OK; sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0); rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); @@ -1161,6 +1149,7 @@ static void renameColumnFunc( int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName); FKey *pFKey; assert( sParse.pNewTable->pSelect==0 ); + sCtx.pTab = sParse.pNewTable; if( bFKOnly==0 ){ renameTokenFind( &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName @@ -1196,11 +1185,11 @@ static void renameColumnFunc( NameContext sNC; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = &sParse; - sParse.pTriggerTab = pTab; + sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb); sParse.eTriggerOp = sParse.pNewTrigger->op; - /* Resolve symbols in WHEN clause */ - if( sParse.pTriggerTab==pTab && sParse.pNewTrigger->pWhen ){ + /* Resolve symbols in WHEN clause */ + if( sParse.pNewTrigger->pWhen ){ rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); } @@ -1226,6 +1215,30 @@ static void renameColumnFunc( if( rc==SQLITE_OK ){ rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); } + if( pStep->pUpsert ){ + Upsert *pUpsert = pStep->pUpsert; + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); + } + if( rc==SQLITE_OK && pUpsert->pUpsertSet){ + ExprList *pUpsertSet = pUpsert->pUpsertSet; + rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet); + if( rc==SQLITE_OK && pTarget==pTab ){ + for(i=0; inExpr; i++){ + char *zName = pUpsertSet->a[i].zName; + if( 0==sqlite3_stricmp(zName, zOld) ){ + renameTokenFind(&sParse, &sCtx, (void*)zName); + } + } + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere); + } + if( rc==SQLITE_OK ){ + rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); + } + } if( rc==SQLITE_OK && pTarget==pTab ){ if( pStep->pIdList ){ @@ -1270,6 +1283,13 @@ static void renameColumnFunc( sqlite3WalkSelect(&sWalker, pStep->pSelect); sqlite3WalkExpr(&sWalker, pStep->pWhere); sqlite3WalkExprList(&sWalker, pStep->pExprList); + if( pStep->pUpsert ){ + Upsert *pUpsert = pStep->pUpsert; + sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget); + sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet); + sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere); + sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere); + } } } diff --git a/test/altercol.test b/test/altercol.test index d5fc8f1505..cf6e75f7f5 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -133,7 +133,6 @@ do_execsql_test 3.0 { INSERT INTO t4 VALUES(3, 2, 1); } -breakpoint do_execsql_test 3.1 { ALTER TABLE t4 RENAME y TO abc; SELECT sql FROM sqlite_master WHERE name='t4'; @@ -331,5 +330,61 @@ do_execsql_test 8.4.5 { SELECT sql FROM sqlite_master WHERE name = 'zzz' } {{CREATE VIEW zzz AS SELECT george, ringo FROM b1}} +#------------------------------------------------------------------------- +# More triggers. +# +foreach {tn old new lSchema} { + 1 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE TRIGGER AFTER INSERT ON t1 BEGIN + SELECT _x_ FROM t1; + END } + } + + 2 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE TABLE t2(c, d, e) } + { CREATE TRIGGER ttt AFTER INSERT ON t2 BEGIN + SELECT _x_ FROM t1; + END } + } + + 3 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) } + { CREATE TABLE t2(c, d, e) } + { CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN + INSERT INTO t2 VALUES(new.a, new.b, new._x_); + END } + } + + 4 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) } + { CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN + INSERT INTO t1 VALUES(new.a, new.b, new._x_) + ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1; + END } + } +} { + reset_db + set lSorted [list] + foreach sql $lSchema { + execsql $sql + lappend lSorted [string trim $sql] + } + set lSorted [lsort $lSorted] + + do_execsql_test 9.$tn.1 { + SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 + } $lSorted + +if { $tn==3 } breakpoint + do_execsql_test 9.$tn.2 "ALTER TABLE t1 RENAME $old TO $new" + + do_execsql_test 9.$tn.1 { + SELECT sql FROM sqlite_master ORDER BY 1 + } [string map [list $old $new] $lSorted] +} + +reset_db finish_test From dabc268f96bb983433e6225b3c51c87f40612481 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 17 Aug 2018 17:18:16 +0000 Subject: [PATCH 5/7] Ensure that CREATE VIEW, TRIGGER or INDEX statements can be edited by ALTER TABLE RENAME COLUMN even if they use collation-sequences or user-defined-functions that are not available. FossilOrigin-Name: 540014efd6a048373313c6cd9413de10d5d7114daf537cf5999ccf3c5c3f9358 --- manifest | 14 ++++----- manifest.uuid | 2 +- src/resolve.c | 78 ++++++++++++++++++++++++---------------------- test/altercol.test | 63 ++++++++++++++++++++++++++----------- 4 files changed, 93 insertions(+), 64 deletions(-) diff --git a/manifest b/manifest index 6eb44a6d00..4eb8d1d4f9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\sprogress\son\supdating\strigger\sprograms\sas\spart\sof\sALTER\sTABLE\sRENAME\nCOLUMN. -D 2018-08-16T19:49:16.440 +C Ensure\sthat\sCREATE\sVIEW,\sTRIGGER\sor\sINDEX\sstatements\scan\sbe\sedited\sby\sALTER\nTABLE\sRENAME\sCOLUMN\seven\sif\sthey\suse\scollation-sequences\sor\nuser-defined-functions\sthat\sare\snot\savailable. +D 2018-08-17T17:18:16.131 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -496,7 +496,7 @@ F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 F src/prepare.c 117e27c6826a83f461986c0cfbb09c31fe004922ce23a61bf78d82a46b0958d9 F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 -F src/resolve.c b51a48f33da36e0c2dd1ea5f0d10197c3e938a54086a69efce064ae41e2254e1 +F src/resolve.c c58793195eaf2989b8d0688355cca15024cc792da155dffc44b85afae426c2d5 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04 F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f @@ -599,7 +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 c60161856f21a447220643c9c7cb447cf766d0c6a115247a58628c2355b66716 +F test/altercol.test 4597bd06c5dc25ce7509ce2b8a18f3ddb76b0e42fd2505e0efd603d3178e4cfb F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P e272dc2b1c0edab59a40f32c77c81a3e636937280524161eff5669cb0046ad84 -R a4f7901353039cfd23087ece73e91338 +P 3f47222b6e20e1c1ef41c0f7391c8b5c7648e2a2d6013303d155ad59ce27143a +R 074dd33a01022ba2b3a74f6795cf4991 U dan -Z edd8747b185ed4a97732b3af8e23ffdd +Z 478b06e640e2a3d3de9b75cbb89dcb88 diff --git a/manifest.uuid b/manifest.uuid index d26f00346b..91035a2afd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f47222b6e20e1c1ef41c0f7391c8b5c7648e2a2d6013303d155ad59ce27143a \ No newline at end of file +540014efd6a048373313c6cd9413de10d5d7114daf537cf5999ccf3c5c3f9358 \ No newline at end of file diff --git a/src/resolve.c b/src/resolve.c index fc815eed64..b6c72759ff 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -760,56 +760,58 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } } + if( 0==IN_RENAME_COLUMN ){ #ifndef SQLITE_OMIT_WINDOWFUNC - assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX) + assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX) || (pDef->xValue==0 && pDef->xInverse==0) || (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize) - ); - if( pDef && pDef->xValue==0 && pExpr->pWin ){ - sqlite3ErrorMsg(pParse, - "%.*s() may not be used as a window function", nId, zId ); - pNC->nErr++; - }else if( - (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) - || (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin) - || (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0) - ){ - const char *zType; - if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){ - zType = "window"; - }else{ - zType = "aggregate"; + if( pDef && pDef->xValue==0 && pExpr->pWin ){ + sqlite3ErrorMsg(pParse, + "%.*s() may not be used as a window function", nId, zId + ); + pNC->nErr++; + }else if( + (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) + || (is_agg && (pDef->funcFlags & SQLITE_FUNC_WINDOW) && !pExpr->pWin) + || (is_agg && pExpr->pWin && (pNC->ncFlags & NC_AllowWin)==0) + ){ + const char *zType; + if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->pWin ){ + zType = "window"; + }else{ + zType = "aggregate"; + } + sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId); + pNC->nErr++; + is_agg = 0; } - sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()", zType, nId,zId); - pNC->nErr++; - is_agg = 0; - } #else - if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ - sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); - pNC->nErr++; - is_agg = 0; - } + if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){ + sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId); + pNC->nErr++; + is_agg = 0; + } #endif - else if( no_such_func && pParse->db->init.busy==0 + else if( no_such_func && pParse->db->init.busy==0 && !IN_RENAME_COLUMN #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION - && pParse->explain==0 + && pParse->explain==0 #endif - ){ - sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); - pNC->nErr++; - }else if( wrong_num_args ){ - sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", - nId, zId); - pNC->nErr++; - } - if( is_agg ){ + ){ + sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); + pNC->nErr++; + }else if( wrong_num_args ){ + sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", + nId, zId); + pNC->nErr++; + } + if( is_agg ){ #ifndef SQLITE_OMIT_WINDOWFUNC - pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg); + pNC->ncFlags &= ~(pExpr->pWin ? NC_AllowWin : NC_AllowAgg); #else - pNC->ncFlags &= ~NC_AllowAgg; + pNC->ncFlags &= ~NC_AllowAgg; #endif + } } sqlite3WalkExprList(pWalker, pList); if( is_agg ){ diff --git a/test/altercol.test b/test/altercol.test index cf6e75f7f5..71476ee457 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -333,6 +333,26 @@ do_execsql_test 8.4.5 { #------------------------------------------------------------------------- # More triggers. # +proc do_rename_column_test {tn old new lSchema} { + reset_db + set lSorted [list] + foreach sql $lSchema { + execsql $sql + lappend lSorted [string trim $sql] + } + set lSorted [lsort $lSorted] + + do_execsql_test $tn.1 { + SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 + } $lSorted + + do_execsql_test $tn.2 "ALTER TABLE t1 RENAME $old TO $new" + + do_execsql_test $tn.3 { + SELECT sql FROM sqlite_master ORDER BY 1 + } [string map [list $old $new] $lSorted] +} + foreach {tn old new lSchema} { 1 _x_ _xxx_ { { CREATE TABLE t1(a, b, _x_) } @@ -365,26 +385,33 @@ foreach {tn old new lSchema} { END } } } { - reset_db - set lSorted [list] - foreach sql $lSchema { - execsql $sql - lappend lSorted [string trim $sql] - } - set lSorted [lsort $lSorted] - - do_execsql_test 9.$tn.1 { - SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 - } $lSorted - -if { $tn==3 } breakpoint - do_execsql_test 9.$tn.2 "ALTER TABLE t1 RENAME $old TO $new" - - do_execsql_test 9.$tn.1 { - SELECT sql FROM sqlite_master ORDER BY 1 - } [string map [list $old $new] $lSorted] + do_rename_column_test 9.$tn $old $new $lSchema } +#------------------------------------------------------------------------- +# Test that views can be edited even if there are missing collation +# sequences or user defined functions. +# reset_db +foreach {tn old new lSchema} { + 1 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz } + } + + 2 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE scalar(_x_) } + } + + 3 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_ = unicode(1, 2, 3) } + } +} { + do_rename_column_test 10.$tn $old $new $lSchema +} + + finish_test From 499b825487af9b95aef8b1dc34a0049e9d5c080c Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 17 Aug 2018 18:08:28 +0000 Subject: [PATCH 6/7] Allow an ALTER TABLE RENAME COLUMN to proceed even if the schema contains a virtual table for which the module is unavailable. FossilOrigin-Name: 7b72b2360a70eb1f788b3c4d745967dfedb2c80af6b2e146ba940a3741fd51a1 --- manifest | 14 ++++++------ manifest.uuid | 2 +- src/alter.c | 3 ++- test/altercol.test | 53 +++++++++++++++++++++++++++++++--------------- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index 4eb8d1d4f9..7287245cfa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Ensure\sthat\sCREATE\sVIEW,\sTRIGGER\sor\sINDEX\sstatements\scan\sbe\sedited\sby\sALTER\nTABLE\sRENAME\sCOLUMN\seven\sif\sthey\suse\scollation-sequences\sor\nuser-defined-functions\sthat\sare\snot\savailable. -D 2018-08-17T17:18:16.131 +C Allow\san\sALTER\sTABLE\sRENAME\sCOLUMN\sto\sproceed\seven\sif\sthe\sschema\scontains\sa\nvirtual\stable\sfor\swhich\sthe\smodule\sis\sunavailable. +D 2018-08-17T18:08:28.458 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c c16b42be9a598c48d6869082f74c8391641dc31a4ba85811997b8b80c0e3ce4c +F src/alter.c c81f6d8d4f07ba89b2aa237bf8aaa4cb63b43b716eec8291f70eeea145a66a09 F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -599,7 +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 4597bd06c5dc25ce7509ce2b8a18f3ddb76b0e42fd2505e0efd603d3178e4cfb +F test/altercol.test cac9395932f9fa18b3c074559c33c557352db5d0fdf81aec28b1513828cb855f F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3f47222b6e20e1c1ef41c0f7391c8b5c7648e2a2d6013303d155ad59ce27143a -R 074dd33a01022ba2b3a74f6795cf4991 +P 540014efd6a048373313c6cd9413de10d5d7114daf537cf5999ccf3c5c3f9358 +R f20218c0b275f1c838232dab3f0beae8 U dan -Z 478b06e640e2a3d3de9b75cbb89dcb88 +Z 4df0525d7f8fcece703661753e166268 diff --git a/manifest.uuid b/manifest.uuid index 91035a2afd..5786d9265b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -540014efd6a048373313c6cd9413de10d5d7114daf537cf5999ccf3c5c3f9358 \ No newline at end of file +7b72b2360a70eb1f788b3c4d745967dfedb2c80af6b2e146ba940a3741fd51a1 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 5b36a4b106..84640f3ab3 100644 --- a/src/alter.c +++ b/src/alter.c @@ -845,7 +845,8 @@ void sqlite3AlterRenameColumn( sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = sqlite_rename_column(sql, %Q, %Q, %d, %Q, %d) " - "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)", + "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)" + " AND sql NOT LIKE 'create virtual%%'", zDb, MASTER_NAME, zDb, pTab->zName, iCol, zNew, bQuote, pTab->zName diff --git a/test/altercol.test b/test/altercol.test index 71476ee457..a48a1444d7 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -334,23 +334,36 @@ do_execsql_test 8.4.5 { # More triggers. # proc do_rename_column_test {tn old new lSchema} { - reset_db - set lSorted [list] - foreach sql $lSchema { - execsql $sql - lappend lSorted [string trim $sql] + + for {set i 0} {$i < 2} {incr i} { + # DROP all tables and views in database. + set sql "SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1" + foreach nm [db eval $sql] { db eval "DROP TABLE $nm" } + set sql "SELECT name FROM sqlite_master WHERE type='view' ORDER BY 1" + foreach nm [db eval $sql] { db eval "DROP VIEW $nm" } + + set lSorted [list] + foreach sql $lSchema { + execsql $sql + lappend lSorted [string trim $sql] + } + set lSorted [lsort $lSorted] + + do_execsql_test $tn.$i.1 { + SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 + } $lSorted + + if {0 && $i==1} { + db close + sqlite3 db test.db + } + + do_execsql_test $tn.$i.2 "ALTER TABLE t1 RENAME $old TO $new" + + do_execsql_test $tn.$i.3 { + SELECT sql FROM sqlite_master ORDER BY 1 + } [string map [list $old $new] $lSorted] } - set lSorted [lsort $lSorted] - - do_execsql_test $tn.1 { - SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 - } $lSorted - - do_execsql_test $tn.2 "ALTER TABLE t1 RENAME $old TO $new" - - do_execsql_test $tn.3 { - SELECT sql FROM sqlite_master ORDER BY 1 - } [string map [list $old $new] $lSorted] } foreach {tn old new lSchema} { @@ -397,7 +410,7 @@ reset_db foreach {tn old new lSchema} { 1 _x_ _xxx_ { { CREATE TABLE t1(a, b, _x_) } - { CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz } + { CREATE VIEW s1 AS SELECT a, b, _x_ FROM t1 WHERE _x_='abc' COLLATE xyz } } 2 _x_ _xxx_ { @@ -409,7 +422,13 @@ foreach {tn old new lSchema} { { CREATE TABLE t1(a, b, _x_) } { CREATE VIEW v1 AS SELECT a, b, _x_ FROM t1 WHERE _x_ = unicode(1, 2, 3) } } + + 4 _x_ _xxx_ { + { CREATE TABLE t1(a, b, _x_) } + { CREATE VIRTUAL TABLE e1 USING echo(t1) } + } } { + register_echo_module db do_rename_column_test 10.$tn $old $new $lSchema } From 24fedb94c233e2eed21a50453b8d6878add2cfec Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 18 Aug 2018 17:35:38 +0000 Subject: [PATCH 7/7] Improvements to error handling in ALTER TABLE RENAME COLUMN. FossilOrigin-Name: 7fa1faeaff30b74b68ee6f4b363d837f21cf313d8262361c901bda884df139a2 --- manifest | 14 +++---- manifest.uuid | 2 +- src/alter.c | 96 +++++++++++++++++++++++++++++++++------------- test/altercol.test | 53 ++++++++++++++++++++----- 4 files changed, 120 insertions(+), 45 deletions(-) diff --git a/manifest b/manifest index 7287245cfa..94dcb835ce 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Allow\san\sALTER\sTABLE\sRENAME\sCOLUMN\sto\sproceed\seven\sif\sthe\sschema\scontains\sa\nvirtual\stable\sfor\swhich\sthe\smodule\sis\sunavailable. -D 2018-08-17T18:08:28.458 +C Improvements\sto\serror\shandling\sin\sALTER\sTABLE\sRENAME\sCOLUMN. +D 2018-08-18T17:35:38.195 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -432,7 +432,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c c81f6d8d4f07ba89b2aa237bf8aaa4cb63b43b716eec8291f70eeea145a66a09 +F src/alter.c 3342dba26f9023b334e6ab634ef45d0d8b333023c80070a57f22f85f64bbeabb F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -599,7 +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 cac9395932f9fa18b3c074559c33c557352db5d0fdf81aec28b1513828cb855f +F test/altercol.test 7e22d63b1adb3f66e695c5dfb32d102644943ceeb88bc81bf74fa6d65dca5eba F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df @@ -1756,7 +1756,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 540014efd6a048373313c6cd9413de10d5d7114daf537cf5999ccf3c5c3f9358 -R f20218c0b275f1c838232dab3f0beae8 +P 7b72b2360a70eb1f788b3c4d745967dfedb2c80af6b2e146ba940a3741fd51a1 +R 4b2d31defa56ef33764a237b1d928d0d U dan -Z 4df0525d7f8fcece703661753e166268 +Z ea61ccced068208007aae6f195a47978 diff --git a/manifest.uuid b/manifest.uuid index 5786d9265b..20186e110c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7b72b2360a70eb1f788b3c4d745967dfedb2c80af6b2e146ba940a3741fd51a1 \ No newline at end of file +7fa1faeaff30b74b68ee6f4b363d837f21cf313d8262361c901bda884df139a2 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 84640f3ab3..613293182b 100644 --- a/src/alter.c +++ b/src/alter.c @@ -964,11 +964,15 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ } } +/* +** This is a Walker select callback. It does nothing. It is only required +** because without a dummy callback, sqlite3WalkExpr() and similar do not +** descend into sub-select statements. +*/ static int renameColumnSelectCb(Walker *pWalker, Select *p){ return WRC_Continue; } - /* ** This is a Walker expression callback. ** @@ -996,10 +1000,10 @@ static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){ /* ** The RenameCtx contains a list of tokens that reference a column that -** is being renamed by an ALTER TABLE statement. Return the "first" +** is being renamed by an ALTER TABLE statement. Return the "last" ** RenameToken in the RenameCtx and remove that RenameToken from the -** RenameContext. "First" means the first RenameToken encountered when -** the input SQL from left to right. Repeated calls to this routine +** RenameContext. "Last" means the last RenameToken encountered when +** the input SQL is parsed from left to right. Repeated calls to this routine ** return all column name tokens in the order that they are encountered ** in the SQL statement. */ @@ -1017,6 +1021,33 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ return pBest; } +/* +** An error occured while parsing or otherwise processing a database +** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an +** ALTER TABLE RENAME COLUMN program. The error message emitted by the +** sub-routine is currently stored in pParse->zErrMsg. This function +** adds context to the error message and then stores it in pCtx. +*/ +static void renameColumnParseError(sqlite3_context *pCtx, Parse *pParse){ + const char *zT; + const char *zN; + char *zErr; + if( pParse->pNewTable ){ + zT = pParse->pNewTable->pSelect ? "view" : "table"; + zN = pParse->pNewTable->zName; + }else if( pParse->pNewIndex ){ + zT = "index"; + zN = pParse->pNewIndex->zName; + }else{ + assert( pParse->pNewTrigger ); + zT = "trigger"; + zN = pParse->pNewTrigger->zName; + } + zErr = sqlite3_mprintf("error processing %s %s: %s", zT, zN, pParse->zErrMsg); + sqlite3_result_error(pCtx, zErr, -1); + sqlite3_free(zErr); +} + /* ** SQL function: ** @@ -1083,12 +1114,18 @@ static void renameColumnFunc( memset(&sCtx, 0, sizeof(sCtx)); sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); + /* Parse the SQL statement passed as the first argument. If no error + ** occurs and the parse does not result in a new table, index or + ** trigger object, the database must be corrupt. */ 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 ); + assert( sParse.zErrMsg==0 ); + assert( rc!=SQLITE_OK || zErr==0 ); + assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 ); + sParse.zErrMsg = zErr; if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0 @@ -1096,21 +1133,9 @@ static void renameColumnFunc( rc = SQLITE_CORRUPT_BKPT; } - if( rc==SQLITE_OK ){ - zQuot = sqlite3_mprintf("\"%w\"", zNew); - if( zQuot==0 ){ - rc = SQLITE_NOMEM; - }else{ - nQuot = sqlite3Strlen30(zQuot); - } - } - - if( bQuote ){ - zNew = zQuot; - nNew = nQuot; - } - #ifdef SQLITE_DEBUG + /* Ensure that all mappings in the Parse.pRename list really do map to + ** a part of the input string. */ assert( sqlite3Strlen30(zSql)==nSql ); if( rc==SQLITE_OK ){ RenameToken *pToken; @@ -1120,6 +1145,24 @@ static void renameColumnFunc( } #endif + /* Set zQuot to point to a buffer containing a quoted copy of the + ** identifier zNew. If the corresponding identifier in the original + ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to + ** point to zQuot so that all substitutions are made using the + ** quoted version of the new column name. */ + if( rc==SQLITE_OK ){ + zQuot = sqlite3_mprintf("\"%w\"", zNew); + if( zQuot==0 ){ + rc = SQLITE_NOMEM; + }else{ + nQuot = sqlite3Strlen30(zQuot); + } + } + if( bQuote ){ + zNew = zQuot; + nNew = nQuot; + } + /* Find tokens that need to be replaced. */ memset(&sWalker, 0, sizeof(Walker)); sWalker.pParse = &sParse; @@ -1137,12 +1180,6 @@ static void renameColumnFunc( rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); if( rc==SQLITE_OK ){ sqlite3WalkSelect(&sWalker, pSelect); - }else if( rc==SQLITE_ERROR ){ - /* Failed to resolve all symbols in the view. This is not an - ** error, but it will not be edited. */ - sqlite3DbFree(db, sParse.zErrMsg); - sParse.zErrMsg = 0; - rc = SQLITE_OK; } if( rc!=SQLITE_OK ) goto renameColumnFunc_done; }else{ @@ -1294,6 +1331,10 @@ static void renameColumnFunc( } } + /* At this point sCtx.pList contains a list of RenameToken objects + ** corresponding to all tokens in the input SQL that must be replaced + ** with the new column name. All that remains is to construct and + ** return the edited SQL string. */ assert( rc==SQLITE_OK ); assert( nQuot>=nNew ); zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1); @@ -1334,8 +1375,8 @@ static void renameColumnFunc( renameColumnFunc_done: if( rc!=SQLITE_OK ){ - if( zErr ){ - sqlite3_result_error(context, zErr, -1); + if( sParse.zErrMsg ){ + renameColumnParseError(context, &sParse); }else{ sqlite3_result_error_code(context, rc); } @@ -1349,6 +1390,7 @@ renameColumnFunc_done: sqlite3DeleteTrigger(db, sParse.pNewTrigger); renameTokenFree(db, sParse.pRename); renameTokenFree(db, sCtx.pList); + sqlite3DbFree(db, sParse.zErrMsg); sqlite3ParserReset(&sParse); sqlite3_free(zQuot); } diff --git a/test/altercol.test b/test/altercol.test index a48a1444d7..de025ac61c 100644 --- a/test/altercol.test +++ b/test/altercol.test @@ -26,6 +26,20 @@ ifcapable !altertable { return } +# Drop all the tables and views in the 'main' database of database connect +# [db]. Sort the objects by name before dropping them. +# +proc drop_all_tables_and_views {db} { + set SQL { + SELECT name, type FROM sqlite_master + WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' + ORDER BY 1 + } + foreach {z t} [db eval $SQL] { + db eval "DROP $t $z" + } +} + foreach {tn before after} { 1 {CREATE TABLE t1(a INTEGER, b TEXT, c BLOB)} {CREATE TABLE t1(a INTEGER, d TEXT, c BLOB)} @@ -324,23 +338,17 @@ do_execsql_test 8.4.4 { SELECT sql FROM sqlite_master WHERE name='xxx'; } {{CREATE VIEW xxx AS SELECT a FROM b1 UNION SELECT hello FROM b2 ORDER BY 1 COLLATE nocase}} -do_execsql_test 8.4.5 { +do_catchsql_test 8.4.5 { CREATE VIEW zzz AS SELECT george, ringo FROM b1; ALTER TABLE b1 RENAME a TO aaa; - SELECT sql FROM sqlite_master WHERE name = 'zzz' -} {{CREATE VIEW zzz AS SELECT george, ringo FROM b1}} +} {1 {error processing view zzz: no such column: george}} #------------------------------------------------------------------------- # More triggers. # proc do_rename_column_test {tn old new lSchema} { - for {set i 0} {$i < 2} {incr i} { - # DROP all tables and views in database. - set sql "SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1" - foreach nm [db eval $sql] { db eval "DROP TABLE $nm" } - set sql "SELECT name FROM sqlite_master WHERE type='view' ORDER BY 1" - foreach nm [db eval $sql] { db eval "DROP VIEW $nm" } + drop_all_tables_and_views db set lSorted [list] foreach sql $lSchema { @@ -353,7 +361,7 @@ proc do_rename_column_test {tn old new lSchema} { SELECT sql FROM sqlite_master WHERE sql!='' ORDER BY 1 } $lSorted - if {0 && $i==1} { + if {$i==1} { db close sqlite3 db test.db } @@ -432,5 +440,30 @@ foreach {tn old new lSchema} { do_rename_column_test 10.$tn $old $new $lSchema } +#-------------------------------------------------------------------------- +# Test that if a view or trigger refers to a virtual table for which the +# module is not available, RENAME COLUMN cannot proceed. +# +reset_db +register_echo_module db +do_execsql_test 11.0 { + CREATE TABLE x1(a, b, c); + CREATE VIRTUAL TABLE e1 USING echo(x1); +} +db close +sqlite3 db test.db + +do_execsql_test 11.1 { + ALTER TABLE x1 RENAME b TO bbb; + SELECT sql FROM sqlite_master; +} { {CREATE TABLE x1(a, bbb, c)} {CREATE VIRTUAL TABLE e1 USING echo(x1)} } + +do_execsql_test 11.2 { + CREATE VIEW v1 AS SELECT e1.*, x1.c FROM e1, x1; +} + +do_catchsql_test 11.3 { + ALTER TABLE x1 RENAME c TO ccc; +} {1 {error processing view v1: no such module: echo}} finish_test