More complete parsing of UPSERT, including UPSERT within a trigger.

The sqlite3Insert() logic to actually perform the UPSERT is not yet
implemented, however.

FossilOrigin-Name: 5cc2a5a315a2f26b392811de45b3dc352873a173c2c6c65f37ce2e5f88a71cd2
This commit is contained in:
drh 2018-04-07 15:04:05 +00:00
parent 26cf56f80b
commit 2c2e844a36
7 changed files with 86 additions and 28 deletions

View File

@ -1,5 +1,5 @@
C Demonstration\sof\show\sthe\sparser\scan\sbe\saugmented\sto\srecognize\sa\nPostgreSQL-style\sUPSERT.\s\sThis\scheck-in\simplements\sparsing\sonly.
D 2018-04-06T19:36:49.704
C More\scomplete\sparsing\sof\sUPSERT,\sincluding\sUPSERT\swithin\sa\strigger.\nThe\ssqlite3Insert()\slogic\sto\sactually\sperform\sthe\sUPSERT\sis\snot\syet\nimplemented,\showever.
D 2018-04-07T15:04:05.872
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
@ -452,7 +452,7 @@ F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c b9ff71cc2913d1d57698a1e22bf853261a9a642baf62bdf40ddeb3809adb85b5
F src/insert.c 5e7a7dd617950277436e9d7f0b4782a310dc73a70a22fa1ed704f84dd687b638
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302
F src/main.c 1648fc7a9bcfdbfd9a9a04af96ff2796c3164b3f3c7e56ed63a3c51cd11d198d
@ -480,7 +480,7 @@ F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 1bb6a57fa0465296a4d6109a1a64610a0e7adde1f3acf3ef539a9d972908ce8f
F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388
F src/parse.y b6f9277147926f21ef287fbb8bc6592d801bc8f369d414af47ff2f230f026ea0
F src/parse.y 2779b5f1a24508825b7b883d73ba1632f9cc1715ffbf7b0b864b98f867742aa9
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
@ -496,7 +496,7 @@ F src/shell.c.in d6a07811aa9f3b10200c15ab8dd4b6b998849a3b0c8b125bfa980329a33c26a
F src/sqlite.h.in e0be726ea6e4e6571724d39d242472ecd8bd1ba6f84ade88e1641bde98a6d02b
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
F src/sqliteInt.h a4837c57f9a3e2af100bc59f4be60d16b823f18131f8cef6a6685440f775eebd
F src/sqliteInt.h a1a986e2b9d1c7920e355bb88f6c4a1db2667befc59dcadfdb0ae559d5ba7c70
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@ -555,7 +555,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2
F src/treeview.c 14d5d1254702ec96876aa52642cb31548612384134970409fae333b25b39d6bb
F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
F src/trigger.c 974377de987711636aa7faec963863ae8864139a051d5ba2a2a4a0329dcd8895
F src/update.c 97d4c9514229f540f8c441e124d5af7f93c5b030c9574539d01e99462e273998
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
@ -1645,7 +1645,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
F tool/mkkeywordhash.c 9f5fee138b7f8d0ccde7baad1564db8de36e76e1bd1ee7a417a1fe6cecf599fb
F tool/mkmsvcmin.tcl 8baf26690b80d861d0ac341b29880eec6ade39e4f11fe690271ded9cb90563a3
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
@ -1717,10 +1717,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 602fbd8149b53d8f0e9a223cc1aec912e7df03fca35071e8d707776ce225371c
R 944a876dd463c4d747f0489cc93d0c37
T *branch * upsert
T *sym-upsert *
T -sym-trunk *
P 9b22905b15791170998a5d1bcf42c7b60b5064f6848fff827bd55e864bf724aa
R 1c5f4cdb7d88fb6bd739d670ffaa88be
U drh
Z e91165cd71d725e31774ced0fb05304c
Z 62c48cab2b4c90f71fb632bd2119d13f

View File

@ -1 +1 @@
9b22905b15791170998a5d1bcf42c7b60b5064f6848fff827bd55e864bf724aa
5cc2a5a315a2f26b392811de45b3dc352873a173c2c6c65f37ce2e5f88a71cd2

View File

