AryaWu/sqlite
0
1/*2** 2018-04-123**4** The author disclaims copyright to this source code. In place of5** a legal notice, here is a blessing:6**7** May you do good and not evil.8** May you find forgiveness for yourself and forgive others.9** May you share freely, never taking more than you give.10**11*************************************************************************12** This file contains code to implement various aspects of UPSERT13** processing and handling of the Upsert object.14*/15#include "sqliteInt.h"16 17#ifndef SQLITE_OMIT_UPSERT18/*19** Free a list of Upsert objects20*/21static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){22 do{23 Upsert *pNext = p->pNextUpsert;24 sqlite3ExprListDelete(db, p->pUpsertTarget);25 sqlite3ExprDelete(db, p->pUpsertTargetWhere);26 sqlite3ExprListDelete(db, p->pUpsertSet);27 sqlite3ExprDelete(db, p->pUpsertWhere);28 sqlite3DbFree(db, p->pToFree);29 sqlite3DbFree(db, p);30 p = pNext;31 }while( p );32}33void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){34 if( p ) upsertDelete(db, p);35}36 37 38/*39** Duplicate an Upsert object.40*/41Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){42 if( p==0 ) return 0;43 return sqlite3UpsertNew(db,44 sqlite3ExprListDup(db, p->pUpsertTarget, 0),45 sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),46 sqlite3ExprListDup(db, p->pUpsertSet, 0),47 sqlite3ExprDup(db, p->pUpsertWhere, 0),48 sqlite3UpsertDup(db, p->pNextUpsert)49 );50}51 52/*53** Create a new Upsert object.54*/55Upsert *sqlite3UpsertNew(56 sqlite3 *db, /* Determines which memory allocator to use */57 ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */58 Expr *pTargetWhere, /* Optional WHERE clause on the target */59 ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */60 Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */61 Upsert *pNext /* Next ON CONFLICT clause in the list */62){63 Upsert *pNew;64 pNew = sqlite3DbMallocZero(db, sizeof(Upsert));65 if( pNew==0 ){66 sqlite3ExprListDelete(db, pTarget);67 sqlite3ExprDelete(db, pTargetWhere);68 sqlite3ExprListDelete(db, pSet);69 sqlite3ExprDelete(db, pWhere);70 sqlite3UpsertDelete(db, pNext);71 return 0;72 }else{73 pNew->pUpsertTarget = pTarget;74 pNew->pUpsertTargetWhere = pTargetWhere;75 pNew->pUpsertSet = pSet;76 pNew->pUpsertWhere = pWhere;77 pNew->isDoUpdate = pSet!=0;78 pNew->pNextUpsert = pNext;79 }80 return pNew;81}82 83/*84** Analyze the ON CONFLICT clause described by pUpsert. Resolve all85** symbols in the conflict-target.86**87** Return SQLITE_OK if everything works, or an error code is something88** is wrong.89*/90int sqlite3UpsertAnalyzeTarget(91 Parse *pParse, /* The parsing context */92 SrcList *pTabList, /* Table into which we are inserting */93 Upsert *pUpsert, /* The ON CONFLICT clauses */94 Upsert *pAll /* Complete list of all ON CONFLICT clauses */95){96 Table *pTab; /* That table into which we are inserting */97 int rc; /* Result code */98 int iCursor; /* Cursor used by pTab */99 Index *pIdx; /* One of the indexes of pTab */100 ExprList *pTarget; /* The conflict-target clause */101 Expr *pTerm; /* One term of the conflict-target clause */102 NameContext sNC; /* Context for resolving symbolic names */103 Expr sCol[2]; /* Index column converted into an Expr */104 int nClause = 0; /* Counter of ON CONFLICT clauses */105 106 assert( pTabList->nSrc==1 );107 assert( pTabList->a[0].pSTab!=0 );108 assert( pUpsert!=0 );109 assert( pUpsert->pUpsertTarget!=0 );110 111 /* Resolve all symbolic names in the conflict-target clause, which112 ** includes both the list of columns and the optional partial-index113 ** WHERE clause.114 */115 memset(&sNC, 0, sizeof(sNC));116 sNC.pParse = pParse;117 sNC.pSrcList = pTabList;118 for(; pUpsert && pUpsert->pUpsertTarget;119 pUpsert=pUpsert->pNextUpsert, nClause++){120 rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);121 if( rc ) return rc;122 rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);123 if( rc ) return rc;124 125 /* Check to see if the conflict target matches the rowid. */ 126 pTab = pTabList->a[0].pSTab;127 pTarget = pUpsert->pUpsertTarget;128 iCursor = pTabList->a[0].iCursor;129 if( HasRowid(pTab) 130 && pTarget->nExpr==1131 && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN132 && pTerm->iColumn==XN_ROWID133 ){134 /* The conflict-target is the rowid of the primary table */135 assert( pUpsert->pUpsertIdx==0 );136 continue;137 }138 139 /* Initialize sCol[0..1] to be an expression parse tree for a140 ** single column of an index. The sCol[0] node will be the TK_COLLATE141 ** operator and sCol[1] will be the TK_COLUMN operator. Code below142 ** will populate the specific collation and column number values143 ** prior to comparing against the conflict-target expression.144 */145 memset(sCol, 0, sizeof(sCol));146 sCol[0].op = TK_COLLATE;147 sCol[0].pLeft = &sCol[1];148 sCol[1].op = TK_COLUMN;149 sCol[1].iTable = pTabList->a[0].iCursor;150 151 /* Check for matches against other indexes */152 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){153 int ii, jj, nn;154 if( !IsUniqueIndex(pIdx) ) continue;155 if( pTarget->nExpr!=pIdx->nKeyCol ) continue;156 if( pIdx->pPartIdxWhere ){157 if( pUpsert->pUpsertTargetWhere==0 ) continue;158 if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,159 pIdx->pPartIdxWhere, iCursor)!=0 ){160 continue;161 }162 }163 nn = pIdx->nKeyCol;164 for(ii=0; ii<nn; ii++){165 Expr *pExpr;166 sCol[0].u.zToken = (char*)pIdx->azColl[ii];167 if( pIdx->aiColumn[ii]==XN_EXPR ){168 assert( pIdx->aColExpr!=0 );169 assert( pIdx->aColExpr->nExpr>ii );170 assert( pIdx->bHasExpr );171 pExpr = pIdx->aColExpr->a[ii].pExpr;172 if( pExpr->op!=TK_COLLATE ){173 sCol[0].pLeft = pExpr;174 pExpr = &sCol[0];175 }176 }else{177 sCol[0].pLeft = &sCol[1];178 sCol[1].iColumn = pIdx->aiColumn[ii];179 pExpr = &sCol[0];180 }181 for(jj=0; jj<nn; jj++){182 if( sqlite3ExprCompare(0,pTarget->a[jj].pExpr,pExpr,iCursor)<2 ){183 break; /* Column ii of the index matches column jj of target */184 }185 }186 if( jj>=nn ){187 /* The target contains no match for column jj of the index */188 break;189 }190 }191 if( ii<nn ){192 /* Column ii of the index did not match any term of the conflict target.193 ** Continue the search with the next index. */194 continue;195 }196 pUpsert->pUpsertIdx = pIdx;197 if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){198 /* Really this should be an error. The isDup ON CONFLICT clause will199 ** never fire. But this problem was not discovered until three years200 ** after multi-CONFLICT upsert was added, and so we silently ignore201 ** the problem to prevent breaking applications that might actually202 ** have redundant ON CONFLICT clauses. */203 pUpsert->isDup = 1;204 }205 break;206 }207 if( pUpsert->pUpsertIdx==0 ){208 char zWhich[16];209 if( nClause==0 && pUpsert->pNextUpsert==0 ){210 zWhich[0] = 0;211 }else{212 sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1);213 }214 sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any "215 "PRIMARY KEY or UNIQUE constraint", zWhich);216 return SQLITE_ERROR;217 }218 }219 return SQLITE_OK;220}221 222/*223** Return true if pUpsert is the last ON CONFLICT clause with a224** conflict target, or if pUpsert is followed by another ON CONFLICT225** clause that targets the INTEGER PRIMARY KEY.226*/227int sqlite3UpsertNextIsIPK(Upsert *pUpsert){228 Upsert *pNext;229 if( NEVER(pUpsert==0) ) return 0;230 pNext = pUpsert->pNextUpsert;231 while( 1 /*exit-by-return*/ ){232 if( pNext==0 ) return 1;233 if( pNext->pUpsertTarget==0 ) return 1;234 if( pNext->pUpsertIdx==0 ) return 1;235 if( !pNext->isDup ) return 0;236 pNext = pNext->pNextUpsert;237 }238 return 0;239}240 241/*242** Given the list of ON CONFLICT clauses described by pUpsert, and243** a particular index pIdx, return a pointer to the particular ON CONFLICT244** clause that applies to the index. Or, if the index is not subject to245** any ON CONFLICT clause, return NULL.246*/247Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){248 while(249 pUpsert250 && pUpsert->pUpsertTarget!=0251 && pUpsert->pUpsertIdx!=pIdx252 ){253 pUpsert = pUpsert->pNextUpsert;254 }255 return pUpsert;256}257 258/*259** Generate bytecode that does an UPDATE as part of an upsert.260**261** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.262** In this case parameter iCur is a cursor open on the table b-tree that263** currently points to the conflicting table row. Otherwise, if pIdx264** is not NULL, then pIdx is the constraint that failed and iCur is a265** cursor points to the conflicting row.266*/267void sqlite3UpsertDoUpdate(268 Parse *pParse, /* The parsing and code-generating context */269 Upsert *pUpsert, /* The ON CONFLICT clause for the upsert */270 Table *pTab, /* The table being updated */271 Index *pIdx, /* The UNIQUE constraint that failed */272 int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */273){274 Vdbe *v = pParse->pVdbe;275 sqlite3 *db = pParse->db;276 SrcList *pSrc; /* FROM clause for the UPDATE */277 int iDataCur;278 int i;279 Upsert *pTop = pUpsert;280 281 assert( v!=0 );282 assert( pUpsert!=0 );283 iDataCur = pUpsert->iDataCur;284 pUpsert = sqlite3UpsertOfIndex(pTop, pIdx);285 VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));286 if( pIdx && iCur!=iDataCur ){287 if( HasRowid(pTab) ){288 int regRowid = sqlite3GetTempReg(pParse);289 sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);290 sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);291 VdbeCoverage(v);292 sqlite3ReleaseTempReg(pParse, regRowid);293 }else{294 Index *pPk = sqlite3PrimaryKeyIndex(pTab);295 int nPk = pPk->nKeyCol;296 int iPk = pParse->nMem+1;297 pParse->nMem += nPk;298 for(i=0; i<nPk; i++){299 int k;300 assert( pPk->aiColumn[i]>=0 );301 k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);302 sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);303 VdbeComment((v, "%s.%s", pIdx->zName,304 pTab->aCol[pPk->aiColumn[i]].zCnName));305 }306 sqlite3VdbeVerifyAbortable(v, OE_Abort);307 i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);308 VdbeCoverage(v);309 sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0, 310 "corrupt database", P4_STATIC);311 sqlite3MayAbort(pParse);312 sqlite3VdbeJumpHere(v, i);313 }314 }315 /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does.316 ** So we have to make a copy before passing it down into sqlite3Update() */317 pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0);318 /* excluded.* columns of type REAL need to be converted to a hard real */319 for(i=0; i<pTab->nCol; i++){320 if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){321 sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i);322 }323 }324 sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0),325 sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert);326 VdbeNoopComment((v, "End DO UPDATE of UPSERT"));327}328 329#endif /* SQLITE_OMIT_UPSERT */330 