From 2549e4cc2fef9fc969a9efdc723c2bcf1ac69f1f Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 8 Dec 2020 14:29:03 +0000 Subject: [PATCH 01/13] Enhance UPSERT parsing to allow multiple ON CONFLICT clauses. Only the very last clause may omit the conflict target, but the conflict target may now be omitted for the DO UPDATE resolution. FossilOrigin-Name: 2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae --- manifest | 19 +++-- manifest.uuid | 2 +- src/parse.y | 19 ++--- src/sqliteInt.h | 7 +- src/upsert.c | 189 +++++++++++++++++++++++++++--------------------- 5 files changed, 130 insertions(+), 106 deletions(-) diff --git a/manifest b/manifest index 18c17599b4..9471059ac9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbad\sassert()\sin\smath1Func(). -D 2020-12-07T23:14:25.210 +C Enhance\sUPSERT\sparsing\sto\sallow\smultiple\sON\sCONFLICT\sclauses.\s\sOnly\sthe\nvery\slast\sclause\smay\somit\sthe\sconflict\starget,\sbut\sthe\sconflict\starget\smay\nnow\sbe\somitted\sfor\sthe\sDO\sUPDATE\sresolution. +D 2020-12-08T14:29:03.533 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -529,7 +529,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f -F src/parse.y 9ce4dfb772608ed5bd3c32f33e943e021e3b06cfd2c01932d4280888fdd2ebed +F src/parse.y 72b884c73f2b446e7dc4c7169ec7fbb82e0e292eec733fcf554f0fde46f269f6 F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177 F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h 6ab40b33a1f5edbb7d71c78e82e0f9c5291dcff4704df8e4f0ab0d9c1a0c06af +F src/sqliteInt.h 351d29fad669d5c98066a89ab48259d451379edac3c24773c3c8ac5df66fd8ff F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78 +F src/upsert.c 25673d007c2408fec47a6326b6d7ac265abd2cbc162d11f3b3c333de27d3c78a F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1888,7 +1888,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 99ff6418492adcbaf2be728737735afa1c2997de5868395e69c53d08fc14491f -R 747ed0f69719082482a4ff82786fa288 +P 4b286129138d44e6f8e9b3450289941e01d20fdfb9d0b5d846031425e8ca6b49 +R 204aa888c3a8b96c5008bc9a579603f7 +T *branch * generalized-upsert +T *sym-generalized-upsert * +T -sym-trunk * U drh -Z ac855459dfaf775bb9ffc51142afdf45 +Z d13edca14ac31a65fc7ae7d7fdc7eaff diff --git a/manifest.uuid b/manifest.uuid index 7e5c04ae26..0eeaf11f2a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4b286129138d44e6f8e9b3450289941e01d20fdfb9d0b5d846031425e8ca6b49 \ No newline at end of file +2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index d3ec2b3da6..f28bb47414 100644 --- a/src/parse.y +++ b/src/parse.y @@ -952,20 +952,17 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES. } %type upsert {Upsert*} - -// Because upsert only occurs at the tip end of the INSERT rule for cmd, -// there is never a case where the value of the upsert pointer will not -// be destroyed by the cmd action. So comment-out the destructor to -// avoid unreachable code. -//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);} +%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);} upsert(A) ::= . { A = 0; } upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) - DO UPDATE SET setlist(Z) where_opt(W). - { A = sqlite3UpsertNew(pParse->db,T,TW,Z,W);} -upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING. - { A = sqlite3UpsertNew(pParse->db,T,TW,0,0); } + DO UPDATE SET setlist(Z) where_opt(W) upsert(N). + { A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);} +upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N). + { A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); } upsert(A) ::= ON CONFLICT DO NOTHING. - { A = sqlite3UpsertNew(pParse->db,0,0,0,0); } + { A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } +upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W). + { A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);} %type insert_cmd {int} insert_cmd(A) ::= INSERT orconf(R). {A = R;} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 438f79c092..68c8680600 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3071,6 +3071,7 @@ struct Upsert { Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ + Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ /* The fields above comprise the parse tree for the upsert clause. ** The fields below are used to transfer information from the INSERT ** processing down into the UPDATE processing while generating code. @@ -4824,15 +4825,15 @@ const char *sqlite3JournalModename(int); #define sqlite3WithDelete(x,y) #endif #ifndef SQLITE_OMIT_UPSERT - Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*); + Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); void sqlite3UpsertDelete(sqlite3*,Upsert*); Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); #else -#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0) +#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) #define sqlite3UpsertDelete(x,y) -#define sqlite3UpsertDup(x,y) ((Upsert*)0) +#define sqlite3UpsertDup(x,y) ((Upsert*)0) #endif diff --git a/src/upsert.c b/src/upsert.c index 9a33f75d0a..ddd7c18428 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -18,15 +18,21 @@ /* ** Free a list of Upsert objects */ -void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ - if( p ){ +static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){ + do{ + Upsert *pNext = p->pNextUpsert; sqlite3ExprListDelete(db, p->pUpsertTarget); sqlite3ExprDelete(db, p->pUpsertTargetWhere); sqlite3ExprListDelete(db, p->pUpsertSet); sqlite3ExprDelete(db, p->pUpsertWhere); sqlite3DbFree(db, p); - } + p = pNext; + }while( p ); } +void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){ + if( p ) upsertDelete(db, p); +} + /* ** Duplicate an Upsert object. @@ -37,7 +43,8 @@ Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){ sqlite3ExprListDup(db, p->pUpsertTarget, 0), sqlite3ExprDup(db, p->pUpsertTargetWhere, 0), sqlite3ExprListDup(db, p->pUpsertSet, 0), - sqlite3ExprDup(db, p->pUpsertWhere, 0) + sqlite3ExprDup(db, p->pUpsertWhere, 0), + sqlite3UpsertDup(db, p->pNextUpsert) ); } @@ -49,7 +56,8 @@ Upsert *sqlite3UpsertNew( ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */ Expr *pTargetWhere, /* Optional WHERE clause on the target */ ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */ - Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */ + Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */ + Upsert *pNext /* Next ON CONFLICT clause in the list */ ){ Upsert *pNew; pNew = sqlite3DbMallocRaw(db, sizeof(Upsert)); @@ -58,6 +66,7 @@ Upsert *sqlite3UpsertNew( sqlite3ExprDelete(db, pTargetWhere); sqlite3ExprListDelete(db, pSet); sqlite3ExprDelete(db, pWhere); + sqlite3UpsertDelete(db, pNext); return 0; }else{ pNew->pUpsertTarget = pTarget; @@ -65,6 +74,7 @@ Upsert *sqlite3UpsertNew( pNew->pUpsertSet = pSet; pNew->pUpsertWhere = pWhere; pNew->pUpsertIdx = 0; + pNew->pNextUpsert = pNext; } return pNew; } @@ -89,6 +99,7 @@ int sqlite3UpsertAnalyzeTarget( Expr *pTerm; /* One term of the conflict-target clause */ NameContext sNC; /* Context for resolving symbolic names */ Expr sCol[2]; /* Index column converted into an Expr */ + int nClause = 0; /* Counter of ON CONFLICT clauses */ assert( pTabList->nSrc==1 ); assert( pTabList->a[0].pTab!=0 ); @@ -102,87 +113,99 @@ int sqlite3UpsertAnalyzeTarget( memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; - rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); - if( rc ) return rc; - rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); - if( rc ) return rc; - - /* Check to see if the conflict target matches the rowid. */ - pTab = pTabList->a[0].pTab; - pTarget = pUpsert->pUpsertTarget; - iCursor = pTabList->a[0].iCursor; - if( HasRowid(pTab) - && pTarget->nExpr==1 - && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN - && pTerm->iColumn==XN_ROWID - ){ - /* The conflict-target is the rowid of the primary table */ - assert( pUpsert->pUpsertIdx==0 ); - return SQLITE_OK; - } - - /* Initialize sCol[0..1] to be an expression parse tree for a - ** single column of an index. The sCol[0] node will be the TK_COLLATE - ** operator and sCol[1] will be the TK_COLUMN operator. Code below - ** will populate the specific collation and column number values - ** prior to comparing against the conflict-target expression. - */ - memset(sCol, 0, sizeof(sCol)); - sCol[0].op = TK_COLLATE; - sCol[0].pLeft = &sCol[1]; - sCol[1].op = TK_COLUMN; - sCol[1].iTable = pTabList->a[0].iCursor; - - /* Check for matches against other indexes */ - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int ii, jj, nn; - if( !IsUniqueIndex(pIdx) ) continue; - if( pTarget->nExpr!=pIdx->nKeyCol ) continue; - if( pIdx->pPartIdxWhere ){ - if( pUpsert->pUpsertTargetWhere==0 ) continue; - if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, - pIdx->pPartIdxWhere, iCursor)!=0 ){ - continue; - } - } - nn = pIdx->nKeyCol; - for(ii=0; iiazColl[ii]; - if( pIdx->aiColumn[ii]==XN_EXPR ){ - assert( pIdx->aColExpr!=0 ); - assert( pIdx->aColExpr->nExpr>ii ); - pExpr = pIdx->aColExpr->a[ii].pExpr; - if( pExpr->op!=TK_COLLATE ){ - sCol[0].pLeft = pExpr; - pExpr = &sCol[0]; - } - }else{ - sCol[0].pLeft = &sCol[1]; - sCol[1].iColumn = pIdx->aiColumn[ii]; - pExpr = &sCol[0]; - } - for(jj=0; jja[jj].pExpr, pExpr,iCursor)<2 ){ - break; /* Column ii of the index matches column jj of target */ - } - } - if( jj>=nn ){ - /* The target contains no match for column jj of the index */ - break; - } - } - if( iipUpsertTarget; + pUpsert=pUpsert->pNextUpsert, nClause++){ + rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); + if( rc ) return rc; + rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); + if( rc ) return rc; + + /* Check to see if the conflict target matches the rowid. */ + pTab = pTabList->a[0].pTab; + pTarget = pUpsert->pUpsertTarget; + iCursor = pTabList->a[0].iCursor; + if( HasRowid(pTab) + && pTarget->nExpr==1 + && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN + && pTerm->iColumn==XN_ROWID + ){ + /* The conflict-target is the rowid of the primary table */ + assert( pUpsert->pUpsertIdx==0 ); continue; } - pUpsert->pUpsertIdx = pIdx; - return SQLITE_OK; + + /* Initialize sCol[0..1] to be an expression parse tree for a + ** single column of an index. The sCol[0] node will be the TK_COLLATE + ** operator and sCol[1] will be the TK_COLUMN operator. Code below + ** will populate the specific collation and column number values + ** prior to comparing against the conflict-target expression. + */ + memset(sCol, 0, sizeof(sCol)); + sCol[0].op = TK_COLLATE; + sCol[0].pLeft = &sCol[1]; + sCol[1].op = TK_COLUMN; + sCol[1].iTable = pTabList->a[0].iCursor; + + /* Check for matches against other indexes */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int ii, jj, nn; + if( !IsUniqueIndex(pIdx) ) continue; + if( pTarget->nExpr!=pIdx->nKeyCol ) continue; + if( pIdx->pPartIdxWhere ){ + if( pUpsert->pUpsertTargetWhere==0 ) continue; + if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere, + pIdx->pPartIdxWhere, iCursor)!=0 ){ + continue; + } + } + nn = pIdx->nKeyCol; + for(ii=0; iiazColl[ii]; + if( pIdx->aiColumn[ii]==XN_EXPR ){ + assert( pIdx->aColExpr!=0 ); + assert( pIdx->aColExpr->nExpr>ii ); + pExpr = pIdx->aColExpr->a[ii].pExpr; + if( pExpr->op!=TK_COLLATE ){ + sCol[0].pLeft = pExpr; + pExpr = &sCol[0]; + } + }else{ + sCol[0].pLeft = &sCol[1]; + sCol[1].iColumn = pIdx->aiColumn[ii]; + pExpr = &sCol[0]; + } + for(jj=0; jja[jj].pExpr,pExpr,iCursor)<2 ){ + break; /* Column ii of the index matches column jj of target */ + } + } + if( jj>=nn ){ + /* The target contains no match for column jj of the index */ + break; + } + } + if( iipUpsertIdx = pIdx; + break; + } + if( pUpsert->pUpsertIdx==0 ){ + char zWhich[16]; + if( nClause==0 && pUpsert->pNextUpsert==0 ){ + zWhich[0] = 0; + }else{ + sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1); + } + sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any " + "PRIMARY KEY or UNIQUE constraint", zWhich); + return SQLITE_ERROR; + } } - sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any " - "PRIMARY KEY or UNIQUE constraint"); - return SQLITE_ERROR; + return SQLITE_OK; } /* From 20b86324a1b3c78b5eee3872d1934ec21c911bfa Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 9 Dec 2020 01:34:48 +0000 Subject: [PATCH 02/13] Initialize all terms in the ON CONFLICT clause stack. FossilOrigin-Name: 5e683fd1cbde53f37cf8a2b1e981191e2b29e3376db554691767f33c37c7547e --- manifest | 15 ++++++--------- manifest.uuid | 2 +- src/insert.c | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 9471059ac9..b6cfaef7fe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sUPSERT\sparsing\sto\sallow\smultiple\sON\sCONFLICT\sclauses.\s\sOnly\sthe\nvery\slast\sclause\smay\somit\sthe\sconflict\starget,\sbut\sthe\sconflict\starget\smay\nnow\sbe\somitted\sfor\sthe\sDO\sUPDATE\sresolution. -D 2020-12-08T14:29:03.533 +C Initialize\sall\sterms\sin\sthe\sON\sCONFLICT\sclause\sstack. +D 2020-12-09T01:34:48.233 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 7e081d33aab4a9d761c39dccf3c3872c35501565d2ed9db66301918d23bc7901 +F src/insert.c 2fdfd14bf872501f041cb2220632dace2df63342151b9b30ebfb798df3f14632 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -1888,10 +1888,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 4b286129138d44e6f8e9b3450289941e01d20fdfb9d0b5d846031425e8ca6b49 -R 204aa888c3a8b96c5008bc9a579603f7 -T *branch * generalized-upsert -T *sym-generalized-upsert * -T -sym-trunk * +P 2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae +R 522b57bc98ce1a3ca7ad7f44cd4944b4 U drh -Z d13edca14ac31a65fc7ae7d7fdc7eaff +Z 6380cbc8ce17e23f96dde835eaec54ed diff --git a/manifest.uuid b/manifest.uuid index 0eeaf11f2a..f0390ac713 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae \ No newline at end of file +5e683fd1cbde53f37cf8a2b1e981191e2b29e3376db554691767f33c37c7547e \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 393cd528f1..137590a5e5 100644 --- a/src/insert.c +++ b/src/insert.c @@ -975,6 +975,7 @@ void sqlite3Insert( } #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ + Upsert *pNx; if( IsVirtual(pTab) ){ sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", pTab->zName); @@ -988,13 +989,17 @@ void sqlite3Insert( goto insert_cleanup; } pTabList->a[0].iCursor = iDataCur; - pUpsert->pUpsertSrc = pTabList; - pUpsert->regData = regData; - pUpsert->iDataCur = iDataCur; - pUpsert->iIdxCur = iIdxCur; - if( pUpsert->pUpsertTarget ){ - sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert); - } + pNx = pUpsert; + do{ + pNx->pUpsertSrc = pTabList; + pNx->regData = regData; + pNx->iDataCur = iDataCur; + pNx->iIdxCur = iIdxCur; + if( pNx->pUpsertTarget ){ + sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx); + } + pNx = pNx->pNextUpsert; + }while( pNx!=0 ); } #endif From 5602777e8fc872e638c0d9ccc116bebfd66e02e9 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 9 Dec 2020 13:11:02 +0000 Subject: [PATCH 03/13] Improved comments in sqliteInt.h. No changes to code. FossilOrigin-Name: 8ccb8d1d55fa5aaf625c30f0e7c10aa403d79b5574dbdfa3fd0271a4e546f7e3 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/sqliteInt.h | 14 ++++++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index b6cfaef7fe..ce2af91be7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Initialize\sall\sterms\sin\sthe\sON\sCONFLICT\sclause\sstack. -D 2020-12-09T01:34:48.233 +C Improved\scomments\sin\ssqliteInt.h.\s\sNo\schanges\sto\scode. +D 2020-12-09T13:11:02.579 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h 351d29fad669d5c98066a89ab48259d451379edac3c24773c3c8ac5df66fd8ff +F src/sqliteInt.h f8e462357ee34e6859f20e7f83adcca0809bbc1033a85a19d9be50a09930717a F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1888,7 +1888,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 2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae -R 522b57bc98ce1a3ca7ad7f44cd4944b4 +P 5e683fd1cbde53f37cf8a2b1e981191e2b29e3376db554691767f33c37c7547e +R f2e8184a51a526d27f353dba2479feca U drh -Z 6380cbc8ce17e23f96dde835eaec54ed +Z 0139a36f3f85012162d949eb364157e1 diff --git a/manifest.uuid b/manifest.uuid index f0390ac713..0de188d914 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5e683fd1cbde53f37cf8a2b1e981191e2b29e3376db554691767f33c37c7547e \ No newline at end of file +8ccb8d1d55fa5aaf625c30f0e7c10aa403d79b5574dbdfa3fd0271a4e546f7e3 \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 68c8680600..06ddb3a613 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2304,16 +2304,22 @@ struct FKey { ** is returned. REPLACE means that preexisting database rows that caused ** a UNIQUE constraint violation are removed so that the new insert or ** update can proceed. Processing continues and no error is reported. +** UPDATE applies to insert operations only and means that the insert +** is omitted and the DO UPDATE clause of an upsert is run instead. ** -** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys. ** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the ** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign -** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** key is set to NULL. SETDFLT means that the foreign key is set +** to its default value. CASCADE means that a DELETE or UPDATE of the ** referenced table row is propagated into the row that holds the ** foreign key. ** +** The OE_Default value is a place holder that means to use whatever +** conflict resolution algorthm is required from context. +** ** The following symbolic values are used to record which type -** of action to take. +** of conflict resolution action to take. */ #define OE_None 0 /* There is no constraint to check */ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ @@ -3067,7 +3073,7 @@ struct NameContext { ** WHERE clause is omitted. */ struct Upsert { - ExprList *pUpsertTarget; /* Optional description of conflicting index */ + ExprList *pUpsertTarget; /* Optional description of conflict target */ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ From e84ad92f17b96f1952f27645042bbf83fe6d0da5 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 9 Dec 2020 20:30:47 +0000 Subject: [PATCH 04/13] For upsert, the constraint check code generator uses a copy of the index list for the target table, which can potentially be reordered. FossilOrigin-Name: 3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a --- manifest | 19 +++++++++++-------- manifest.uuid | 2 +- src/insert.c | 48 +++++++++++++++++++++++++++++++++++++----------- src/sqliteInt.h | 12 ++++++++---- src/upsert.c | 4 ++-- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/manifest b/manifest index ce2af91be7..7766814266 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improved\scomments\sin\ssqliteInt.h.\s\sNo\schanges\sto\scode. -D 2020-12-09T13:11:02.579 +C For\supsert,\sthe\sconstraint\scheck\scode\sgenerator\suses\sa\scopy\sof\sthe\sindex\slist\nfor\sthe\starget\stable,\swhich\scan\spotentially\sbe\sreordered. +D 2020-12-09T20:30:47.480 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 2fdfd14bf872501f041cb2220632dace2df63342151b9b30ebfb798df3f14632 +F src/insert.c df28564b7d79f146266e906549687c74e3a5e9c4d8ba8697ef9c479ad83407e0 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h f8e462357ee34e6859f20e7f83adcca0809bbc1033a85a19d9be50a09930717a +F src/sqliteInt.h f01f37844eed0fab3e1fa8efe72cc327cf8e2ac121688ec94199d610978ecb09 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 25673d007c2408fec47a6326b6d7ac265abd2cbc162d11f3b3c333de27d3c78a +F src/upsert.c 803c383d493546c71580e9e532d6c7f87fc337316a2ffc202ba1812158dfa8fb F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1888,7 +1888,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5e683fd1cbde53f37cf8a2b1e981191e2b29e3376db554691767f33c37c7547e -R f2e8184a51a526d27f353dba2479feca +P 8ccb8d1d55fa5aaf625c30f0e7c10aa403d79b5574dbdfa3fd0271a4e546f7e3 +R cb21a770762087a569cd96190733ebe7 +T *branch * generalized-upsert-ex1 +T *sym-generalized-upsert-ex1 * +T -sym-generalized-upsert * U drh -Z 0139a36f3f85012162d949eb364157e1 +Z d9a35c3fa2c53151f722f0a994f8bfdd diff --git a/manifest.uuid b/manifest.uuid index 0de188d914..ce7066ad55 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8ccb8d1d55fa5aaf625c30f0e7c10aa403d79b5574dbdfa3fd0271a4e546f7e3 \ No newline at end of file +3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 137590a5e5..f10587f9f2 100644 --- a/src/insert.c +++ b/src/insert.c @@ -976,6 +976,9 @@ void sqlite3Insert( #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ Upsert *pNx; + int nIdx; + Index *pIdxList; + int *aReg; if( IsVirtual(pTab) ){ sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", pTab->zName); @@ -1000,6 +1003,26 @@ void sqlite3Insert( } pNx = pNx->pNextUpsert; }while( pNx!=0 ); + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ + assert( pIdx ); + assert( aRegIdx[nIdx]>0 ); + } + if( nIdx==0 ){ + pUpsert->pIdxList = 0; + }else{ + u64 nByte = sizeof(Index)*nIdx + sizeof(int)*(nIdx+2); + pIdxList = sqlite3DbMallocRaw(db, nByte); + if( pIdxList==0 ) goto insert_cleanup; + aReg = (int*)&pIdxList[nIdx]; + for(i=0, pIdx=pTab->pIndex; ipNext, i++){ + memcpy(&pIdxList[i], pIdx, sizeof(Index)); + pIdxList[i].pNext = 0; + if( i ) pIdxList[i-1].pNext = &pIdxList[i]; + aReg[i] = aRegIdx[i]; + } + aReg[i] = aRegIdx[i]; + pUpsert->pIdxList = pIdxList; + } } #endif @@ -1512,7 +1535,7 @@ void sqlite3GenerateConstraintChecks( ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ - Index *pPk = 0; /* The PRIMARY KEY index */ + Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */ sqlite3 *db; /* Database connection */ int i; /* loop counter */ int ix; /* Index loop counter */ @@ -1930,7 +1953,8 @@ void sqlite3GenerateConstraintChecks( ** This loop also handles the case of the PRIMARY KEY index for a ** WITHOUT ROWID table. */ - for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){ + pIdx = pUpsert ? pUpsert->pIdxList : pTab->pIndex; + for(ix=0; pIdx; pIdx=pIdx->pNext, ix++){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ @@ -1938,7 +1962,7 @@ void sqlite3GenerateConstraintChecks( int addrConflictCk; /* First opcode in the conflict check logic */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ addrUniqueOk = upsertJump+1; upsertBypass = sqlite3VdbeGoto(v, 0); VdbeComment((v, "Skip upsert subroutine")); @@ -1946,7 +1970,7 @@ void sqlite3GenerateConstraintChecks( }else{ addrUniqueOk = sqlite3VdbeMakeLabel(pParse); } - if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ + if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx->zName==pIdx->zName) ){ sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } @@ -1999,7 +2023,7 @@ void sqlite3GenerateConstraintChecks( ** of a WITHOUT ROWID table and there has been no change the ** primary key, then no collision is possible. The collision detection ** logic below can all be skipped. */ - if( isUpdate && pPk==pIdx && pkChng==0 ){ + if( isUpdate && pPk && pPk->zName==pIdx->zName && pkChng==0 ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } @@ -2017,7 +2041,7 @@ void sqlite3GenerateConstraintChecks( } /* Figure out if the upsert clause applies to this index */ - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ if( pUpsert->pUpsertSet==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ @@ -2037,7 +2061,7 @@ void sqlite3GenerateConstraintChecks( ** is invoked. */ #ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ - && pPk==pIdx /* Condition 2 */ + && pPk && pPk->zName==pIdx->zName /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) @@ -2056,7 +2080,8 @@ void sqlite3GenerateConstraintChecks( regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ - regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); + regR = (pPk && pIdx->zName==pPk->zName) ? + regIdx : sqlite3GetTempRange(pParse, nPkField); if( isUpdate || onError==OE_Replace ){ if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); @@ -2071,7 +2096,7 @@ void sqlite3GenerateConstraintChecks( int x; /* Extract the PRIMARY KEY from the end of the index entry and ** store it in registers regR..regR+nPk-1 */ - if( pIdx!=pPk ){ + if( pPk && pIdx->zName!=pPk->zName ){ for(i=0; inKeyCol; i++){ assert( pPk->aiColumn[i]>=0 ); x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); @@ -2152,7 +2177,8 @@ void sqlite3GenerateConstraintChecks( } sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, regR, nPkField, 0, OE_Replace, - (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur); + (pPk && pIdx->zName==pPk->zName ? ONEPASS_SINGLE : ONEPASS_OFF), + iThisCur); if( pTrigger && isUpdate ){ sqlite3VdbeAddOp1(v, OP_CursorUnlock, iDataCur); } @@ -2208,7 +2234,7 @@ void sqlite3GenerateConstraintChecks( break; } } - if( pUpIdx==pIdx ){ + if( pUpIdx && pUpIdx->zName==pIdx->zName ){ sqlite3VdbeGoto(v, upsertJump+1); sqlite3VdbeJumpHere(v, upsertBypass); }else{ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 06ddb3a613..8661f9058c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3078,10 +3078,14 @@ struct Upsert { ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ - /* The fields above comprise the parse tree for the upsert clause. - ** The fields below are used to transfer information from the INSERT - ** processing down into the UPDATE processing while generating code. - ** Upsert owns the memory allocated above, but not the memory below. */ + /* Above this point is the parse tree for the ON CONFLICT clauses. + ** The next group of fields stores intermediate data. */ + Index *pIdxList; + /* All fields above are owned by the Upsert object and must be freed + ** when the Upsert is destroyed. The fields below are used to transfer + ** information from the INSERT processing down into the UPDATE processing + ** while generating code. The fields below are owned by the INSERT + ** statement and will be freed by INSERT processing. */ Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */ SrcList *pUpsertSrc; /* Table to be updated */ int regData; /* First register holding array of VALUES */ diff --git a/src/upsert.c b/src/upsert.c index ddd7c18428..0f1f6d8a32 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -25,6 +25,7 @@ static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){ sqlite3ExprDelete(db, p->pUpsertTargetWhere); sqlite3ExprListDelete(db, p->pUpsertSet); sqlite3ExprDelete(db, p->pUpsertWhere); + sqlite3DbFree(db, p->pIdxList); sqlite3DbFree(db, p); p = pNext; }while( p ); @@ -60,7 +61,7 @@ Upsert *sqlite3UpsertNew( Upsert *pNext /* Next ON CONFLICT clause in the list */ ){ Upsert *pNew; - pNew = sqlite3DbMallocRaw(db, sizeof(Upsert)); + pNew = sqlite3DbMallocZero(db, sizeof(Upsert)); if( pNew==0 ){ sqlite3ExprListDelete(db, pTarget); sqlite3ExprDelete(db, pTargetWhere); @@ -73,7 +74,6 @@ Upsert *sqlite3UpsertNew( pNew->pUpsertTargetWhere = pTargetWhere; pNew->pUpsertSet = pSet; pNew->pUpsertWhere = pWhere; - pNew->pUpsertIdx = 0; pNew->pNextUpsert = pNext; } return pNew; From 91f2717f22e5a7c3feab8224a16416677ad28e47 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 10 Dec 2020 12:49:26 +0000 Subject: [PATCH 05/13] The DO UPDATE code generator searches for the correct ON CONFLICT clause to use. FossilOrigin-Name: a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a --- manifest | 17 +++++++---------- manifest.uuid | 2 +- src/sqliteInt.h | 3 ++- src/upsert.c | 26 +++++++++++++++++++++----- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/manifest b/manifest index 7766814266..abce3073d7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C For\supsert,\sthe\sconstraint\scheck\scode\sgenerator\suses\sa\scopy\sof\sthe\sindex\slist\nfor\sthe\starget\stable,\swhich\scan\spotentially\sbe\sreordered. -D 2020-12-09T20:30:47.480 +C The\sDO\sUPDATE\scode\sgenerator\ssearches\sfor\sthe\scorrect\sON\sCONFLICT\sclause\sto\nuse. +D 2020-12-10T12:49:26.425 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h f01f37844eed0fab3e1fa8efe72cc327cf8e2ac121688ec94199d610978ecb09 +F src/sqliteInt.h 0e042bd76044617ae919a294778ceacdb00a405d60de1b94c8ee4d2b81829bdb F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 803c383d493546c71580e9e532d6c7f87fc337316a2ffc202ba1812158dfa8fb +F src/upsert.c 49170532578f2f30984b35e2c1e2f308b089c6814edf384d32158a53b25fc930 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1888,10 +1888,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 8ccb8d1d55fa5aaf625c30f0e7c10aa403d79b5574dbdfa3fd0271a4e546f7e3 -R cb21a770762087a569cd96190733ebe7 -T *branch * generalized-upsert-ex1 -T *sym-generalized-upsert-ex1 * -T -sym-generalized-upsert * +P 3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a +R 8dc7164868d12444f67e9c3abe760e73 U drh -Z d9a35c3fa2c53151f722f0a994f8bfdd +Z 978ca06206ec1f9d5cc426705017394f diff --git a/manifest.uuid b/manifest.uuid index ce7066ad55..88e882cea1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a \ No newline at end of file +a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8661f9058c..23df9cc710 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3086,11 +3086,12 @@ struct Upsert { ** information from the INSERT processing down into the UPDATE processing ** while generating code. The fields below are owned by the INSERT ** statement and will be freed by INSERT processing. */ - Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */ + Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */ SrcList *pUpsertSrc; /* Table to be updated */ int regData; /* First register holding array of VALUES */ int iDataCur; /* Index of the data cursor */ int iIdxCur; /* Index of the first index cursor */ + int addrGenericUpdate; /* Address of routine for generic DO UPDATE */ }; /* diff --git a/src/upsert.c b/src/upsert.c index 0f1f6d8a32..63b9144419 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -229,11 +229,27 @@ void sqlite3UpsertDoUpdate( SrcList *pSrc; /* FROM clause for the UPDATE */ int iDataCur; int i; + Upsert *pTop = pUpsert; assert( v!=0 ); assert( pUpsert!=0 ); - VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); iDataCur = pUpsert->iDataCur; + while( + pUpsert->pUpsertTarget!=0 + && (pUpsert->pUpsertIdx==0 ? pIdx!=0 : + pUpsert->pUpsertIdx->zName!=pIdx->zName) + ){ + assert( pUpsert->pNextUpsert!=0 ); + pUpsert = pUpsert->pNextUpsert; + } + if( pUpsert->addrGenericUpdate>0 ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, pUpsert->addrGenericUpdate); + return; + } + VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); + if( pUpsert->pUpsertTarget==0 ){ + pUpsert->addrGenericUpdate = sqlite3VdbeCurrentAddr(v); + } if( pIdx && iCur!=iDataCur ){ if( HasRowid(pTab) ){ int regRowid = sqlite3GetTempReg(pParse); @@ -263,13 +279,13 @@ void sqlite3UpsertDoUpdate( sqlite3VdbeJumpHere(v, i); } } - /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So - ** we have to make a copy before passing it down into sqlite3Update() */ - pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0); + /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does. + ** So we have to make a copy before passing it down into sqlite3Update() */ + pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0); /* excluded.* columns of type REAL need to be converted to a hard real */ for(i=0; inCol; i++){ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i); + sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); } } sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet, From daf2761c62103f7bf126f8029dcf5fb473216f5f Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 10 Dec 2020 20:31:25 +0000 Subject: [PATCH 06/13] Use an iterator for the index loop in sqlite3GenerateConstraintChecks(). The idea is that this iterator can be enhanced to traverse the indexes in any order, as required by multi-index UPSERT. FossilOrigin-Name: 64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c --- manifest | 16 ++++----- manifest.uuid | 2 +- src/insert.c | 89 +++++++++++++++++++++++++++++++++++-------------- src/sqliteInt.h | 2 +- src/upsert.c | 2 +- 5 files changed, 75 insertions(+), 36 deletions(-) diff --git a/manifest b/manifest index abce3073d7..293f993ca7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sDO\sUPDATE\scode\sgenerator\ssearches\sfor\sthe\scorrect\sON\sCONFLICT\sclause\sto\nuse. -D 2020-12-10T12:49:26.425 +C Use\san\siterator\sfor\sthe\sindex\sloop\sin\ssqlite3GenerateConstraintChecks().\nThe\sidea\sis\sthat\sthis\siterator\scan\sbe\senhanced\sto\straverse\sthe\sindexes\sin\nany\sorder,\sas\srequired\sby\smulti-index\sUPSERT. +D 2020-12-10T20:31:25.985 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c df28564b7d79f146266e906549687c74e3a5e9c4d8ba8697ef9c479ad83407e0 +F src/insert.c ac236526a7f239d420291fae1bf7ea56c9e54f335598315a0ddf8ebb5a110120 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h 0e042bd76044617ae919a294778ceacdb00a405d60de1b94c8ee4d2b81829bdb +F src/sqliteInt.h 72c7bfc4831f171d25b7b03c9fb152f1a3bbf55f51d5bbcdef313ebfa5873bea F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 49170532578f2f30984b35e2c1e2f308b089c6814edf384d32158a53b25fc930 +F src/upsert.c 4b960968084a1d2a301ddbc782f625dbb1c02e0e8d0b0d6ad4a1986ecf305729 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1888,7 +1888,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 3194c00c2c6a32bdfd5acc9fda5b38ae131d20cd3b7aea8512a41b2e76808f6a -R 8dc7164868d12444f67e9c3abe760e73 +P a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a +R 57e2c4174298e4f52a8ae7fd60045bd5 U drh -Z 978ca06206ec1f9d5cc426705017394f +Z 27b1defd588019eb0041967767579913 diff --git a/manifest.uuid b/manifest.uuid index 88e882cea1..1fd8f4a00d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a \ No newline at end of file +64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index f10587f9f2..44cd8a3390 100644 --- a/src/insert.c +++ b/src/insert.c @@ -976,9 +976,6 @@ void sqlite3Insert( #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ Upsert *pNx; - int nIdx; - Index *pIdxList; - int *aReg; if( IsVirtual(pTab) ){ sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", pTab->zName); @@ -1003,26 +1000,6 @@ void sqlite3Insert( } pNx = pNx->pNextUpsert; }while( pNx!=0 ); - for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ - assert( pIdx ); - assert( aRegIdx[nIdx]>0 ); - } - if( nIdx==0 ){ - pUpsert->pIdxList = 0; - }else{ - u64 nByte = sizeof(Index)*nIdx + sizeof(int)*(nIdx+2); - pIdxList = sqlite3DbMallocRaw(db, nByte); - if( pIdxList==0 ) goto insert_cleanup; - aReg = (int*)&pIdxList[nIdx]; - for(i=0, pIdx=pTab->pIndex; ipNext, i++){ - memcpy(&pIdxList[i], pIdx, sizeof(Index)); - pIdxList[i].pNext = 0; - if( i ) pIdxList[i-1].pNext = &pIdxList[i]; - aReg[i] = aRegIdx[i]; - } - aReg[i] = aRegIdx[i]; - pUpsert->pIdxList = pIdxList; - } } #endif @@ -1427,6 +1404,62 @@ int sqlite3ExprReferencesUpdatedColumn( return w.eCode!=0; } +/* +** The sqlite3GenerateConstraintChecks() routine usually wants to visit +** the indexes of a table in the order provided in the Table->pIndex list. +** However, sometimes (rarely - when there is an upsert) it wants to visit +** the indexes in a different order. The following data structures accomplish +** this. +** +** The IndexIterator object is used to walk through all of the indexes +** of a table in either Index.pNext order, or in some other order established +** by an array of IndexListTerm objects. +*/ +typedef struct IndexListTerm IndexListTerm; +typedef struct IndexIterator IndexIterator; +struct IndexIterator { + int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */ + int i; /* Index of the current item from the list */ + union { + struct { /* Use this object for eType==0: A Index.pNext list */ + Index *pIdx; /* The current Index */ + } lx; + struct { /* Use this object for eType==1; Array of IndexListTerm */ + int nIdx; /* Size of the array */ + IndexListTerm *aIdx; /* Array of IndexListTerms */ + } ax; + } u; +}; + +/* When IndexIterator.eType==1, then each index is an array of instances +** of the following object +*/ +struct IndexListTerm { + Index *p; /* The index */ + int ix; /* Which entry in the original Table.pIndex list is this index*/ +}; + +/* Return the first index on the list */ +static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ + int i = pIter->i; + *pIx = i; + return pIter->eType ? pIter->u.ax.aIdx[i].p : pIter->u.lx.pIdx; +} + +/* Return the next index from the list. Return NULL when out of indexes */ +static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ + int i = ++pIter->i; + if( pIter->eType ){ + if( i>=pIter->u.ax.nIdx ) return 0; + *pIx = pIter->u.ax.aIdx[i].ix; + return pIter->u.ax.aIdx[i].p; + }else{ + *pIx = i; + pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext; + return pIter->u.lx.pIdx; + } +} + /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE ** on table pTab. @@ -1557,6 +1590,7 @@ void sqlite3GenerateConstraintChecks( int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ int nReplaceTrig = 0; /* Number of replace triggers coded */ + IndexIterator ixi; /* Index iterator */ isUpdate = regOldData!=0; db = pParse->db; @@ -1769,6 +1803,9 @@ void sqlite3GenerateConstraintChecks( VdbeComment((v, "UPSERT constraint goes first")); } } + ixi.eType = 0; + ixi.i = 0; + ixi.u.lx.pIdx = pTab->pIndex; /* Determine if it is possible that triggers (either explicitly coded ** triggers or FK resolution actions) might run as a result of deletes @@ -1953,8 +1990,10 @@ void sqlite3GenerateConstraintChecks( ** This loop also handles the case of the PRIMARY KEY index for a ** WITHOUT ROWID table. */ - pIdx = pUpsert ? pUpsert->pIdxList : pTab->pIndex; - for(ix=0; pIdx; pIdx=pIdx->pNext, ix++){ + for(pIdx = indexIteratorFirst(&ixi, &ix); + pIdx; + pIdx = indexIteratorNext(&ixi, &ix) + ){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ int iThisCur; /* Cursor for this UNIQUE index */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 23df9cc710..83993f08c5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3080,7 +3080,7 @@ struct Upsert { Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ - Index *pIdxList; + void *pToFree; /* Free memory when deleting the Upsert object */ /* All fields above are owned by the Upsert object and must be freed ** when the Upsert is destroyed. The fields below are used to transfer ** information from the INSERT processing down into the UPDATE processing diff --git a/src/upsert.c b/src/upsert.c index 63b9144419..6b9bff6851 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -25,7 +25,7 @@ static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){ sqlite3ExprDelete(db, p->pUpsertTargetWhere); sqlite3ExprListDelete(db, p->pUpsertSet); sqlite3ExprDelete(db, p->pUpsertWhere); - sqlite3DbFree(db, p->pIdxList); + sqlite3DbFree(db, p->pToFree); sqlite3DbFree(db, p); p = pNext; }while( p ); From 61e280ad8a3ce9d14c65948265cd8732825e18dd Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 11 Dec 2020 01:17:06 +0000 Subject: [PATCH 07/13] Logic is in place to handle multiple ON CONFLICT clauses, but it does not work. Any use of ON CONFLICT will likely lead to memory faults. This is an incremental check-in to save my place. FossilOrigin-Name: 155142314feb007d526f8f67723636fd50dc52d1cd4d3a67dd93b105c9d5c2be --- manifest | 16 +++--- manifest.uuid | 2 +- src/insert.c | 140 +++++++++++++++++++++++++++++++++--------------- src/sqliteInt.h | 4 ++ src/upsert.c | 41 +++++++++++--- 5 files changed, 142 insertions(+), 61 deletions(-) diff --git a/manifest b/manifest index 293f993ca7..5bb77216d5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Use\san\siterator\sfor\sthe\sindex\sloop\sin\ssqlite3GenerateConstraintChecks().\nThe\sidea\sis\sthat\sthis\siterator\scan\sbe\senhanced\sto\straverse\sthe\sindexes\sin\nany\sorder,\sas\srequired\sby\smulti-index\sUPSERT. -D 2020-12-10T20:31:25.985 +C Logic\sis\sin\splace\sto\shandle\smultiple\sON\sCONFLICT\sclauses,\sbut\sit\sdoes\snot\swork.\nAny\suse\sof\sON\sCONFLICT\swill\slikely\slead\sto\smemory\sfaults.\s\sThis\sis\san\nincremental\scheck-in\sto\ssave\smy\splace. +D 2020-12-11T01:17:06.489 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c ac236526a7f239d420291fae1bf7ea56c9e54f335598315a0ddf8ebb5a110120 +F src/insert.c c8d4bc0dcde4d9d42a1761e919a6a99668000e16840814f8bb643e161894b27f F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h 72c7bfc4831f171d25b7b03c9fb152f1a3bbf55f51d5bbcdef313ebfa5873bea +F src/sqliteInt.h aff99a1938b53530971fcfa7a6a5e6b47f986ddd673660bafb41f1c559747b72 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 4b960968084a1d2a301ddbc782f625dbb1c02e0e8d0b0d6ad4a1986ecf305729 +F src/upsert.c 6471d9e0e5e05547f8a00d7d686714ac08fe89526de133abcf8e1c7a35f2cb82 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1888,7 +1888,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 a47e35ee2d901baaa37e7229d190f934e1b0bd3510147cd4a2a49c4a1411416a -R 57e2c4174298e4f52a8ae7fd60045bd5 +P 64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c +R 750cd21a590114aba81487b95892f11b U drh -Z 27b1defd588019eb0041967767579913 +Z dd643da8c0e77547922105a9f9bf1ab2 diff --git a/manifest.uuid b/manifest.uuid index 1fd8f4a00d..7a1e0b0cb8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c \ No newline at end of file +155142314feb007d526f8f67723636fd50dc52d1cd4d3a67dd93b105c9d5c2be \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 44cd8a3390..5e59221278 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1450,7 +1450,10 @@ static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ int i = ++pIter->i; if( pIter->eType ){ - if( i>=pIter->u.ax.nIdx ) return 0; + if( i>=pIter->u.ax.nIdx ){ + *pIx = i; + return 0; + } *pIx = pIter->u.ax.aIdx[i].ix; return pIter->u.ax.aIdx[i].p; }else{ @@ -1576,11 +1579,11 @@ void sqlite3GenerateConstraintChecks( int onError; /* Conflict resolution strategy */ 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 */ - Index *pUpIdx = 0; /* Index to which to apply the upsert */ - u8 isUpdate; /* True if this is an UPDATE operation */ + Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */ + 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 upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */ + int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */ int ipkTop = 0; /* Top of the IPK uniqueness check */ int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */ /* Variables associated with retesting uniqueness constraints after @@ -1590,7 +1593,7 @@ void sqlite3GenerateConstraintChecks( int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */ Trigger *pTrigger; /* List of DELETE triggers on the table pTab */ int nReplaceTrig = 0; /* Number of replace triggers coded */ - IndexIterator ixi; /* Index iterator */ + IndexIterator sIdxIter; /* Index iterator */ isUpdate = regOldData!=0; db = pParse->db; @@ -1788,24 +1791,64 @@ void sqlite3GenerateConstraintChecks( ** list of indexes attached to a table puts all OE_Replace indexes last ** in the list. See sqlite3CreateIndex() for where that happens. */ - + sIdxIter.eType = 0; + sIdxIter.i = 0; + sIdxIter.u.lx.pIdx = pTab->pIndex; if( pUpsert ){ if( pUpsert->pUpsertTarget==0 ){ - /* An ON CONFLICT DO NOTHING clause, without a constraint-target. - ** Make all unique constraint resolution be OE_Ignore */ - assert( pUpsert->pUpsertSet==0 ); - overrideError = OE_Ignore; - pUpsert = 0; - }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){ - /* 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")); + /* There is just on ON CONFLICT clause and it has no constraint-target */ + assert( pUpsert->pNextUpsert==0 ); + if( pUpsert->pUpsertSet==0 ){ + /* A single ON CONFLICT DO NOTHING clause, without a constraint-target. + ** Make all unique constraint resolution be OE_Ignore */ + overrideError = OE_Ignore; + pUpsert = 0; + }else{ + /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */ + overrideError = OE_Update; + } + }else if( pTab->pIndex!=0 ){ + /* Otherwise, we'll need to run the IndexListTerm array version of the + ** iterator to ensure that all of the ON CONFLICT conditions are + ** checked first and in order. */ + int nIdx, jj; + u64 nByte; + Upsert *pTerm; + u8 *bUsed; + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ + assert( aRegIdx[nIdx]>0 ); + } + sIdxIter.eType = 1; + sIdxIter.u.ax.nIdx = nIdx; + nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx; + sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte); + if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */ + bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx]; + pUpsert->pToFree = sIdxIter.u.ax.aIdx; + for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){ + if( pTerm->pUpsertTarget==0 ) break; + if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */ + jj = 0; + pIdx = pTab->pIndex; + while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){ + pIdx = pIdx->pNext; + jj++; + } + if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */ + bUsed[jj] = 1; + sIdxIter.u.ax.aIdx[i].p = pIdx; + sIdxIter.u.ax.aIdx[i].ix = jj; + i++; + } + for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){ + if( bUsed[jj] ) continue; + sIdxIter.u.ax.aIdx[i].p = pIdx; + sIdxIter.u.ax.aIdx[i].ix = jj; + i++; + } + assert( i==nIdx ); } } - ixi.eType = 0; - ixi.i = 0; - ixi.u.lx.pIdx = pTab->pIndex; /* Determine if it is possible that triggers (either explicitly coded ** triggers or FK resolution actions) might run as a result of deletes @@ -1866,11 +1909,20 @@ void sqlite3GenerateConstraintChecks( } /* figure out whether or not upsert applies in this case */ - if( pUpsert && pUpsert->pUpsertIdx==0 ){ - if( pUpsert->pUpsertSet==0 ){ - onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ - }else{ - onError = OE_Update; /* DO UPDATE */ + if( pUpsert ){ + pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0); + if( pUpsertClause!=0 ){ + if( pUpsertClause->pUpsertSet==0 ){ + onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ + }else{ + onError = OE_Update; /* DO UPDATE */ + } + } + if( pUpsertClause!=pUpsert ){ + /* The first ON CONFLICT clause has a conflict target other than + ** the IPK. We have to jump ahead to that first ON CONFLICT clause + ** and then come back here and deal with the IPK afterwards */ + upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto); } } @@ -1977,7 +2029,9 @@ void sqlite3GenerateConstraintChecks( } } sqlite3VdbeResolveLabel(v, addrRowidOk); - if( ipkTop ){ + if( pUpsert && pUpsertClause!=pUpsert ){ + upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto); + }else if( ipkTop ){ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, ipkTop-1); } @@ -1990,9 +2044,9 @@ void sqlite3GenerateConstraintChecks( ** This loop also handles the case of the PRIMARY KEY index for a ** WITHOUT ROWID table. */ - for(pIdx = indexIteratorFirst(&ixi, &ix); + for(pIdx = indexIteratorFirst(&sIdxIter, &ix); pIdx; - pIdx = indexIteratorNext(&ixi, &ix) + pIdx = indexIteratorNext(&sIdxIter, &ix) ){ int regIdx; /* Range of registers hold conent for pIdx */ int regR; /* Range of registers holding conflicting PK */ @@ -2001,15 +2055,14 @@ void sqlite3GenerateConstraintChecks( int addrConflictCk; /* First opcode in the conflict check logic */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ - if( pUpIdx && pUpIdx->zName==pIdx->zName ){ - addrUniqueOk = upsertJump+1; - upsertBypass = sqlite3VdbeGoto(v, 0); - VdbeComment((v, "Skip upsert subroutine")); - sqlite3VdbeJumpHere(v, upsertJump); - }else{ - addrUniqueOk = sqlite3VdbeMakeLabel(pParse); + if( pUpsert ){ + pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx); + if( upsertIpkDelay && pUpsertClause==pUpsert ){ + sqlite3VdbeJumpHere(v, upsertIpkDelay); + } } - if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx->zName==pIdx->zName) ){ + addrUniqueOk = sqlite3VdbeMakeLabel(pParse); + if( bAffinityDone==0 ){ sqlite3TableAffinity(v, pTab, regNewData+1); bAffinityDone = 1; } @@ -2080,8 +2133,8 @@ void sqlite3GenerateConstraintChecks( } /* Figure out if the upsert clause applies to this index */ - if( pUpIdx && pUpIdx->zName==pIdx->zName ){ - if( pUpsert->pUpsertSet==0 ){ + if( pUpsertClause ){ + if( pUpsertClause->pUpsertSet==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ @@ -2273,13 +2326,12 @@ void sqlite3GenerateConstraintChecks( break; } } - if( pUpIdx && pUpIdx->zName==pIdx->zName ){ - sqlite3VdbeGoto(v, upsertJump+1); - sqlite3VdbeJumpHere(v, upsertBypass); - }else{ - sqlite3VdbeResolveLabel(v, addrUniqueOk); - } + sqlite3VdbeResolveLabel(v, addrUniqueOk); if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); + if( pUpsertClause && sqlite3UpsertNextIsIPK(pUpsertClause) ){ + sqlite3VdbeGoto(v, upsertIpkDelay+1); + sqlite3VdbeJumpHere(v, upsertIpkReturn); + } } /* If the IPK constraint is a REPLACE, run it last */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 83993f08c5..403f31326d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4841,10 +4841,14 @@ const char *sqlite3JournalModename(int); Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); + Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); + int sqlite3UpsertNextIsIPK(Upsert*); #else #define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0) #define sqlite3UpsertDelete(x,y) #define sqlite3UpsertDup(x,y) ((Upsert*)0) +#define sqlite3UpsertOfIndex(x,y) ((Upsert*)0) +#define sqlite3UpsertNextIsIPK(x) 0 #endif diff --git a/src/upsert.c b/src/upsert.c index 6b9bff6851..0a9b67dbdb 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -208,6 +208,38 @@ int sqlite3UpsertAnalyzeTarget( return SQLITE_OK; } +/* +** Return true if pUpsert is the last ON CONFLICT clause with a +** conflict target, or if pUpsert is followed by another ON CONFLICT +** clause that targets the INTEGER PRIMARY KEY. +*/ +int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ + Upsert *pNext; + if( pUpsert==0 ) return 0; + pNext = pUpsert->pNextUpsert; + if( pNext==0 ) return 1; + if( pNext->pUpsertTarget==0 ) return 1; + if( pNext->pUpsertIdx==0 ) return 1; + return 0; +} + +/* +** Given the list of ON CONFLICT clauses described by pUpsert, and +** a particular index pIdx, return a pointer to the particular ON CONFLICT +** clause that applies to the index. Or, if the index is not subject to +** any ON CONFLICT clause, return NULL. +*/ +Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){ + while( + pUpsert + && pUpsert->pUpsertTarget!=0 + && pUpsert->pUpsertIdx!=pIdx + ){ + pUpsert = pUpsert->pNextUpsert; + } + return pUpsert; +} + /* ** Generate bytecode that does an UPDATE as part of an upsert. ** @@ -234,14 +266,7 @@ void sqlite3UpsertDoUpdate( assert( v!=0 ); assert( pUpsert!=0 ); iDataCur = pUpsert->iDataCur; - while( - pUpsert->pUpsertTarget!=0 - && (pUpsert->pUpsertIdx==0 ? pIdx!=0 : - pUpsert->pUpsertIdx->zName!=pIdx->zName) - ){ - assert( pUpsert->pNextUpsert!=0 ); - pUpsert = pUpsert->pNextUpsert; - } + pUpsert = sqlite3UpsertOfIndex(pTop, pIdx); if( pUpsert->addrGenericUpdate>0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, pUpsert->addrGenericUpdate); return; From ed4c54699df2c2a0494e96dacaf21d279ca0adf2 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 11 Dec 2020 16:49:51 +0000 Subject: [PATCH 08/13] Bug fixes so that legacy tests pass. New tests for new functionality have not yet been added. FossilOrigin-Name: aa76790e58cea9a2b707f5912fd66c76545e7417442553fc13c87f773a2fe1dd --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/insert.c | 16 ++++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/manifest b/manifest index 5bb77216d5..09d7eda057 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Logic\sis\sin\splace\sto\shandle\smultiple\sON\sCONFLICT\sclauses,\sbut\sit\sdoes\snot\swork.\nAny\suse\sof\sON\sCONFLICT\swill\slikely\slead\sto\smemory\sfaults.\s\sThis\sis\san\nincremental\scheck-in\sto\ssave\smy\splace. -D 2020-12-11T01:17:06.489 +C Bug\sfixes\sso\sthat\slegacy\stests\spass.\s\sNew\stests\sfor\snew\sfunctionality\shave\nnot\syet\sbeen\sadded. +D 2020-12-11T16:49:51.732 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c c8d4bc0dcde4d9d42a1761e919a6a99668000e16840814f8bb643e161894b27f +F src/insert.c 3f90efe0afbbf4a64f7fe37cb061527a86445f0a1b85a78038ae406b41fbf806 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -1888,7 +1888,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 64a4a91ecc5dcde3fa07d3cf038c74b9ede63d36628ecfb35203a9dfbbfe113c -R 750cd21a590114aba81487b95892f11b +P 155142314feb007d526f8f67723636fd50dc52d1cd4d3a67dd93b105c9d5c2be +R 63e742ce65925663b08e2a758de8f6e2 U drh -Z dd643da8c0e77547922105a9f9bf1ab2 +Z 0aa452b6f5f81d99a0b8c06fa6b89e8a diff --git a/manifest.uuid b/manifest.uuid index 7a1e0b0cb8..13c5d7b5a1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -155142314feb007d526f8f67723636fd50dc52d1cd4d3a67dd93b105c9d5c2be \ No newline at end of file +aa76790e58cea9a2b707f5912fd66c76545e7417442553fc13c87f773a2fe1dd \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 5e59221278..41503224ec 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1441,9 +1441,14 @@ struct IndexListTerm { /* Return the first index on the list */ static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ - int i = pIter->i; - *pIx = i; - return pIter->eType ? pIter->u.ax.aIdx[i].p : pIter->u.lx.pIdx; + assert( pIter->i==0 ); + if( pIter->eType ){ + *pIx = pIter->u.ax.aIdx[0].ix; + return pIter->u.ax.aIdx[0].p; + }else{ + *pIx = 0; + return pIter->u.lx.pIdx; + } } /* Return the next index from the list. Return NULL when out of indexes */ @@ -2328,7 +2333,10 @@ void sqlite3GenerateConstraintChecks( } sqlite3VdbeResolveLabel(v, addrUniqueOk); if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField); - if( pUpsertClause && sqlite3UpsertNextIsIPK(pUpsertClause) ){ + if( pUpsertClause + && upsertIpkReturn + && sqlite3UpsertNextIsIPK(pUpsertClause) + ){ sqlite3VdbeGoto(v, upsertIpkDelay+1); sqlite3VdbeJumpHere(v, upsertIpkReturn); } From d3e21a1098ea6b5002417514ba2c984c4d1871ff Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 11 Dec 2020 17:11:56 +0000 Subject: [PATCH 09/13] Small performance tweaks. FossilOrigin-Name: 5321d60c575ef8f888d1b315df02cf9ed96a3ffc61babbc1429aa73b2a61a190 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/insert.c | 17 ++++++++--------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index 09d7eda057..953046263d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfixes\sso\sthat\slegacy\stests\spass.\s\sNew\stests\sfor\snew\sfunctionality\shave\nnot\syet\sbeen\sadded. -D 2020-12-11T16:49:51.732 +C Small\sperformance\stweaks. +D 2020-12-11T17:11:56.302 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 3f90efe0afbbf4a64f7fe37cb061527a86445f0a1b85a78038ae406b41fbf806 +F src/insert.c 2b9b6a9cae187bf6c1d4220726511459a23d6089dd4025c4cb00657195c81ba8 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -1888,7 +1888,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 155142314feb007d526f8f67723636fd50dc52d1cd4d3a67dd93b105c9d5c2be -R 63e742ce65925663b08e2a758de8f6e2 +P aa76790e58cea9a2b707f5912fd66c76545e7417442553fc13c87f773a2fe1dd +R facb33fc25631aa36550b455921f2f42 U drh -Z 0aa452b6f5f81d99a0b8c06fa6b89e8a +Z 1d9f47d8176ed5a99b3ef4efb5d1d2d4 diff --git a/manifest.uuid b/manifest.uuid index 13c5d7b5a1..529b505cd2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aa76790e58cea9a2b707f5912fd66c76545e7417442553fc13c87f773a2fe1dd \ No newline at end of file +5321d60c575ef8f888d1b315df02cf9ed96a3ffc61babbc1429aa73b2a61a190 \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 41503224ec..8306c602bc 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1453,8 +1453,8 @@ static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){ /* Return the next index from the list. Return NULL when out of indexes */ static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ - int i = ++pIter->i; if( pIter->eType ){ + int i = ++pIter->i; if( i>=pIter->u.ax.nIdx ){ *pIx = i; return 0; @@ -1462,7 +1462,7 @@ static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){ *pIx = pIter->u.ax.aIdx[i].ix; return pIter->u.ax.aIdx[i].p; }else{ - *pIx = i; + ++(*pIx); pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext; return pIter->u.lx.pIdx; } @@ -1798,6 +1798,7 @@ void sqlite3GenerateConstraintChecks( */ sIdxIter.eType = 0; sIdxIter.i = 0; + sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */ sIdxIter.u.lx.pIdx = pTab->pIndex; if( pUpsert ){ if( pUpsert->pUpsertTarget==0 ){ @@ -2120,7 +2121,7 @@ void sqlite3GenerateConstraintChecks( ** of a WITHOUT ROWID table and there has been no change the ** primary key, then no collision is possible. The collision detection ** logic below can all be skipped. */ - if( isUpdate && pPk && pPk->zName==pIdx->zName && pkChng==0 ){ + if( isUpdate && pPk==pIdx && pkChng==0 ){ sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } @@ -2158,7 +2159,7 @@ void sqlite3GenerateConstraintChecks( ** is invoked. */ #ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ - && pPk && pPk->zName==pIdx->zName /* Condition 2 */ + && pPk==pIdx /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ && ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */ 0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0)) @@ -2177,8 +2178,7 @@ void sqlite3GenerateConstraintChecks( regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ - regR = (pPk && pIdx->zName==pPk->zName) ? - regIdx : sqlite3GetTempRange(pParse, nPkField); + regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField); if( isUpdate || onError==OE_Replace ){ if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR); @@ -2193,7 +2193,7 @@ void sqlite3GenerateConstraintChecks( int x; /* Extract the PRIMARY KEY from the end of the index entry and ** store it in registers regR..regR+nPk-1 */ - if( pPk && pIdx->zName!=pPk->zName ){ + if( pIdx!=pPk ){ for(i=0; inKeyCol; i++){ assert( pPk->aiColumn[i]>=0 ); x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]); @@ -2274,8 +2274,7 @@ void sqlite3GenerateConstraintChecks( } sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur, regR, nPkField, 0, OE_Replace, - (pPk && pIdx->zName==pPk->zName ? ONEPASS_SINGLE : ONEPASS_OFF), - iThisCur); + (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur); if( pTrigger && isUpdate ){ sqlite3VdbeAddOp1(v, OP_CursorUnlock, iDataCur); } From 58b18a47df0ec28487bf7be3bf517b7f0f801686 Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 11 Dec 2020 19:36:19 +0000 Subject: [PATCH 10/13] Begin adding test cases. Fix one bug found so far. More are pending. FossilOrigin-Name: aadd67ddf2a191629b5356395f75e4556aac904a6e2f6b83742fa4f26e4253a4 --- manifest | 13 +-- manifest.uuid | 2 +- src/insert.c | 1 + test/upsert5.test | 242 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+), 7 deletions(-) create mode 100644 test/upsert5.test diff --git a/manifest b/manifest index 953046263d..728e086000 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Small\sperformance\stweaks. -D 2020-12-11T17:11:56.302 +C Begin\sadding\stest\scases.\s\sFix\sone\sbug\sfound\sso\sfar.\s\sMore\sare\spending. +D 2020-12-11T19:36:19.206 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 2b9b6a9cae187bf6c1d4220726511459a23d6089dd4025c4cb00657195c81ba8 +F src/insert.c 7bb29b8acdd49917fe12cc14e164b9c49fc98f5f2725e5311a03b30349ca3880 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -1643,6 +1643,7 @@ F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5 +F test/upsert5.test 82acbe52e9a7d74b9eeb93dc219558f44dccd07ef5e07e74fec9eca7173e4bfe F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35 F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568 F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7 @@ -1888,7 +1889,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 aa76790e58cea9a2b707f5912fd66c76545e7417442553fc13c87f773a2fe1dd -R facb33fc25631aa36550b455921f2f42 +P 5321d60c575ef8f888d1b315df02cf9ed96a3ffc61babbc1429aa73b2a61a190 +R 8ec7743a66623a70c1f2fd83cc9cbf09 U drh -Z 1d9f47d8176ed5a99b3ef4efb5d1d2d4 +Z d9c45c2e1447fdcad46c943d9137f6df diff --git a/manifest.uuid b/manifest.uuid index 529b505cd2..87fe70a5b0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5321d60c575ef8f888d1b315df02cf9ed96a3ffc61babbc1429aa73b2a61a190 \ No newline at end of file +aadd67ddf2a191629b5356395f75e4556aac904a6e2f6b83742fa4f26e4253a4 \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 8306c602bc..4fa337a2f2 100644 --- a/src/insert.c +++ b/src/insert.c @@ -2338,6 +2338,7 @@ void sqlite3GenerateConstraintChecks( ){ sqlite3VdbeGoto(v, upsertIpkDelay+1); sqlite3VdbeJumpHere(v, upsertIpkReturn); + upsertIpkReturn = 0; } } diff --git a/test/upsert5.test b/test/upsert5.test new file mode 100644 index 0000000000..e3840a9b32 --- /dev/null +++ b/test/upsert5.test @@ -0,0 +1,242 @@ +# 2020-12-11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# Test cases for generalized UPSERT + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix upsert5 + +foreach {tn sql} { + 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) } + 2 { CREATE TABLE t1(a INT PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) } + 3 { CREATE TABLE t1(a INT PRIMARY KEY, b, c UNIQUE, d UNIQUE, e UNIQUE) WITHOUT ROWID} + 4 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INTEGER PRIMARY KEY, b) } + 5 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INT PRIMARY KEY, b) } + 6 { CREATE TABLE t1(e UNIQUE, d UNIQUE, c UNIQUE, a INT PRIMARY KEY, b) WITHOUT ROWID} +} { + reset_db + execsql $sql + + do_execsql_test 1.$tn.100 { + 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,NULL,3,4,5) + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.101 { + 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(91,NULL,3,4,5) + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.102 { + 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(91,NULL,93,4,5) + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 d 3 4 5} + do_execsql_test 1.$tn.103 { + 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(91,NULL,93,94,5) + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 e 3 4 5} + do_execsql_test 1.$tn.200 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.201 { + 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,NULL,3,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.202 { + 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,NULL,3,4,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.203 { + 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,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.204 { + 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,NULL,93,4,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.210 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.211 { + 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,NULL,93,4,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 d 3 4 5} + do_execsql_test 1.$tn.212 { + 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,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + do_execsql_test 1.$tn.213 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 e 3 4 5} + do_execsql_test 1.$tn.214 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e' + ON CONFLICT(a) DO UPDATE SET b='a'; + SELECT a,b,c,d,e FROM t1; + } {1 e 3 4 5} + do_execsql_test 1.$tn.215 { + 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,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e' + ON CONFLICT(a) DO UPDATE SET b='a'; + SELECT a,b,c,d,e FROM t1; + } {1 e 3 4 5} + do_execsql_test 1.$tn.216 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(e) DO UPDATE SET b='e' + ON CONFLICT(a) DO UPDATE SET b='a'; + SELECT a,b,c,d,e FROM t1; + } {1 a 3 4 5} + + do_execsql_test 1.$tn.300 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a1' + ON CONFLICT(a) DO UPDATE SET b='a2' + ON CONFLICT(a) DO UPDATE SET b='a3' + ON CONFLICT(a) DO UPDATE SET b='a4' + ON CONFLICT(a) DO UPDATE SET b='a5' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 a1 3 4 5} + do_execsql_test 1.$tn.301 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT(a) DO UPDATE SET b='a1' + ON CONFLICT(a) DO UPDATE SET b='a2' + ON CONFLICT(a) DO UPDATE SET b='a3' + ON CONFLICT(a) DO UPDATE SET b='a4' + ON CONFLICT(a) DO UPDATE SET b='a5' + ON CONFLICT(e) DO UPDATE SET b='e'; + SELECT a,b,c,d,e FROM t1; + } {1 e 3 4 5} + +if {0} { + do_execsql_test 1.$tn.400 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.401 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} +} + +} + +finish_test From 255c1c159b1082c712995436862e4dd4cfa20761 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 12 Dec 2020 00:28:15 +0000 Subject: [PATCH 11/13] New test cases with corresponding bug fixes. FossilOrigin-Name: f22c21a94ca4cad0217f91c1a5a275bc348cb6ba0f3a54c927533bc8d8c96a90 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/insert.c | 6 +++--- src/sqliteInt.h | 2 +- src/upsert.c | 14 +++----------- test/upsert5.test | 38 ++++++++++++++++++++++++++++++++++++-- 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/manifest b/manifest index 728e086000..43fe5d0f2c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\sadding\stest\scases.\s\sFix\sone\sbug\sfound\sso\sfar.\s\sMore\sare\spending. -D 2020-12-11T19:36:19.206 +C New\stest\scases\swith\scorresponding\sbug\sfixes. +D 2020-12-12T00:28:15.628 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -501,7 +501,7 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c 7bb29b8acdd49917fe12cc14e164b9c49fc98f5f2725e5311a03b30349ca3880 +F src/insert.c ec68f3a8b9af7c6e127735af8f61633bc0a2c9cd24a961fb21331ac95c7df251 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067 F src/main.c 97e9f137354bc1f76dc9bb60a0a24f8c45cf73b33e80d3ee4c64155336fb820d @@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009 F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e -F src/sqliteInt.h aff99a1938b53530971fcfa7a6a5e6b47f986ddd673660bafb41f1c559747b72 +F src/sqliteInt.h 31492bf5f9d7ccc4835d5e0a88758737812b0ee4c35c0b961840373dbd18bbb0 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 6471d9e0e5e05547f8a00d7d686714ac08fe89526de133abcf8e1c7a35f2cb82 +F src/upsert.c 5ebb702817ee157badae0548098ea216cc1636e98eda63554e346968565c64c4 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1643,7 +1643,7 @@ F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5 -F test/upsert5.test 82acbe52e9a7d74b9eeb93dc219558f44dccd07ef5e07e74fec9eca7173e4bfe +F test/upsert5.test 32ff55ed7e3dcfe62a8e54897c20e074d4629ddf72297092e79997f82ccf06b5 F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35 F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568 F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7 @@ -1889,7 +1889,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 5321d60c575ef8f888d1b315df02cf9ed96a3ffc61babbc1429aa73b2a61a190 -R 8ec7743a66623a70c1f2fd83cc9cbf09 +P aadd67ddf2a191629b5356395f75e4556aac904a6e2f6b83742fa4f26e4253a4 +R ebc93beb1a5a9905106ef56cc676e95d U drh -Z d9c45c2e1447fdcad46c943d9137f6df +Z 4e2db7f4f46c9338f10a1365883b3835 diff --git a/manifest.uuid b/manifest.uuid index 87fe70a5b0..73f4c884b4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aadd67ddf2a191629b5356395f75e4556aac904a6e2f6b83742fa4f26e4253a4 \ No newline at end of file +f22c21a94ca4cad0217f91c1a5a275bc348cb6ba0f3a54c927533bc8d8c96a90 \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index 4fa337a2f2..d0f82dccfe 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1804,7 +1804,7 @@ void sqlite3GenerateConstraintChecks( if( pUpsert->pUpsertTarget==0 ){ /* There is just on ON CONFLICT clause and it has no constraint-target */ assert( pUpsert->pNextUpsert==0 ); - if( pUpsert->pUpsertSet==0 ){ + if( pUpsert->isDoUpdate==0 ){ /* A single ON CONFLICT DO NOTHING clause, without a constraint-target. ** Make all unique constraint resolution be OE_Ignore */ overrideError = OE_Ignore; @@ -1918,7 +1918,7 @@ void sqlite3GenerateConstraintChecks( if( pUpsert ){ pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0); if( pUpsertClause!=0 ){ - if( pUpsertClause->pUpsertSet==0 ){ + if( pUpsertClause->isDoUpdate==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ @@ -2140,7 +2140,7 @@ void sqlite3GenerateConstraintChecks( /* Figure out if the upsert clause applies to this index */ if( pUpsertClause ){ - if( pUpsertClause->pUpsertSet==0 ){ + if( pUpsertClause->isDoUpdate==0 ){ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */ }else{ onError = OE_Update; /* DO UPDATE */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 403f31326d..3c88c07806 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3078,6 +3078,7 @@ struct Upsert { ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ + u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ void *pToFree; /* Free memory when deleting the Upsert object */ @@ -3091,7 +3092,6 @@ struct Upsert { int regData; /* First register holding array of VALUES */ int iDataCur; /* Index of the data cursor */ int iIdxCur; /* Index of the first index cursor */ - int addrGenericUpdate; /* Address of routine for generic DO UPDATE */ }; /* diff --git a/src/upsert.c b/src/upsert.c index 0a9b67dbdb..cb173aa53f 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -74,6 +74,7 @@ Upsert *sqlite3UpsertNew( pNew->pUpsertTargetWhere = pTargetWhere; pNew->pUpsertSet = pSet; pNew->pUpsertWhere = pWhere; + pNew->isDoUpdate = pSet!=0; pNew->pNextUpsert = pNext; } return pNew; @@ -267,14 +268,7 @@ void sqlite3UpsertDoUpdate( assert( pUpsert!=0 ); iDataCur = pUpsert->iDataCur; pUpsert = sqlite3UpsertOfIndex(pTop, pIdx); - if( pUpsert->addrGenericUpdate>0 ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, pUpsert->addrGenericUpdate); - return; - } VdbeNoopComment((v, "Begin DO UPDATE of UPSERT")); - if( pUpsert->pUpsertTarget==0 ){ - pUpsert->addrGenericUpdate = sqlite3VdbeCurrentAddr(v); - } if( pIdx && iCur!=iDataCur ){ if( HasRowid(pTab) ){ int regRowid = sqlite3GetTempReg(pParse); @@ -313,10 +307,8 @@ void sqlite3UpsertDoUpdate( sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); } } - sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet, - pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert); - pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */ - pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */ + sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0), + sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert); VdbeNoopComment((v, "End DO UPDATE of UPSERT")); } diff --git a/test/upsert5.test b/test/upsert5.test index e3840a9b32..fe748f5f00 100644 --- a/test/upsert5.test +++ b/test/upsert5.test @@ -216,7 +216,6 @@ foreach {tn sql} { SELECT a,b,c,d,e FROM t1; } {1 e 3 4 5} -if {0} { do_execsql_test 1.$tn.400 { DELETE FROM t1; INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5); @@ -235,7 +234,42 @@ if {0} { ON CONFLICT DO UPDATE set b='x'; SELECT a,b,c,d,e FROM t1; } {1 x 3 4 5} -} + do_execsql_test 1.$tn.402 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.403 { + 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(91,NULL,3,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.404 { + 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(91,NULL,3,4,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.405 { + 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,NULL,93,4,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 d 3 4 5} } From 250af6e0fb8c9fab2aab73651e39b5832eccd331 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 12 Dec 2020 00:43:52 +0000 Subject: [PATCH 12/13] More test cases. No new problems discovered. FossilOrigin-Name: f34dd67e2d0dfc9e3b5d49148fb0162853119c097cbc3fe961878875ba98d8e3 --- manifest | 12 ++--- manifest.uuid | 2 +- test/upsert5.test | 121 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 43fe5d0f2c..3410b246b8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C New\stest\scases\swith\scorresponding\sbug\sfixes. -D 2020-12-12T00:28:15.628 +C More\stest\scases.\s\sNo\snew\sproblems\sdiscovered. +D 2020-12-12T00:43:52.775 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1643,7 +1643,7 @@ F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266 F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09 F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5 -F test/upsert5.test 32ff55ed7e3dcfe62a8e54897c20e074d4629ddf72297092e79997f82ccf06b5 +F test/upsert5.test f49faf5f15b5c3641c6f5d7c7cc531ef5ac997567b2b6bb7bc96f7c88753ca0b F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35 F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568 F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7 @@ -1889,7 +1889,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 aadd67ddf2a191629b5356395f75e4556aac904a6e2f6b83742fa4f26e4253a4 -R ebc93beb1a5a9905106ef56cc676e95d +P f22c21a94ca4cad0217f91c1a5a275bc348cb6ba0f3a54c927533bc8d8c96a90 +R 5a5b9a84e82bccc93a94b5f0a9cd7326 U drh -Z 4e2db7f4f46c9338f10a1365883b3835 +Z 16b3f3b436b8a24d4c2a8d5d463d0b17 diff --git a/manifest.uuid b/manifest.uuid index 73f4c884b4..bf1f11ba9a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f22c21a94ca4cad0217f91c1a5a275bc348cb6ba0f3a54c927533bc8d8c96a90 \ No newline at end of file +f34dd67e2d0dfc9e3b5d49148fb0162853119c097cbc3fe961878875ba98d8e3 \ No newline at end of file diff --git a/test/upsert5.test b/test/upsert5.test index fe748f5f00..c4747ddf18 100644 --- a/test/upsert5.test +++ b/test/upsert5.test @@ -271,6 +271,127 @@ foreach {tn sql} { SELECT a,b,c,d,e FROM t1; } {1 d 3 4 5} + do_execsql_test 1.$tn.410 { + 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,NULL,93,94,95) + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.411 { + 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(91,NULL,93,94,5) + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.412 { + 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(91,NULL,93,4,95) + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.413 { + 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(91,NULL,3,94,95) + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + + do_execsql_test 1.$tn.420 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO NOTHING + ON CONFLICT(d) DO NOTHING + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.421 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO NOTHING + ON CONFLICT(d) DO NOTHING + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 x 3 4 5} + do_execsql_test 1.$tn.422 { + 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(91,NULL,93,4,95) + ON CONFLICT(c) DO NOTHING + ON CONFLICT(d) DO NOTHING + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 2 3 4 5} + do_execsql_test 1.$tn.423 { + 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(91,NULL,3,94,95) + ON CONFLICT(c) DO NOTHING + ON CONFLICT(d) DO NOTHING + ON CONFLICT DO UPDATE set b='x'; + SELECT a,b,c,d,e FROM t1; + } {1 2 3 4 5} + + do_execsql_test 1.$tn.500 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 2 3 4 5} + do_execsql_test 1.$tn.501 { + 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(91,NULL,93,94,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 2 3 4 5} + do_execsql_test 1.$tn.502 { + 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,NULL,93,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 2 3 4 5} + do_execsql_test 1.$tn.503 { + 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(91,NULL,3,94,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.504 { + 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(91,NULL,3,4,95) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 c 3 4 5} + do_execsql_test 1.$tn.505 { + 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,NULL,93,4,5) + ON CONFLICT(c) DO UPDATE SET b='c' + ON CONFLICT(d) DO UPDATE SET b='d' + ON CONFLICT DO NOTHING; + SELECT a,b,c,d,e FROM t1; + } {1 d 3 4 5} + } finish_test From 1c19848386babe4056e0e825433fcbe4b4ef1a5d Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 14 Dec 2020 13:52:03 +0000 Subject: [PATCH 13/13] Minor changes for test coverage. FossilOrigin-Name: e5a8fa50f4e5e5c24664452eda4af80904f75e5123b8f84353347dbd505d416d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/parse.y | 7 ++++++- src/upsert.c | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 3410b246b8..d18f0d5c73 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\stest\scases.\s\sNo\snew\sproblems\sdiscovered. -D 2020-12-12T00:43:52.775 +C Minor\schanges\sfor\stest\scoverage. +D 2020-12-14T13:52:03.476 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -529,7 +529,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f -F src/parse.y 72b884c73f2b446e7dc4c7169ec7fbb82e0e292eec733fcf554f0fde46f269f6 +F src/parse.y 6c8aa09a7fa6e0867c3a3d67ef61b911aa392c9b084a61dc632cd93732aef8ad F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177 F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a @@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97 F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10 F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580 -F src/upsert.c 5ebb702817ee157badae0548098ea216cc1636e98eda63554e346968565c64c4 +F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002 F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286 @@ -1889,7 +1889,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 f22c21a94ca4cad0217f91c1a5a275bc348cb6ba0f3a54c927533bc8d8c96a90 -R 5a5b9a84e82bccc93a94b5f0a9cd7326 +P f34dd67e2d0dfc9e3b5d49148fb0162853119c097cbc3fe961878875ba98d8e3 +R e4ba77cafc1a5ebe11440b61ff8c6ede U drh -Z 16b3f3b436b8a24d4c2a8d5d463d0b17 +Z 22668862baa080d495b808bc49582152 diff --git a/manifest.uuid b/manifest.uuid index bf1f11ba9a..22965be8e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f34dd67e2d0dfc9e3b5d49148fb0162853119c097cbc3fe961878875ba98d8e3 \ No newline at end of file +e5a8fa50f4e5e5c24664452eda4af80904f75e5123b8f84353347dbd505d416d \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index f28bb47414..faec4b5cf5 100644 --- a/src/parse.y +++ b/src/parse.y @@ -952,7 +952,12 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES. } %type upsert {Upsert*} -%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);} + +// Because upsert only occurs at the tip end of the INSERT rule for cmd, +// there is never a case where the value of the upsert pointer will not +// be destroyed by the cmd action. So comment-out the destructor to +// avoid unreachable code. +//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);} upsert(A) ::= . { A = 0; } upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO UPDATE SET setlist(Z) where_opt(W) upsert(N). diff --git a/src/upsert.c b/src/upsert.c index cb173aa53f..982dc7dbc1 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -216,7 +216,7 @@ int sqlite3UpsertAnalyzeTarget( */ int sqlite3UpsertNextIsIPK(Upsert *pUpsert){ Upsert *pNext; - if( pUpsert==0 ) return 0; + if( NEVER(pUpsert==0) ) return 0; pNext = pUpsert->pNextUpsert; if( pNext==0 ) return 1; if( pNext->pUpsertTarget==0 ) return 1;