blob: 5db4f5fc903e4069046c9873a64ae42a5d60300b [file] [log] [blame]
drhfcfd7562018-04-12 21:42:51 +00001/*
2** 2018-04-12
3**
4** The author disclaims copyright to this source code. In place of
5** 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 UPSERT
13** processing and handling of the Upsert object.
14*/
15#include "sqliteInt.h"
16
17#ifndef SQLITE_OMIT_UPSERT
18/*
19** Free a list of Upsert objects
20*/
21void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
drhe9c2e772018-04-13 13:06:45 +000022 if( p ){
drhfcfd7562018-04-12 21:42:51 +000023 sqlite3ExprListDelete(db, p->pUpsertTarget);
drhe9c2e772018-04-13 13:06:45 +000024 sqlite3ExprDelete(db, p->pUpsertTargetWhere);
drhfcfd7562018-04-12 21:42:51 +000025 sqlite3ExprListDelete(db, p->pUpsertSet);
26 sqlite3ExprDelete(db, p->pUpsertWhere);
27 sqlite3DbFree(db, p);
drhfcfd7562018-04-12 21:42:51 +000028 }
29}
30
31/*
32** Duplicate an Upsert object.
33*/
34Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
35 if( p==0 ) return 0;
36 return sqlite3UpsertNew(db,
drhfcfd7562018-04-12 21:42:51 +000037 sqlite3ExprListDup(db, p->pUpsertTarget, 0),
drhe9c2e772018-04-13 13:06:45 +000038 sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
drhfcfd7562018-04-12 21:42:51 +000039 sqlite3ExprListDup(db, p->pUpsertSet, 0),
40 sqlite3ExprDup(db, p->pUpsertWhere, 0)
41 );
42}
43
44/*
45** Create a new Upsert object.
46*/
47Upsert *sqlite3UpsertNew(
48 sqlite3 *db, /* Determines which memory allocator to use */
drhfcfd7562018-04-12 21:42:51 +000049 ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
drhe9c2e772018-04-13 13:06:45 +000050 Expr *pTargetWhere, /* Optional WHERE clause on the target */
drhfcfd7562018-04-12 21:42:51 +000051 ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
52 Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */
53){
54 Upsert *pNew;
55 pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
56 if( pNew==0 ){
drhfcfd7562018-04-12 21:42:51 +000057 sqlite3ExprListDelete(db, pTarget);
drhe9c2e772018-04-13 13:06:45 +000058 sqlite3ExprDelete(db, pTargetWhere);
drhfcfd7562018-04-12 21:42:51 +000059 sqlite3ExprListDelete(db, pSet);
60 sqlite3ExprDelete(db, pWhere);
61 return 0;
62 }else{
63 pNew->pUpsertTarget = pTarget;
drhe9c2e772018-04-13 13:06:45 +000064 pNew->pUpsertTargetWhere = pTargetWhere;
drhfcfd7562018-04-12 21:42:51 +000065 pNew->pUpsertSet = pSet;
drhfcfd7562018-04-12 21:42:51 +000066 pNew->pUpsertWhere = pWhere;
drhe9c2e772018-04-13 13:06:45 +000067 pNew->pUpsertIdx = 0;
drhfcfd7562018-04-12 21:42:51 +000068 }
69 return pNew;
70}
71
drh788d55a2018-04-13 01:15:09 +000072/*
drhe9c2e772018-04-13 13:06:45 +000073** Analyze the ON CONFLICT clause described by pUpsert. Resolve all
74** symbols in the conflict-target.
drh788d55a2018-04-13 01:15:09 +000075**
drhe9c2e772018-04-13 13:06:45 +000076** Return SQLITE_OK if everything works, or an error code is something
77** is wrong.
drh788d55a2018-04-13 01:15:09 +000078*/
drhe9c2e772018-04-13 13:06:45 +000079int sqlite3UpsertAnalyzeTarget(
drh788d55a2018-04-13 01:15:09 +000080 Parse *pParse, /* The parsing context */
81 SrcList *pTabList, /* Table into which we are inserting */
drhe9c2e772018-04-13 13:06:45 +000082 Upsert *pUpsert /* The ON CONFLICT clauses */
drh788d55a2018-04-13 01:15:09 +000083){
drhd5af5422018-04-13 14:27:01 +000084 Table *pTab; /* That table into which we are inserting */
85 int rc; /* Result code */
86 int iCursor; /* Cursor used by pTab */
87 Index *pIdx; /* One of the indexes of pTab */
88 ExprList *pTarget; /* The conflict-target clause */
89 Expr *pTerm; /* One term of the conflict-target clause */
90 NameContext sNC; /* Context for resolving symbolic names */
91 Expr sCol[2]; /* Index column converted into an Expr */
drh788d55a2018-04-13 01:15:09 +000092
93 assert( pTabList->nSrc==1 );
94 assert( pTabList->a[0].pTab!=0 );
drhe9c2e772018-04-13 13:06:45 +000095 assert( pUpsert!=0 );
96 assert( pUpsert->pUpsertTarget!=0 );
97
98 /* Resolve all symbolic names in the conflict-target clause, which
99 ** includes both the list of columns and the optional partial-index
100 ** WHERE clause.
101 */
drh788d55a2018-04-13 01:15:09 +0000102 memset(&sNC, 0, sizeof(sNC));
103 sNC.pParse = pParse;
104 sNC.pSrcList = pTabList;
drhe9c2e772018-04-13 13:06:45 +0000105 rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
106 if( rc ) return rc;
107 rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
108 if( rc ) return rc;
109
110 /* Check to see if the conflict target matches the rowid. */
drh788d55a2018-04-13 01:15:09 +0000111 pTab = pTabList->a[0].pTab;
drhe9c2e772018-04-13 13:06:45 +0000112 pTarget = pUpsert->pUpsertTarget;
drhd5af5422018-04-13 14:27:01 +0000113 iCursor = pTabList->a[0].iCursor;
drhe9c2e772018-04-13 13:06:45 +0000114 if( HasRowid(pTab)
115 && pTarget->nExpr==1
116 && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
drh54514c92018-04-17 21:59:34 +0000117 && pTerm->iColumn==XN_ROWID
drhe9c2e772018-04-13 13:06:45 +0000118 ){
119 /* The conflict-target is the rowid of the primary table */
120 assert( pUpsert->pUpsertIdx==0 );
121 return SQLITE_OK;
122 }
123
drh3b45d8b2018-04-13 13:44:48 +0000124 /* Initialize sCol[0..1] to be an expression parse tree for a
125 ** single column of an index. The sCol[0] node will be the TK_COLLATE
126 ** operator and sCol[1] will be the TK_COLUMN operator. Code below
127 ** will populate the specific collation and column number values
128 ** prior to comparing against the conflict-target expression.
129 */
130 memset(sCol, 0, sizeof(sCol));
131 sCol[0].op = TK_COLLATE;
132 sCol[0].pLeft = &sCol[1];
133 sCol[1].op = TK_COLUMN;
134 sCol[1].iTable = pTabList->a[0].iCursor;
135
drhe9c2e772018-04-13 13:06:45 +0000136 /* Check for matches against other indexes */
137 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
138 int ii, jj, nn;
139 if( !IsUniqueIndex(pIdx) ) continue;
140 if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
141 if( pIdx->pPartIdxWhere ){
142 if( pUpsert->pUpsertTargetWhere==0 ) continue;
143 if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
drhd5af5422018-04-13 14:27:01 +0000144 pIdx->pPartIdxWhere, iCursor)!=0 ){
drhe9c2e772018-04-13 13:06:45 +0000145 continue;
146 }
147 }
148 nn = pIdx->nKeyCol;
149 for(ii=0; ii<nn; ii++){
drh3b45d8b2018-04-13 13:44:48 +0000150 Expr *pExpr;
drh277434e2018-04-18 18:18:12 +0000151 sCol[0].u.zToken = (char*)pIdx->azColl[ii];
drh3b45d8b2018-04-13 13:44:48 +0000152 if( pIdx->aiColumn[ii]==XN_EXPR ){
drhe9c2e772018-04-13 13:06:45 +0000153 assert( pIdx->aColExpr!=0 );
154 assert( pIdx->aColExpr->nExpr>ii );
155 pExpr = pIdx->aColExpr->a[ii].pExpr;
drh277434e2018-04-18 18:18:12 +0000156 if( pExpr->op!=TK_COLLATE ){
157 sCol[0].pLeft = pExpr;
158 pExpr = &sCol[0];
159 }
drh3b45d8b2018-04-13 13:44:48 +0000160 }else{
drh277434e2018-04-18 18:18:12 +0000161 sCol[0].pLeft = &sCol[1];
drh3b45d8b2018-04-13 13:44:48 +0000162 sCol[1].iColumn = pIdx->aiColumn[ii];
drh3b45d8b2018-04-13 13:44:48 +0000163 pExpr = &sCol[0];
164 }
165 for(jj=0; jj<nn; jj++){
drhd5af5422018-04-13 14:27:01 +0000166 if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
drh3b45d8b2018-04-13 13:44:48 +0000167 break; /* Column ii of the index matches column jj of target */
drh788d55a2018-04-13 01:15:09 +0000168 }
169 }
drh3b45d8b2018-04-13 13:44:48 +0000170 if( jj>=nn ){
171 /* The target contains no match for column jj of the index */
172 break;
173 }
drh788d55a2018-04-13 01:15:09 +0000174 }
drh3b45d8b2018-04-13 13:44:48 +0000175 if( ii<nn ){
176 /* Column ii of the index did not match any term of the conflict target.
177 ** Continue the search with the next index. */
178 continue;
179 }
drhe9c2e772018-04-13 13:06:45 +0000180 pUpsert->pUpsertIdx = pIdx;
181 return SQLITE_OK;
drh788d55a2018-04-13 01:15:09 +0000182 }
drhe9c2e772018-04-13 13:06:45 +0000183 sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
184 "PRIMARY KEY or UNIQUE constraint");
185 return SQLITE_ERROR;
drh788d55a2018-04-13 01:15:09 +0000186}
187
drh9eddaca2018-04-13 18:59:17 +0000188/*
189** Generate bytecode that does an UPDATE as part of an upsert.
dan2cc00422018-04-17 18:16:10 +0000190**
191** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
192** In this case parameter iCur is a cursor open on the table b-tree that
193** currently points to the conflicting table row. Otherwise, if pIdx
194** is not NULL, then pIdx is the constraint that failed and iCur is a
195** cursor points to the conflicting row.
drh9eddaca2018-04-13 18:59:17 +0000196*/
197void sqlite3UpsertDoUpdate(
198 Parse *pParse, /* The parsing and code-generating context */
199 Upsert *pUpsert, /* The ON CONFLICT clause for the upsert */
200 Table *pTab, /* The table being updated */
201 Index *pIdx, /* The UNIQUE constraint that failed */
dan2cc00422018-04-17 18:16:10 +0000202 int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */
drh9eddaca2018-04-13 18:59:17 +0000203){
204 Vdbe *v = pParse->pVdbe;
drh0b30a112018-04-13 21:55:22 +0000205 sqlite3 *db = pParse->db;
drh0b30a112018-04-13 21:55:22 +0000206 SrcList *pSrc; /* FROM clause for the UPDATE */
drhc4ceea72018-08-21 12:16:33 +0000207 int iDataCur;
drha7ce1672019-08-30 23:15:00 +0000208 int i;
drh0b30a112018-04-13 21:55:22 +0000209
drh9eddaca2018-04-13 18:59:17 +0000210 assert( v!=0 );
drhc4ceea72018-08-21 12:16:33 +0000211 assert( pUpsert!=0 );
drh9eddaca2018-04-13 18:59:17 +0000212 VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
drhc4ceea72018-08-21 12:16:33 +0000213 iDataCur = pUpsert->iDataCur;
drhfb2213e2018-04-20 15:56:24 +0000214 if( pIdx && iCur!=iDataCur ){
215 if( HasRowid(pTab) ){
216 int regRowid = sqlite3GetTempReg(pParse);
217 sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
218 sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
219 VdbeCoverage(v);
220 sqlite3ReleaseTempReg(pParse, regRowid);
drh0b30a112018-04-13 21:55:22 +0000221 }else{
drhfb2213e2018-04-20 15:56:24 +0000222 Index *pPk = sqlite3PrimaryKeyIndex(pTab);
223 int nPk = pPk->nKeyCol;
224 int iPk = pParse->nMem+1;
drhfb2213e2018-04-20 15:56:24 +0000225 pParse->nMem += nPk;
226 for(i=0; i<nPk; i++){
227 int k;
228 assert( pPk->aiColumn[i]>=0 );
drhb9bcf7c2019-10-19 13:29:10 +0000229 k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);
drhfb2213e2018-04-20 15:56:24 +0000230 sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
drh9cadb232018-04-20 18:01:31 +0000231 VdbeComment((v, "%s.%s", pIdx->zName,
232 pTab->aCol[pPk->aiColumn[i]].zName));
drhe966a362018-04-14 22:35:34 +0000233 }
drh4031baf2018-05-28 17:31:20 +0000234 sqlite3VdbeVerifyAbortable(v, OE_Abort);
drhfb2213e2018-04-20 15:56:24 +0000235 i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
236 VdbeCoverage(v);
drh9cadb232018-04-20 18:01:31 +0000237 sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
238 "corrupt database", P4_STATIC);
drhfb2213e2018-04-20 15:56:24 +0000239 sqlite3VdbeJumpHere(v, i);
drhe966a362018-04-14 22:35:34 +0000240 }
drh0b30a112018-04-13 21:55:22 +0000241 }
drh2633b282018-04-19 21:29:52 +0000242 /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
243 ** we have to make a copy before passing it down into sqlite3Update() */
drhe966a362018-04-14 22:35:34 +0000244 pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
drha7ce1672019-08-30 23:15:00 +0000245 /* excluded.* columns of type REAL need to be converted to a hard real */
246 for(i=0; i<pTab->nCol; i++){
247 if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
248 sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i);
249 }
250 }
drh2633b282018-04-19 21:29:52 +0000251 sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
drhfb2213e2018-04-20 15:56:24 +0000252 pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
253 pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
254 pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */
drh9eddaca2018-04-13 18:59:17 +0000255 VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
256}
257
drhfcfd7562018-04-12 21:42:51 +0000258#endif /* SQLITE_OMIT_UPSERT */