diff options
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/update.c')
-rw-r--r-- | ext/pdo_sqlite/sqlite/src/update.c | 176 |
1 files changed, 146 insertions, 30 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/update.c b/ext/pdo_sqlite/sqlite/src/update.c index b6c6b8b1ed..2565787f04 100644 --- a/ext/pdo_sqlite/sqlite/src/update.c +++ b/ext/pdo_sqlite/sqlite/src/update.c @@ -16,9 +16,22 @@ */ #include "sqliteInt.h" +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Forward declaration */ +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 *pRowidExpr, /* 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 */ +); +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + /* -** The most recently coded instruction was an OP_Column to retrieve column -** 'i' of table pTab. This routine sets the P3 parameter of the +** The most recently coded instruction was an OP_Column to retrieve the +** i-th column of table pTab. This routine sets the P3 parameter of the ** OP_Column to the default value, if any. ** ** The default value of a column is specified by a DEFAULT clause in the @@ -44,7 +57,7 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i){ if( pTab && !pTab->pSelect ){ sqlite3_value *pValue; - u8 enc = sqlite3VdbeDb(v)->enc; + u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; sqlite3ValueFromExpr(pCol->pDflt, enc, pCol->affinity, &pValue); if( pValue ){ @@ -89,6 +102,7 @@ void sqlite3Update( int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ + int iDb; /* Database containing the table being updated */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* Trying to update a view */ @@ -99,7 +113,9 @@ void sqlite3Update( int oldIdx = -1; /* index of trigger "old" temp table */ sContext.pParse = 0; - if( pParse->nErr || sqlite3_malloc_failed ) goto update_cleanup; + if( pParse->nErr || sqlite3MallocFailed() ){ + goto update_cleanup; + } db = pParse->db; assert( pTabList->nSrc==1 ); @@ -107,6 +123,7 @@ void sqlite3Update( */ pTab = sqlite3SrcListLookup(pParse, pTabList); if( pTab==0 ) goto update_cleanup; + iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); /* Figure out if we have any triggers and if the table being ** updated is a view @@ -126,10 +143,8 @@ void sqlite3Update( if( sqlite3IsReadOnly(pParse, pTab, triggers_exist) ){ goto update_cleanup; } - if( isView ){ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto update_cleanup; - } + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto update_cleanup; } aXRef = sqliteMallocRaw( sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; @@ -192,7 +207,7 @@ void sqlite3Update( { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - pTab->aCol[j].zName, db->aDb[pTab->iDb].zName); + pTab->aCol[j].zName, db->aDb[iDb].zName); if( rc==SQLITE_DENY ){ goto update_cleanup; }else if( rc==SQLITE_IGNORE ){ @@ -231,7 +246,6 @@ void sqlite3Update( } } if( i<pIdx->nColumn ){ - if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto update_cleanup; apIdx[nIdx++] = pIdx; aIdxUsed[j] = 1; }else{ @@ -239,6 +253,24 @@ void sqlite3Update( } } + /* Begin generating code. + */ + v = sqlite3GetVdbe(pParse); + if( v==0 ) goto update_cleanup; + if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); + sqlite3BeginWriteOperation(pParse, 1, iDb); + +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* Virtual tables must be handled separately */ + if( IsVirtual(pTab) ){ + updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, + pWhere); + pWhere = 0; + pTabList = 0; + goto update_cleanup; + } +#endif + /* Resolve the column names in all the expressions in the ** WHERE clause. */ @@ -252,20 +284,13 @@ void sqlite3Update( sqlite3AuthContextPush(pParse, &sContext, pTab->zName); } - /* Begin generating code. - */ - v = sqlite3GetVdbe(pParse); - if( v==0 ) goto update_cleanup; - if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, 1, pTab->iDb); - /* If we are trying to update a view, realize that view into ** a ephemeral table. */ if( isView ){ Select *pView; pView = sqlite3SelectDup(pTab->pSelect); - sqlite3Select(pParse, pView, SRT_VirtualTab, iCur, 0, 0, 0, 0); + sqlite3Select(pParse, pView, SRT_EphemTab, iCur, 0, 0, 0, 0); sqlite3SelectDelete(pView); } @@ -274,9 +299,9 @@ void sqlite3Update( pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0); if( pWInfo==0 ) goto update_cleanup; - /* Remember the index of every item to be updated. + /* Remember the rowid of every item to be updated. */ - sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0); + sqlite3VdbeAddOp(v, IsVirtual(pTab) ? OP_VRowid : OP_Rowid, iCur, 0); sqlite3VdbeAddOp(v, OP_FifoWrite, 0, 0); /* End the database scan loop. @@ -307,7 +332,7 @@ void sqlite3Update( /* Open a cursor and make it point to the record that is ** being updated. */ - sqlite3OpenTableForReading(v, iCur, pTab); + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); } sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0); @@ -355,16 +380,14 @@ void sqlite3Update( } } - if( !isView ){ + if( !isView && !IsVirtual(pTab) ){ /* ** Open every index that needs updating. Note that if any ** index could potentially invoke a REPLACE conflict resolution ** action, then we need to open all indices because we might need ** to be deleting some records. */ - sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0); - sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum); - sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol); + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); if( onError==OE_Replace ){ openAll = 1; }else{ @@ -378,9 +401,10 @@ void sqlite3Update( } for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ - sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0); + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + sqlite3VdbeAddOp(v, OP_Integer, iDb, 0); sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, - (char*)&pIdx->keyInfo, P3_KEYINFO); + (char*)pKey, P3_KEYINFO_HANDOFF); assert( pParse->nTab>iCur+i+1 ); } } @@ -388,7 +412,7 @@ void sqlite3Update( /* Loop over every record that needs updating. We have to load ** the old data for each record to be updated because some columns ** might not change and we will need to copy the old value. - ** Also, the old data is needed to delete the old index entires. + ** Also, the old data is needed to delete the old index entries. ** So make the cursor point at the old record. */ if( !triggers_exist ){ @@ -429,7 +453,7 @@ void sqlite3Update( /* Delete the old indices for the current record. */ - sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, aIdxUsed); + sqlite3GenerateRowIndexDelete(v, pTab, iCur, aIdxUsed); /* If changing the record number, delete the old record. */ @@ -492,7 +516,7 @@ void sqlite3Update( if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){ sqlite3VdbeAddOp(v, OP_Callback, 1, 0); sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, "rows updated", P3_STATIC); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", P3_STATIC); } update_cleanup: @@ -504,3 +528,95 @@ update_cleanup: sqlite3ExprDelete(pWhere); return; } + +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** 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); +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ |