/* ** Generate code for an UPDATE of a virtual table. ** ** The strategy is that we create an ephemerial table that contains ** for each row to be changed: ** ** (A) The original rowid of that row. ** (B) The revised rowid for the row. (note1) ** (C) The content of every column in the row. ** ** Then we loop over this ephemeral table and for each row in ** the ephermeral table call VUpdate. ** ** When finished, drop the ephemeral table. ** ** (note1) Actually, if we know in advance that (A) is always the same ** as (B) we only store (A), then duplicate (A) when pulling ** it out of the ephemeral table before calling VUpdate. */ static void updateVirtualTable( Parse *pParse, /* The parsing context */ SrcList *pSrc, /* The virtual table to be modified */ Table *pTab, /* The virtual table */ ExprList *pChanges, /* The columns to change in the UPDATE statement */ Expr *pRowid, /* Expression used to recompute the rowid */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ Expr *pWhere /* WHERE clause of the UPDATE statement */ ){ Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ ExprList *pEList = 0; /* The result set of the SELECT statement */ Select *pSelect = 0; /* The SELECT statement */ Expr *pExpr; /* Temporary expression */ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 *db = pParse->db; /* Database connection */ const char *pVtab = (const char*)pTab->pVtab; SelectDest dest; /* Construct the SELECT statement that will find the new values for ** all updated rows. */ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3CreateIdExpr(pParse, "_rowid_"), 0); if( pRowid ){ pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid), 0); } assert( pTab->iPKey<0 ); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]>=0 ){ pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr); }else{ pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); } pEList = sqlite3ExprListAppend(pParse, pEList, pExpr, 0); } pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ iReg = ++pParse->nMem; pParse->nMem += pTab->nCol+1; sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); for(i=0; i<pTab->nCol; i++){ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr); sqlite3VdbeJumpHere(v, addr-1); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(db, pSelect); }
/* ** Generate code for an UPDATE of a virtual table. ** ** The strategy is that we create an ephemerial table that contains ** for each row to be changed: ** ** (A) The original rowid of that row. ** (B) The revised rowid for the row. (note1) ** (C) The content of every column in the row. ** ** Then we loop over this ephemeral table and for each row in ** the ephermeral table call VUpdate. ** ** When finished, drop the ephemeral table. ** ** (note1) Actually, if we know in advance that (A) is always the same ** as (B) we only store (A), then duplicate (A) when pulling ** it out of the ephemeral table before calling VUpdate. */ static void updateVirtualTable( Parse *pParse, /* The parsing context */ SrcList *pSrc, /* The virtual table to be modified */ Table *pTab, /* The virtual table */ ExprList *pChanges, /* The columns to change in the UPDATE statement */ Expr *pRowid, /* Expression used to recompute the rowid */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ Expr *pWhere /* WHERE clause of the UPDATE statement */ ){ Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ ExprList *pEList = 0; /* The result set of the SELECT statement */ Select *pSelect = 0; /* The SELECT statement */ Expr *pExpr; /* Temporary expression */ int ephemTab; /* Table holding the result of the SELECT */ int i; /* Loop counter */ int addr; /* Address of top of loop */ /* Construct the SELECT statement that will find the new values for ** all updated rows. */ pEList = sqlite3ExprListAppend(0, sqlite3CreateIdExpr("_rowid_"), 0); if( pRowid ){ pEList = sqlite3ExprListAppend(pEList, sqlite3ExprDup(pRowid), 0); } assert( pTab->iPKey<0 ); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]>=0 ){ pExpr = sqlite3ExprDup(pChanges->a[aXRef[i]].pExpr); }else{ pExpr = sqlite3CreateIdExpr(pTab->aCol[i].zName); } pEList = sqlite3ExprListAppend(pEList, pExpr, 0); } pSelect = sqlite3SelectNew(pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0); /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); /* fill the ephemeral table */ sqlite3Select(pParse, pSelect, SRT_Table, ephemTab, 0, 0, 0, 0); /* ** Generate code to scan the ephemeral table and call VDelete and ** VInsert */ sqlite3VdbeAddOp(v, OP_Rewind, ephemTab, 0); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp(v, OP_Column, ephemTab, 0); if( pRowid ){ sqlite3VdbeAddOp(v, OP_Column, ephemTab, 1); }else{ sqlite3VdbeAddOp(v, OP_Dup, 0, 0); } for(i=0; i<pTab->nCol; i++){ sqlite3VdbeAddOp(v, OP_Column, ephemTab, i+1+(pRowid!=0)); } pParse->pVirtualLock = pTab; sqlite3VdbeOp3(v, OP_VUpdate, 0, pTab->nCol+2, (const char*)pTab->pVtab, P3_VTAB); sqlite3VdbeAddOp(v, OP_Next, ephemTab, addr); sqlite3VdbeAddOp(v, OP_Close, ephemTab, 0); /* Cleanup */ sqlite3SelectDelete(pSelect); }