aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c31
-rw-r--r--src/insert.c9
-rw-r--r--src/parse.y57
-rw-r--r--src/sqliteInt.h28
-rw-r--r--src/trigger.c10
5 files changed, 70 insertions, 65 deletions
diff --git a/src/build.c b/src/build.c
index c286b4bbe..e947f2c84 100644
--- a/src/build.c
+++ b/src/build.c
@@ -4473,3 +4473,34 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){
}
}
#endif /* !defined(SQLITE_OMIT_CTE) */
+
+#ifndef SQLITE_OMIT_UPSERT
+/*
+** Free a list of Upsert objects
+*/
+void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
+ while( p ){
+ Upsert *pNext = p->pUpsertNext;
+ sqlite3ExprListDelete(db, p->pUpsertTarget);
+ sqlite3ExprListDelete(db, p->pUpsertSet);
+ sqlite3DbFree(db, p);
+ p = pNext;
+ }
+}
+#endif /* SQLITE_OMIT_UPSERT */
+
+#ifndef SQLITE_OMIT_UPSERT
+/*
+** Duplicate an Upsert object
+*/
+Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
+ Upsert *pNew;
+ if( p==0 ) return 0;
+ pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
+ if( pNew==0 ) return 0;
+ pNew->pUpsertTarget = sqlite3ExprListDup(db, p->pUpsertTarget, 0);
+ pNew->pUpsertSet = sqlite3ExprListDup(db, p->pUpsertSet, 0);
+ pNew->pUpsertNext = sqlite3UpsertDup(db, p->pUpsertNext);
+ return pNew;
+}
+#endif /* SQLITE_OMIT_UPSERT */
diff --git a/src/insert.c b/src/insert.c
index 6906efe0b..4fc79c223 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -489,7 +489,7 @@ void sqlite3Insert(
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 */
- ExprList *pUpsert /* Upsert values */
+ Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
){
sqlite3 *db; /* The main database structure */
Table *pTab; /* The table to insert into. aka TABLE */
@@ -528,11 +528,6 @@ 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;
@@ -1080,7 +1075,7 @@ insert_end:
insert_cleanup:
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pList);
- sqlite3ExprListDelete(db, pUpsert);
+ sqlite3UpsertDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
sqlite3IdListDelete(db, pColumn);
sqlite3DbFree(db, aRegIdx);
diff --git a/src/parse.y b/src/parse.y
index 45ead7866..09335d21e 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -99,22 +99,6 @@
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.
*/
@@ -875,44 +859,18 @@ setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
//
cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S)
upsert(U). {
- sqlite3Insert(pParse, X, S, F, upsertType(pParse, R, U.e), U.p);
+ sqlite3Insert(pParse, X, S, F, R, U);
}
cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES.
{
sqlite3Insert(pParse, X, 0, F, R, 0);
}
-%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 upsert {Upsert*}
+%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
+upsert(A) ::= . { A = 0; }
+upsert(A) ::= ON CONFLICT DO UPDATE SET setlist. { A = 0; }
+upsert(A) ::= ON CONFLICT DO NOTHING. { A = 0; }
%type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
@@ -1460,8 +1418,7 @@ trigger_cmd(A) ::=
// INSERT
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). {
- A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,upsertType(pParse,R,U.e),
- U.p,B,Z);/*A-overwrites-R*/
+ A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,U,B,Z);/*A-overwrites-R*/
}
// DELETE
trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E).
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 1d40b45cc..d77df3ef9 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1095,6 +1095,7 @@ typedef struct Trigger Trigger;
typedef struct TriggerPrg TriggerPrg;
typedef struct TriggerStep TriggerStep;
typedef struct UnpackedRecord UnpackedRecord;
+typedef struct Upsert Upsert;
typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
@@ -2046,7 +2047,6 @@ 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 */
@@ -2710,6 +2710,17 @@ struct NameContext {
#define NC_Complex 0x2000 /* True if a function or subquery seen */
/*
+** An instance of the following object describes a single ON CONFLICT
+** clause in an upsert. A list of these objects may be attached to
+** an INSERT statement in order to form an upsert.
+*/
+struct Upsert {
+ ExprList *pUpsertTarget; /* Optional description of conflicting index */
+ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
+ Upsert *pUpsertNext; /* Next ON CONFLICT clause in the list */
+};
+
+/*
** An instance of the following structure contains all information
** needed to generate code for a single SELECT statement.
**
@@ -3208,8 +3219,9 @@ 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 or UPSERT. */
+ ExprList *pExprList; /* SET clause for UPDATE */
IdList *pIdList; /* Column names for INSERT */
+ Upsert *pUpsert; /* Upsert clauses on an INSERT */
char *zSpan; /* Original SQL text of this command */
TriggerStep *pNext; /* Next in the link-list */
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
@@ -3741,7 +3753,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
-void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, ExprList*);
+void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
@@ -3917,7 +3929,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
const char*,const char*);
TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
- Select*,u8,ExprList*,
+ Select*,u8,Upsert*,
const char*,const char*);
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8,
const char*,const char*);
@@ -4257,6 +4269,14 @@ const char *sqlite3JournalModename(int);
#define sqlite3WithPush(x,y,z)
#define sqlite3WithDelete(x,y)
#endif
+#ifndef SQLITE_OMIT_UPSERT
+ void sqlite3UpsertDelete(sqlite3*,Upsert*);
+ Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
+#else
+#define sqlite3UpsertDelete(x,y)
+#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#endif
+
/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
diff --git a/src/trigger.c b/src/trigger.c
index 4240357a6..bd5711f07 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -25,6 +25,7 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
sqlite3ExprListDelete(db, pTmp->pExprList);
sqlite3SelectDelete(db, pTmp->pSelect);
sqlite3IdListDelete(db, pTmp->pIdList);
+ sqlite3UpsertDelete(db, pTmp->pUpsert);
sqlite3DbFree(db, pTmp->zSpan);
sqlite3DbFree(db, pTmp);
@@ -416,7 +417,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 */
+ Upsert *pUpsert, /* ON CONFLICT clauses for upsert */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
){
@@ -428,10 +429,11 @@ TriggerStep *sqlite3TriggerInsertStep(
if( pTriggerStep ){
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
pTriggerStep->pIdList = pColumn;
+ pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
}else{
sqlite3IdListDelete(db, pColumn);
- sqlite3ExprListDelete(db, pUpsert);
+ sqlite3UpsertDelete(db, pUpsert);
}
sqlite3SelectDelete(db, pSelect);
@@ -757,8 +759,8 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
- pStep->pExprList ? OE_Update : pParse->eOrconf,
- sqlite3ExprListDup(db, pStep->pExprList, 0)
+ pParse->eOrconf,
+ sqlite3UpsertDup(db, pStep->pUpsert)
);
break;
}