mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
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
This commit is contained in:
19
manifest
19
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Fix\sa\sbad\sassert()\sin\smath1Func().
|
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-07T23:14:25.210
|
D 2020-12-08T14:29:03.533
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||||
@@ -529,7 +529,7 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
|
|||||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||||
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
|
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
|
||||||
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
|
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
|
||||||
F src/parse.y 9ce4dfb772608ed5bd3c32f33e943e021e3b06cfd2c01932d4280888fdd2ebed
|
F src/parse.y 72b884c73f2b446e7dc4c7169ec7fbb82e0e292eec733fcf554f0fde46f269f6
|
||||||
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
||||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||||
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
||||||
@@ -545,7 +545,7 @@ F src/shell.c.in e9f674ee4ec6c345679e8a5b16c869c6c59eb1540dd98ac69e4736ecddce009
|
|||||||
F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706
|
F src/sqlite.h.in 0e2b4259e49a0eda54d9118eb18a04fcd60e0727a2fd2c81aade0bf57520e706
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||||
F src/sqliteInt.h 6ab40b33a1f5edbb7d71c78e82e0f9c5291dcff4704df8e4f0ab0d9c1a0c06af
|
F src/sqliteInt.h 351d29fad669d5c98066a89ab48259d451379edac3c24773c3c8ac5df66fd8ff
|
||||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||||
@@ -608,7 +608,7 @@ F src/tokenize.c 01dba3023659dc6f6b1e054c14b35a0074bd35de10466b99454d33278191d97
|
|||||||
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
|
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
|
||||||
F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10
|
F src/trigger.c 515e79206d40d1d4149129318582e79a6e9db590a7b74e226fdb5b2a6c7e1b10
|
||||||
F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
|
F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
|
||||||
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
F src/upsert.c 25673d007c2408fec47a6326b6d7ac265abd2cbc162d11f3b3c333de27d3c78a
|
||||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||||
F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
|
F src/util.c c0c7977de7ef9b8cb10f6c85f2d0557889a658f817b0455909a49179ba4c8002
|
||||||
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
||||||
@@ -1888,7 +1888,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P 99ff6418492adcbaf2be728737735afa1c2997de5868395e69c53d08fc14491f
|
P 4b286129138d44e6f8e9b3450289941e01d20fdfb9d0b5d846031425e8ca6b49
|
||||||
R 747ed0f69719082482a4ff82786fa288
|
R 204aa888c3a8b96c5008bc9a579603f7
|
||||||
|
T *branch * generalized-upsert
|
||||||
|
T *sym-generalized-upsert *
|
||||||
|
T -sym-trunk *
|
||||||
U drh
|
U drh
|
||||||
Z ac855459dfaf775bb9ffc51142afdf45
|
Z d13edca14ac31a65fc7ae7d7fdc7eaff
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4b286129138d44e6f8e9b3450289941e01d20fdfb9d0b5d846031425e8ca6b49
|
2ca62f4c71df6544cb8039bdc80e3701d09697c38800534371f6d44532fcffae
|
||||||
19
src/parse.y
19
src/parse.y
@@ -952,20 +952,17 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
|
|||||||
}
|
}
|
||||||
|
|
||||||
%type upsert {Upsert*}
|
%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) ::= . { A = 0; }
|
||||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
|
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
|
||||||
DO UPDATE SET setlist(Z) where_opt(W).
|
DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
|
||||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W);}
|
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
|
||||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING.
|
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
|
||||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0); }
|
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
|
||||||
upsert(A) ::= ON CONFLICT DO NOTHING.
|
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}
|
%type insert_cmd {int}
|
||||||
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
||||||
|
|||||||
@@ -3071,6 +3071,7 @@ struct Upsert {
|
|||||||
Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
|
Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
|
||||||
ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
|
ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
|
||||||
Expr *pUpsertWhere; /* WHERE clause for the 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 above comprise the parse tree for the upsert clause.
|
||||||
** The fields below are used to transfer information from the INSERT
|
** The fields below are used to transfer information from the INSERT
|
||||||
** processing down into the UPDATE processing while generating code.
|
** processing down into the UPDATE processing while generating code.
|
||||||
@@ -4824,13 +4825,13 @@ const char *sqlite3JournalModename(int);
|
|||||||
#define sqlite3WithDelete(x,y)
|
#define sqlite3WithDelete(x,y)
|
||||||
#endif
|
#endif
|
||||||
#ifndef SQLITE_OMIT_UPSERT
|
#ifndef SQLITE_OMIT_UPSERT
|
||||||
Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
|
Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
|
||||||
void sqlite3UpsertDelete(sqlite3*,Upsert*);
|
void sqlite3UpsertDelete(sqlite3*,Upsert*);
|
||||||
Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
|
Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
|
||||||
int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
|
int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
|
||||||
void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
|
void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
|
||||||
#else
|
#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 sqlite3UpsertDelete(x,y)
|
||||||
#define sqlite3UpsertDup(x,y) ((Upsert*)0)
|
#define sqlite3UpsertDup(x,y) ((Upsert*)0)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
39
src/upsert.c
39
src/upsert.c
@@ -18,16 +18,22 @@
|
|||||||
/*
|
/*
|
||||||
** Free a list of Upsert objects
|
** Free a list of Upsert objects
|
||||||
*/
|
*/
|
||||||
void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
|
static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
|
||||||
if( p ){
|
do{
|
||||||
|
Upsert *pNext = p->pNextUpsert;
|
||||||
sqlite3ExprListDelete(db, p->pUpsertTarget);
|
sqlite3ExprListDelete(db, p->pUpsertTarget);
|
||||||
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
|
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
|
||||||
sqlite3ExprListDelete(db, p->pUpsertSet);
|
sqlite3ExprListDelete(db, p->pUpsertSet);
|
||||||
sqlite3ExprDelete(db, p->pUpsertWhere);
|
sqlite3ExprDelete(db, p->pUpsertWhere);
|
||||||
sqlite3DbFree(db, p);
|
sqlite3DbFree(db, p);
|
||||||
|
p = pNext;
|
||||||
|
}while( p );
|
||||||
}
|
}
|
||||||
|
void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
|
||||||
|
if( p ) upsertDelete(db, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Duplicate an Upsert object.
|
** Duplicate an Upsert object.
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +43,8 @@ Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
|
|||||||
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
|
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
|
||||||
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
|
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
|
||||||
sqlite3ExprListDup(db, p->pUpsertSet, 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 */
|
ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
|
||||||
Expr *pTargetWhere, /* Optional WHERE clause on the target */
|
Expr *pTargetWhere, /* Optional WHERE clause on the target */
|
||||||
ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
|
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;
|
Upsert *pNew;
|
||||||
pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
|
pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
|
||||||
@@ -58,6 +66,7 @@ Upsert *sqlite3UpsertNew(
|
|||||||
sqlite3ExprDelete(db, pTargetWhere);
|
sqlite3ExprDelete(db, pTargetWhere);
|
||||||
sqlite3ExprListDelete(db, pSet);
|
sqlite3ExprListDelete(db, pSet);
|
||||||
sqlite3ExprDelete(db, pWhere);
|
sqlite3ExprDelete(db, pWhere);
|
||||||
|
sqlite3UpsertDelete(db, pNext);
|
||||||
return 0;
|
return 0;
|
||||||
}else{
|
}else{
|
||||||
pNew->pUpsertTarget = pTarget;
|
pNew->pUpsertTarget = pTarget;
|
||||||
@@ -65,6 +74,7 @@ Upsert *sqlite3UpsertNew(
|
|||||||
pNew->pUpsertSet = pSet;
|
pNew->pUpsertSet = pSet;
|
||||||
pNew->pUpsertWhere = pWhere;
|
pNew->pUpsertWhere = pWhere;
|
||||||
pNew->pUpsertIdx = 0;
|
pNew->pUpsertIdx = 0;
|
||||||
|
pNew->pNextUpsert = pNext;
|
||||||
}
|
}
|
||||||
return pNew;
|
return pNew;
|
||||||
}
|
}
|
||||||
@@ -89,6 +99,7 @@ int sqlite3UpsertAnalyzeTarget(
|
|||||||
Expr *pTerm; /* One term of the conflict-target clause */
|
Expr *pTerm; /* One term of the conflict-target clause */
|
||||||
NameContext sNC; /* Context for resolving symbolic names */
|
NameContext sNC; /* Context for resolving symbolic names */
|
||||||
Expr sCol[2]; /* Index column converted into an Expr */
|
Expr sCol[2]; /* Index column converted into an Expr */
|
||||||
|
int nClause = 0; /* Counter of ON CONFLICT clauses */
|
||||||
|
|
||||||
assert( pTabList->nSrc==1 );
|
assert( pTabList->nSrc==1 );
|
||||||
assert( pTabList->a[0].pTab!=0 );
|
assert( pTabList->a[0].pTab!=0 );
|
||||||
@@ -102,6 +113,8 @@ int sqlite3UpsertAnalyzeTarget(
|
|||||||
memset(&sNC, 0, sizeof(sNC));
|
memset(&sNC, 0, sizeof(sNC));
|
||||||
sNC.pParse = pParse;
|
sNC.pParse = pParse;
|
||||||
sNC.pSrcList = pTabList;
|
sNC.pSrcList = pTabList;
|
||||||
|
for(; pUpsert && pUpsert->pUpsertTarget;
|
||||||
|
pUpsert=pUpsert->pNextUpsert, nClause++){
|
||||||
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
|
||||||
@@ -118,7 +131,7 @@ int sqlite3UpsertAnalyzeTarget(
|
|||||||
){
|
){
|
||||||
/* The conflict-target is the rowid of the primary table */
|
/* The conflict-target is the rowid of the primary table */
|
||||||
assert( pUpsert->pUpsertIdx==0 );
|
assert( pUpsert->pUpsertIdx==0 );
|
||||||
return SQLITE_OK;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize sCol[0..1] to be an expression parse tree for a
|
/* Initialize sCol[0..1] to be an expression parse tree for a
|
||||||
@@ -178,12 +191,22 @@ int sqlite3UpsertAnalyzeTarget(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pUpsert->pUpsertIdx = pIdx;
|
pUpsert->pUpsertIdx = pIdx;
|
||||||
return SQLITE_OK;
|
break;
|
||||||
}
|
}
|
||||||
sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
|
if( pUpsert->pUpsertIdx==0 ){
|
||||||
"PRIMARY KEY or UNIQUE constraint");
|
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;
|
return SQLITE_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate bytecode that does an UPDATE as part of an upsert.
|
** Generate bytecode that does an UPDATE as part of an upsert.
|
||||||
|
|||||||
Reference in New Issue
Block a user