#include "sqliteInt.h"
#ifndef SQLITE_OMIT_UPSERT
static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
do{
Upsert *pNext = p->pNextUpsert;
sqlite3ExprListDelete(db, p->pUpsertTarget);
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
sqlite3ExprListDelete(db, p->pUpsertSet);
sqlite3ExprDelete(db, p->pUpsertWhere);
sqlite3DbFree(db, p->pToFree);
sqlite3DbFree(db, p);
p = pNext;
}while( p );
}
void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
if( p ) upsertDelete(db, p);
}
Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
if( p==0 ) return 0;
return sqlite3UpsertNew(db,
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
sqlite3ExprListDup(db, p->pUpsertSet, 0),
sqlite3ExprDup(db, p->pUpsertWhere, 0),
sqlite3UpsertDup(db, p->pNextUpsert)
);
}
Upsert *sqlite3UpsertNew(
sqlite3 *db,
ExprList *pTarget,
Expr *pTargetWhere,
ExprList *pSet,
Expr *pWhere,
Upsert *pNext
){
Upsert *pNew;
pNew = sqlite3DbMallocZero(db, sizeof(Upsert));
if( pNew==0 ){
sqlite3ExprListDelete(db, pTarget);
sqlite3ExprDelete(db, pTargetWhere);
sqlite3ExprListDelete(db, pSet);
sqlite3ExprDelete(db, pWhere);
sqlite3UpsertDelete(db, pNext);
return 0;
}else{
pNew->pUpsertTarget = pTarget;
pNew->pUpsertTargetWhere = pTargetWhere;
pNew->pUpsertSet = pSet;
pNew->pUpsertWhere = pWhere;
pNew->isDoUpdate = pSet!=0;
pNew->pNextUpsert = pNext;
}
return pNew;
}
int sqlite3UpsertAnalyzeTarget(
Parse *pParse,
SrcList *pTabList,
Upsert *pUpsert
){
Table *pTab;
int rc;
int iCursor;
Index *pIdx;
ExprList *pTarget;
Expr *pTerm;
NameContext sNC;
Expr sCol[2];
int nClause = 0;
assert( pTabList->nSrc==1 );
assert( pTabList->a[0].pTab!=0 );
assert( pUpsert!=0 );
assert( pUpsert->pUpsertTarget!=0 );
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
for(; pUpsert && pUpsert->pUpsertTarget;
pUpsert=pUpsert->pNextUpsert, nClause++){
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
if( rc ) return rc;
rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
if( rc ) return rc;
pTab = pTabList->a[0].pTab;
pTarget = pUpsert->pUpsertTarget;
iCursor = pTabList->a[0].iCursor;
if( HasRowid(pTab)
&& pTarget->nExpr==1
&& (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
&& pTerm->iColumn==XN_ROWID
){
assert( pUpsert->pUpsertIdx==0 );
continue;
}
memset(sCol, 0, sizeof(sCol));
sCol[0].op = TK_COLLATE;
sCol[0].pLeft = &sCol[1];
sCol[1].op = TK_COLUMN;
sCol[1].iTable = pTabList->a[0].iCursor;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int ii, jj, nn;
if( !IsUniqueIndex(pIdx) ) continue;
if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
if( pIdx->pPartIdxWhere ){
if( pUpsert->pUpsertTargetWhere==0 ) continue;
if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
pIdx->pPartIdxWhere, iCursor)!=0 ){
continue;
}
}
nn = pIdx->nKeyCol;
for(ii=0; ii<nn; ii++){
Expr *pExpr;
sCol[0].u.zToken = (char*)pIdx->azColl[ii];
if( pIdx->aiColumn[ii]==XN_EXPR ){
assert( pIdx->aColExpr!=0 );
assert( pIdx->aColExpr->nExpr>ii );
assert( pIdx->bHasExpr );
pExpr = pIdx->aColExpr->a[ii].pExpr;
if( pExpr->op!=TK_COLLATE ){
sCol[0].pLeft = pExpr;
pExpr = &sCol[0];
}
}else{
sCol[0].pLeft = &sCol[1];
sCol[1].iColumn = pIdx->aiColumn[ii];
pExpr = &sCol[0];
}
for(jj=0; jj<nn; jj++){
if( sqlite3ExprCompare(pParse,pTarget->a[jj].pExpr,pExpr,iCursor)<2 ){
break;
}
}
if( jj>=nn ){
break;
}
}
if( ii<nn ){
continue;
}
pUpsert->pUpsertIdx = pIdx;
break;
}
if( pUpsert->pUpsertIdx==0 ){
char zWhich[16];
if( nClause==0 && pUpsert->pNextUpsert==0 ){
zWhich[0] = 0;
}else{
sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1);
}
sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any "
"PRIMARY KEY or UNIQUE constraint", zWhich);
return SQLITE_ERROR;
}
}
return SQLITE_OK;
}
int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
Upsert *pNext;
if( NEVER(pUpsert==0) ) return 0;
pNext = pUpsert->pNextUpsert;
if( pNext==0 ) return 1;
if( pNext->pUpsertTarget==0 ) return 1;
if( pNext->pUpsertIdx==0 ) return 1;
return 0;
}
Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){
while(
pUpsert
&& pUpsert->pUpsertTarget!=0
&& pUpsert->pUpsertIdx!=pIdx
){
pUpsert = pUpsert->pNextUpsert;
}
return pUpsert;
}
void sqlite3UpsertDoUpdate(
Parse *pParse,
Upsert *pUpsert,
Table *pTab,
Index *pIdx,
int iCur
){
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
SrcList *pSrc;
int iDataCur;
int i;
Upsert *pTop = pUpsert;
assert( v!=0 );
assert( pUpsert!=0 );
iDataCur = pUpsert->iDataCur;
pUpsert = sqlite3UpsertOfIndex(pTop, pIdx);
VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
if( pIdx && iCur!=iDataCur ){
if( HasRowid(pTab) ){
int regRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regRowid);
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
int nPk = pPk->nKeyCol;
int iPk = pParse->nMem+1;
pParse->nMem += nPk;
for(i=0; i<nPk; i++){
int k;
assert( pPk->aiColumn[i]>=0 );
k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);
sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
VdbeComment((v, "%s.%s", pIdx->zName,
pTab->aCol[pPk->aiColumn[i]].zCnName));
}
sqlite3VdbeVerifyAbortable(v, OE_Abort);
i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
"corrupt database", P4_STATIC);
sqlite3MayAbort(pParse);
sqlite3VdbeJumpHere(v, i);
}
}
pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0);
for(i=0; i<pTab->nCol; i++){
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i);
}
}
sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0),
sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert);
VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
}
#endif