Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -4471,5 +4471,36 @@ } sqlite3DbFree(db, 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 */ Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -487,11 +487,11 @@ Parse *pParse, /* Parser context */ 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 */ - 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 */ int i, j; /* Loop counters */ Vdbe *v; /* Generate code into this virtual machine */ @@ -526,15 +526,10 @@ int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ 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; } dest.iSDParm = 0; /* Suppress a harmless compiler warning */ @@ -1078,11 +1073,11 @@ } insert_cleanup: sqlite3SrcListDelete(db, pTabList); sqlite3ExprListDelete(db, pList); - sqlite3ExprListDelete(db, pUpsert); + sqlite3UpsertDelete(db, pUpsert); sqlite3SelectDelete(db, pSelect); sqlite3IdListDelete(db, pColumn); sqlite3DbFree(db, aRegIdx); } Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -96,26 +96,10 @@ ** ** Then the "b" IdList records the list "a,b,c". */ 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. */ static void disableLookaside(Parse *pParse){ @@ -873,48 +857,22 @@ ////////////////////////// The INSERT command ///////////////////////////////// // 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;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;} @@ -1458,12 +1416,11 @@ {A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R, B.z, E);} // INSERT trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). { - A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,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). {A = sqlite3TriggerDeleteStep(pParse->db, &X, Y, B.z, E);} Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1093,10 +1093,11 @@ typedef struct TreeView TreeView; 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; typedef struct WhereInfo WhereInfo; typedef struct With With; @@ -2044,11 +2045,10 @@ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ #define OE_Abort 2 /* Back out changes but do no rollback transaction */ #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 */ #define OE_SetDflt 8 /* Set the foreign key value to its default */ #define OE_Cascade 9 /* Cascade the changes */ @@ -2707,10 +2707,21 @@ #define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */ #define NC_VarSelect 0x0040 /* A correlated subquery has been seen */ #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ #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. ** ** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0. @@ -3206,12 +3217,13 @@ u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ 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 */ }; @@ -3739,11 +3751,11 @@ void sqlite3AutoincrementEnd(Parse *pParse); #else # 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*); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); @@ -3915,11 +3927,11 @@ void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); 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*); TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*, const char*,const char*); @@ -4255,10 +4267,18 @@ void sqlite3WithPush(Parse*, With*, u8); #else #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 ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In Index: src/trigger.c ================================================================== --- src/trigger.c +++ src/trigger.c @@ -23,10 +23,11 @@ sqlite3ExprDelete(db, pTmp->pWhere); sqlite3ExprListDelete(db, pTmp->pExprList); sqlite3SelectDelete(db, pTmp->pSelect); sqlite3IdListDelete(db, pTmp->pIdList); + sqlite3UpsertDelete(db, pTmp->pUpsert); sqlite3DbFree(db, pTmp->zSpan); sqlite3DbFree(db, pTmp); } } @@ -414,11 +415,11 @@ sqlite3 *db, /* The database connection */ Token *pTableName, /* Name of the table into which we insert */ IdList *pColumn, /* List of columns in pTableName to insert into */ Select *pSelect, /* A SELECT statement that supplies values */ 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 */ ){ TriggerStep *pTriggerStep; @@ -426,14 +427,15 @@ pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd); 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); return pTriggerStep; } @@ -755,12 +757,12 @@ case TK_INSERT: { sqlite3Insert(pParse, 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; } case TK_DELETE: { sqlite3DeleteFrom(pParse,