diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/insert.c | 12 | ||||
-rw-r--r-- | src/parse.y | 2 | ||||
-rw-r--r-- | src/sqliteInt.h | 19 | ||||
-rw-r--r-- | src/update.c | 2 | ||||
-rw-r--r-- | src/upsert.c | 58 |
5 files changed, 88 insertions, 5 deletions
diff --git a/src/insert.c b/src/insert.c index 4fc79c223..313434bad 100644 --- a/src/insert.c +++ b/src/insert.c @@ -804,6 +804,13 @@ void sqlite3Insert( pParse->nMem += pIdx->nColumn; } } +#ifndef SQLITE_OMIT_UPSERT + if( pUpsert ){ + pTabList->a[0].iCursor = iDataCur; + sqlite3UpsertAnalyze(pParse, pTabList, pUpsert); + } +#endif + /* This is the top of the main insertion loop */ if( useTempTable ){ @@ -1006,7 +1013,7 @@ void sqlite3Insert( int isReplace; /* Set to true if constraints may cause a replace */ int bUseSeek; /* True to use OPFLAG_SEEKRESULT */ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, - regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0 + regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert ); sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0); @@ -1242,7 +1249,8 @@ void sqlite3GenerateConstraintChecks( u8 overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */ - int *aiChng /* column i is unchanged if aiChng[i]<0 */ + int *aiChng, /* column i is unchanged if aiChng[i]<0 */ + Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */ ){ Vdbe *v; /* VDBE under constrution */ Index *pIdx; /* Pointer to one of the indices */ diff --git a/src/parse.y b/src/parse.y index 76643619e..e4a0bc5df 100644 --- a/src/parse.y +++ b/src/parse.y @@ -873,7 +873,7 @@ upsert(A) ::= . { A = 0; } upsert(A) ::= upsert(X) ON CONFLICT LP sortlist(Y) RP DO UPDATE SET setlist(Z) where_opt(W). { A = sqlite3UpsertNew(pParse->db,X,Y,Z,W); /*X-overwrites-A*/ } -upsert(A) ::= upsert(X) ON DUPLIATE KEY UPDATE setlist(Z) where_opt(W). +upsert(A) ::= upsert(X) ON DUPLICATE KEY UPDATE setlist(Z) where_opt(W). { A = sqlite3UpsertNew(pParse->db,X,0,Z,W); /*X-overwrites-A*/ } upsert(A) ::= upsert(X) ON CONFLICT LP sortlist(Y) RP DO NOTHING. { A = sqlite3UpsertNew(pParse->db,X,Y,0,0); /*X-overwrites-A*/ } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index bb23e25b4..b1e412fcb 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2713,11 +2713,27 @@ struct NameContext { ** 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. +** +** The pUpsertTarget field is only set if the ON CONFLICT clause includes +** conflict-target clause. (In "ON CONFLICT(a,b)" the "(a,b)" is the +** conflict-target clause.) +** +** pUpsertSet is the list of column=expr terms of the UPDATE statement. +** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING. The +** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the +** WHERE clause is omitted. +** +** The pUpsertIdx is a transient pointer to the unique index described +** by pUpsertTarget. If pUpsertTarget describes the rowid, then pUpsertIdx +** will be NULL. pUpsertIdx does not own the Index object it points to. +** Care must be taken to ensure that the Index object does not expire while +** the pointer is valid. */ struct Upsert { ExprList *pUpsertTarget; /* Optional description of conflicting index */ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ + Index *pUpsertIdx; /* UNIQUE index referenced by pUpsertTarget */ Upsert *pUpsertNext; /* Next ON CONFLICT clause in the list */ }; @@ -3877,7 +3893,7 @@ void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int); void sqlite3ResolvePartIdxLabel(Parse*,int); void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int, - u8,u8,int,int*,int*); + u8,u8,int,int*,int*,Upsert*); #ifdef SQLITE_ENABLE_NULL_TRIM void sqlite3SetMakeRecordP5(Vdbe*,Table*); #else @@ -4274,6 +4290,7 @@ const char *sqlite3JournalModename(int); Upsert *sqlite3UpsertNew(sqlite3*,Upsert*,ExprList*,ExprList*,Expr*); void sqlite3UpsertDelete(sqlite3*,Upsert*); Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); + int sqlite3UpsertAnalyze(Parse*,SrcList*,Upsert*); #else #define sqlite3UpsertNew(x,y,z,w) ((Upsert*)0) #define sqlite3UpsertDelete(x,y) diff --git a/src/update.c b/src/update.c index a65f2a8e4..6b3d5f494 100644 --- a/src/update.c +++ b/src/update.c @@ -626,7 +626,7 @@ void sqlite3Update( assert( regOldRowid>0 ); sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur, regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace, - aXRef); + aXRef, 0); /* Do FK constraint checks. */ if( hasFK ){ diff --git a/src/upsert.c b/src/upsert.c index b42ef06a1..d326a7c6f 100644 --- a/src/upsert.c +++ b/src/upsert.c @@ -69,4 +69,62 @@ Upsert *sqlite3UpsertNew( return pNew; } +/* +** Analyze the ON CONFLICT clause(s) described by pUpsert. Resolve all +** symbols in the conflict-target clausees. Fill in the pUpsertIdx pointers. +** +** Return non-zero if there are errors. +*/ +int sqlite3UpsertAnalyze( + Parse *pParse, /* The parsing context */ + SrcList *pTabList, /* Table into which we are inserting */ + Upsert *pUpsert /* The list of ON CONFLICT clauses */ +){ + NameContext sNC; + Upsert *p; + Table *pTab; + Index *pIdx; + int rc = SQLITE_OK; + int nDoNothing = 0; + + assert( pTabList->nSrc==1 ); + assert( pTabList->a[0].pTab!=0 ); + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; + pTab = pTabList->a[0].pTab; + for(p=pUpsert; p; p=p->pUpsertNext){ + if( p->pUpsertTarget==0 ){ + if( p->pUpsertSet ){ + /* This is a MySQL-style ON DUPLICATE KEY clause. The ON DUPLICATE + ** KEY clause can only be used if there is exactly one uniqueness + ** constraint and/or PRIMARY KEY */ + int nUnique = 0; + for(pIdx = pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( IsUniqueIndex(pIdx) ){ + p->pUpsertIdx = pIdx; + nUnique++; + } + } + if( pTab->iPKey>=0 ) nUnique++; + if( nUnique!=0 ){ + sqlite3ErrorMsg(pParse, "ON DUPLICATE KEY may only be used if there " + "is exactly one UNIQUE or PRIMARY KEY constraint"); + return SQLITE_ERROR; + } + }else{ + nDoNothing++; + if( nDoNothing>1 ){ + sqlite3ErrorMsg(pParse, "multiple unconstrained DO NOTHING clauses"); + return SQLITE_ERROR; + } + } + continue; + } + rc = sqlite3ResolveExprListNames(&sNC, p->pUpsertTarget); + if( rc ) return rc; + } + return rc; +} + #endif /* SQLITE_OMIT_UPSERT */ |