@ -488,7 +488,8 @@ void sqlite3Insert(
SrcList *pTabList, /* Name of table into which we are inserting */
Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */
int onError, /* How to handle constraint errors */
ExprList *pUpsert /* Upsert values */
){
sqlite3 *db; /* The main database structure */
Table *pTab; /* The table to insert into. aka TABLE */
@ -527,6 +528,11 @@ void sqlite3Insert(
int tmask; /* Mask of trigger times */
#endif
/* The conflict resolution type is always OE_Update or OE_Replace when
** there is an upsert clause */
assert( onError==OE_Update || pUpsert==0 );
assert( OE_Update==OE_Replace );
db = pParse->db;
if( pParse->nErr || db->mallocFailed ){
goto insert_cleanup;
@ -1074,6 +1080,7 @@ insert_end:
insert_cleanup:
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pList);
sqlite3ExprListDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
sqlite3IdListDelete(db, pColumn);
sqlite3DbFree(db, aRegIdx);

View File

@ -98,6 +98,22 @@
*/
struct TrigEvent { int a; IdList * b; };
/*
** An instance of this object holds the argument of the ON CONFLICT
** clause of an UPSERT.
**
** The ON CONFLICT clause takes three forms, identified by the Upsert.e
** field:
**
** OE_None: No ON CONFLICT clause
** OE_Ignore: ON CONFLICT DO NOTHING
** OE_Update: ON CONFLICT DO UPDATE ...
*/
struct Upsert {
ExprList *p; /* column=expr entries for the UPDATE. Or NULL */
int e; /* OE_None, OE_Replace, or OE_Ignore */
};
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
@ -205,7 +221,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
//
%fallback ID
ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
CONFLICT DATABASE DEFERRED DESC DETACH DO EACH END EXCLUSIVE EXPLAIN FAIL FOR
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
@ -842,18 +858,48 @@ setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
////////////////////////// The INSERT command /////////////////////////////////
//
cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S) upsert. {
cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S)
upsert(U). {
sqlite3WithPush(pParse, W, 1);
sqlite3Insert(pParse, X, S, F, R);
sqlite3Insert(pParse, X, S, F, upsertType(pParse, R, U.e), U.p);
}
cmd ::= with(W) insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES.
{
sqlite3WithPush(pParse, W, 1);
sqlite3Insert(pParse, X, 0, F, R);
sqlite3Insert(pParse, X, 0, F, R, 0);
}
upsert ::= .
upsert ::= ON CONFLICT SET setlist.
%type upsert {struct Upsert}
%destructor upsert {sqlite3ExprListDelete(pParse->db,$$.p);}
upsert(A) ::= . {
A.p = 0;
A.e = OE_None;
}
upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(X). {
A.p = X; /*A-overwrites-X*/
A.e = OE_Update;
}
upsert(A) ::= ON CONFLICT DO NOTHING. {
A.p = 0;
A.e = OE_Ignore;
}
%include {
/* Compute and return the correct conflict resolution strategy for an
** INSERT statement. If the statement begins with REPLACE or with
** INSERT OR, and it contains an ON CONFLICT clause, throw an error.
*/
static int upsertType(Parse *pParse, int orconf, int upsertType){
if( upsertType!=OE_None ){
if( orconf!=OE_Default ){
sqlite3ErrorMsg(pParse, "ON CONFLICT clause not allowed");
}
return upsertType;
}else{
return orconf;
}
}
}
%type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
@ -1400,9 +1446,10 @@ trigger_cmd(A) ::=
// INSERT
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
trnm(X) idlist_opt(F) select(S) scanpt(Z).
{A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,B,Z);/*A-overwrites-R*/}
trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). {
A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,upsertType(pParse,R,U.e),
U.p,B,Z);/*A-overwrites-R*/
}
// DELETE
trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E).
{A = sqlite3TriggerDeleteStep(pParse->db, &X, Y, B.z, E);}

View File

@ -2046,6 +2046,7 @@ struct FKey {
#define OE_Fail 3 /* Stop the operation but leave all prior changes */
#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */
#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */
#define OE_Update 5 /* An UPSERT. Same value as OE_Replace. */
#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
#define OE_SetNull 7 /* Set the foreign key value to NULL */
@ -3207,7 +3208,7 @@ struct TriggerStep {
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
ExprList *pExprList; /* SET clause for UPDATE. */
ExprList *pExprList; /* SET clause for UPDATE or UPSERT. */
IdList *pIdList; /* Column names for INSERT */
char *zSpan; /* Original SQL text of this command */
TriggerStep *pNext; /* Next in the link-list */
@ -3740,7 +3741,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, ExprList*);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
@ -3916,7 +3917,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
const char*,const char*);
TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
Select*,u8,const char*,const char*);
Select*,u8,ExprList*,
const char*,const char*);
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8,
const char*,const char*);
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*,

View File

@ -416,6 +416,7 @@ TriggerStep *sqlite3TriggerInsertStep(
IdList *pColumn, /* List of columns in pTableName to insert into */
Select *pSelect, /* A SELECT statement that supplies values */
u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
ExprList *pUpsert, /* Upsert values */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
){
@ -430,6 +431,7 @@ TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->orconf = orconf;
}else{
sqlite3IdListDelete(db, pColumn);
sqlite3ExprListDelete(db, pUpsert);
}
sqlite3SelectDelete(db, pSelect);
@ -755,7 +757,8 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
pParse->eOrconf
pStep->pExprList ? OE_Update : pParse->eOrconf,
sqlite3ExprListDup(db, pStep->pExprList, 0)
);
break;
}

View File

@ -186,6 +186,7 @@ static Keyword aKeywordTable[] = {
{ "DESC", "TK_DESC", ALWAYS },
{ "DETACH", "TK_DETACH", ATTACH },
{ "DISTINCT", "TK_DISTINCT", ALWAYS },
{ "DO", "TK_DO", ALWAYS },
{ "DROP", "TK_DROP", ALWAYS },
{ "END", "TK_END", ALWAYS },
{ "EACH", "TK_EACH", TRIGGER },
@ -226,6 +227,7 @@ static Keyword aKeywordTable[] = {
{ "NATURAL", "TK_JOIN_KW", ALWAYS },
{ "NO", "TK_NO", FKEY },
{ "NOT", "TK_NOT", ALWAYS },
{ "NOTHING", "TK_NOTHING", ALWAYS },
{ "NOTNULL", "TK_NOTNULL", ALWAYS },
{ "NULL", "TK_NULL", ALWAYS },
{ "OF", "TK_OF", ALWAYS },