| Index: third_party/sqlite/sqlite-src-3100200/src/update.c
|
| diff --git a/third_party/sqlite/sqlite-src-3080704/src/update.c b/third_party/sqlite/sqlite-src-3100200/src/update.c
|
| similarity index 82%
|
| copy from third_party/sqlite/sqlite-src-3080704/src/update.c
|
| copy to third_party/sqlite/sqlite-src-3100200/src/update.c
|
| index 3af4017f1ba9a3143bbe2c9d0dd78b50aab3bd54..a9735cadcad390c274f55f08482a3802f79ad0e2 100644
|
| --- a/third_party/sqlite/sqlite-src-3080704/src/update.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/src/update.c
|
| @@ -134,9 +134,9 @@ void sqlite3Update(
|
|
|
| /* Register Allocations */
|
| int regRowCount = 0; /* A count of rows changed */
|
| - int regOldRowid; /* The old rowid */
|
| - int regNewRowid; /* The new rowid */
|
| - int regNew; /* Content of the NEW.* table in triggers */
|
| + int regOldRowid = 0; /* The old rowid */
|
| + int regNewRowid = 0; /* The new rowid */
|
| + int regNew = 0; /* Content of the NEW.* table in triggers */
|
| int regOld = 0; /* Content of OLD.* table in triggers */
|
| int regRowSet = 0; /* Rowset of rows to be updated */
|
| int regKey = 0; /* composite PRIMARY KEY value */
|
| @@ -263,16 +263,20 @@ void sqlite3Update(
|
| assert( chngPk==0 || chngPk==1 );
|
| chngKey = chngRowid + chngPk;
|
|
|
| - /* The SET expressions are not actually used inside the WHERE loop.
|
| - ** So reset the colUsed mask
|
| + /* The SET expressions are not actually used inside the WHERE loop.
|
| + ** So reset the colUsed mask. Unless this is a virtual table. In that
|
| + ** case, set all bits of the colUsed mask (to ensure that the virtual
|
| + ** table implementation makes all columns available).
|
| */
|
| - pTabList->a[0].colUsed = 0;
|
| + pTabList->a[0].colUsed = IsVirtual(pTab) ? (Bitmask)-1 : 0;
|
|
|
| hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
|
|
|
| /* There is one entry in the aRegIdx[] array for each index on the table
|
| ** being updated. Fill in aRegIdx[] with a register number that will hold
|
| - ** the key for accessing each index.
|
| + ** the key for accessing each index.
|
| + **
|
| + ** FIXME: Be smarter about omitting indexes that use expressions.
|
| */
|
| for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
| int reg;
|
| @@ -281,7 +285,8 @@ void sqlite3Update(
|
| }else{
|
| reg = 0;
|
| for(i=0; i<pIdx->nKeyCol; i++){
|
| - if( aXRef[pIdx->aiColumn[i]]>=0 ){
|
| + i16 iIdxCol = pIdx->aiColumn[i];
|
| + if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
|
| reg = ++pParse->nMem;
|
| break;
|
| }
|
| @@ -297,29 +302,20 @@ void sqlite3Update(
|
| 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, onError);
|
| - pWhere = 0;
|
| - pTabList = 0;
|
| - goto update_cleanup;
|
| - }
|
| -#endif
|
| -
|
| /* Allocate required registers. */
|
| - regRowSet = ++pParse->nMem;
|
| - regOldRowid = regNewRowid = ++pParse->nMem;
|
| - if( chngPk || pTrigger || hasFK ){
|
| - regOld = pParse->nMem + 1;
|
| + if( !IsVirtual(pTab) ){
|
| + regRowSet = ++pParse->nMem;
|
| + regOldRowid = regNewRowid = ++pParse->nMem;
|
| + if( chngPk || pTrigger || hasFK ){
|
| + regOld = pParse->nMem + 1;
|
| + pParse->nMem += pTab->nCol;
|
| + }
|
| + if( chngKey || pTrigger || hasFK ){
|
| + regNewRowid = ++pParse->nMem;
|
| + }
|
| + regNew = pParse->nMem + 1;
|
| pParse->nMem += pTab->nCol;
|
| }
|
| - if( chngKey || pTrigger || hasFK ){
|
| - regNewRowid = ++pParse->nMem;
|
| - }
|
| - regNew = pParse->nMem + 1;
|
| - pParse->nMem += pTab->nCol;
|
|
|
| /* Start the view context. */
|
| if( isView ){
|
| @@ -342,6 +338,15 @@ void sqlite3Update(
|
| goto update_cleanup;
|
| }
|
|
|
| +#ifndef SQLITE_OMIT_VIRTUALTABLE
|
| + /* Virtual tables must be handled separately */
|
| + if( IsVirtual(pTab) ){
|
| + updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
|
| + pWhere, onError);
|
| + goto update_cleanup;
|
| + }
|
| +#endif
|
| +
|
| /* Begin the database scan
|
| */
|
| if( HasRowid(pTab) ){
|
| @@ -381,6 +386,7 @@ void sqlite3Update(
|
| if( pWInfo==0 ) goto update_cleanup;
|
| okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
| for(i=0; i<nPk; i++){
|
| + assert( pPk->aiColumn[i]>=0 );
|
| sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
|
| iPk+i);
|
| }
|
| @@ -390,7 +396,7 @@ void sqlite3Update(
|
| regKey = iPk;
|
| }else{
|
| sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
|
| - sqlite3IndexAffinityStr(v, pPk), nPk);
|
| + sqlite3IndexAffinityStr(db, pPk), nPk);
|
| sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
|
| }
|
| sqlite3WhereEnd(pWInfo);
|
| @@ -425,7 +431,7 @@ void sqlite3Update(
|
| if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
| if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
| }
|
| - sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen,
|
| + sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
|
| 0, 0);
|
| }
|
|
|
| @@ -503,7 +509,6 @@ void sqlite3Update(
|
| newmask = sqlite3TriggerColmask(
|
| pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
|
| );
|
| - /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/
|
| for(i=0; i<pTab->nCol; i++){
|
| if( i==pTab->iPKey ){
|
| sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
| @@ -519,7 +524,7 @@ void sqlite3Update(
|
| */
|
| testcase( i==31 );
|
| testcase( i==32 );
|
| - sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
|
| + sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
|
| }else{
|
| sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
|
| }
|
| @@ -561,7 +566,7 @@ void sqlite3Update(
|
| }
|
|
|
| if( !isView ){
|
| - int j1 = 0; /* Address of jump instruction */
|
| + int addr1 = 0; /* Address of jump instruction */
|
| int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
|
|
| /* Do constraint checks. */
|
| @@ -577,20 +582,20 @@ void sqlite3Update(
|
| /* Delete the index entries associated with the current record. */
|
| if( bReplace || chngKey ){
|
| if( pPk ){
|
| - j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
|
| + addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
|
| }else{
|
| - j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
|
| + addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
|
| }
|
| VdbeCoverageNeverTaken(v);
|
| }
|
| - sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
|
| + sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
|
|
|
| /* If changing the record number, delete the old record. */
|
| if( hasFK || chngKey || pPk!=0 ){
|
| sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
|
| }
|
| if( bReplace || chngKey ){
|
| - sqlite3VdbeJumpHere(v, j1);
|
| + sqlite3VdbeJumpHere(v, addr1);
|
| }
|
|
|
| if( hasFK ){
|
| @@ -627,7 +632,7 @@ void sqlite3Update(
|
| sqlite3VdbeResolveLabel(v, labelContinue);
|
| sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
| }else{
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
|
| + sqlite3VdbeGoto(v, labelContinue);
|
| }
|
| sqlite3VdbeResolveLabel(v, labelBreak);
|
|
|
| @@ -681,21 +686,23 @@ update_cleanup:
|
| /*
|
| ** Generate code for an UPDATE of a virtual table.
|
| **
|
| -** The strategy is that we create an ephemeral table that contains
|
| +** There are two possible strategies - the default and the special
|
| +** "onepass" strategy. Onepass is only used if the virtual table
|
| +** implementation indicates that pWhere may match at most one row.
|
| +**
|
| +** The default strategy is to create an ephemeral table that contains
|
| ** for each row to be changed:
|
| **
|
| ** (A) The original rowid of that row.
|
| -** (B) The revised rowid for the row. (note1)
|
| +** (B) The revised rowid for the row.
|
| ** (C) The content of every column in the row.
|
| **
|
| -** Then we loop over this ephemeral table and for each row in
|
| -** the ephemeral table call VUpdate.
|
| -**
|
| -** When finished, drop the ephemeral table.
|
| +** Then loop through the contents of this ephemeral table executing a
|
| +** VUpdate for each row. 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.
|
| +** The "onepass" strategy does not use an ephemeral table. Instead, it
|
| +** stores the same values (A, B and C above) in a register array and
|
| +** makes a single invocation of VUpdate.
|
| */
|
| static void updateVirtualTable(
|
| Parse *pParse, /* The parsing context */
|
| @@ -708,67 +715,95 @@ static void updateVirtualTable(
|
| int onError /* ON CONFLICT strategy */
|
| ){
|
| 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*)sqlite3GetVTable(db, pTab);
|
| - SelectDest dest;
|
| -
|
| - /* Construct the SELECT statement that will find the new values for
|
| - ** all updated rows.
|
| - */
|
| - pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
|
| + WhereInfo *pWInfo;
|
| + int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */
|
| + int regArg; /* First register in VUpdate arg array */
|
| + int regRec; /* Register in which to assemble record */
|
| + int regRowid; /* Register for ephem table rowid */
|
| + int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
|
| + int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
|
| + int bOnePass; /* True to use onepass strategy */
|
| + int addr; /* Address of OP_OpenEphemeral */
|
| +
|
| + /* Allocate nArg registers to martial the arguments to VUpdate. Then
|
| + ** create and open the ephemeral table in which the records created from
|
| + ** these arguments will be temporarily stored. */
|
| + assert( v );
|
| + ephemTab = pParse->nTab++;
|
| + addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
|
| + regArg = pParse->nMem + 1;
|
| + pParse->nMem += nArg;
|
| + regRec = ++pParse->nMem;
|
| + regRowid = ++pParse->nMem;
|
| +
|
| + /* Start scanning the virtual table */
|
| + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
|
| + if( pWInfo==0 ) return;
|
| +
|
| + /* Populate the argument registers. */
|
| + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
|
| if( pRowid ){
|
| - pEList = sqlite3ExprListAppend(pParse, pEList,
|
| - sqlite3ExprDup(db, pRowid, 0));
|
| + sqlite3ExprCode(pParse, pRowid, regArg+1);
|
| + }else{
|
| + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
|
| }
|
| - assert( pTab->iPKey<0 );
|
| for(i=0; i<pTab->nCol; i++){
|
| if( aXRef[i]>=0 ){
|
| - pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
|
| + sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
|
| }else{
|
| - pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName);
|
| + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
|
| }
|
| - pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
|
| }
|
| - 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));
|
| - sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
|
|
| - /* 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;
|
| - addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(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);
|
| + bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
|
| +
|
| + if( bOnePass ){
|
| + /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
|
| + ** above. Also, if this is a top-level parse (not a trigger), clear the
|
| + ** multi-write flag so that the VM does not open a statement journal */
|
| + sqlite3VdbeChangeToNoop(v, addr);
|
| + if( sqlite3IsToplevel(pParse) ){
|
| + pParse->isMultiWrite = 0;
|
| + }
|
| + }else{
|
| + /* Create a record from the argument register contents and insert it into
|
| + ** the ephemeral table. */
|
| + sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
|
| + sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
|
| + sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
|
| + }
|
| +
|
| +
|
| + if( bOnePass==0 ){
|
| + /* End the virtual table scan */
|
| + sqlite3WhereEnd(pWInfo);
|
| +
|
| + /* Begin scannning through the ephemeral table. */
|
| + addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
|
| +
|
| + /* Extract arguments from the current row of the ephemeral table and
|
| + ** invoke the VUpdate method. */
|
| + for(i=0; i<nArg; i++){
|
| + sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i, regArg+i);
|
| + }
|
| }
|
| sqlite3VtabMakeWritable(pParse, pTab);
|
| - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
|
| + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, nArg, regArg, pVTab, P4_VTAB);
|
| sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
|
| sqlite3MayAbort(pParse);
|
| - sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
| - sqlite3VdbeJumpHere(v, addr);
|
| - sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
|
|
| - /* Cleanup */
|
| - sqlite3SelectDelete(db, pSelect);
|
| + /* End of the ephemeral table scan. Or, if using the onepass strategy,
|
| + ** jump to here if the scan visited zero rows. */
|
| + if( bOnePass==0 ){
|
| + sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
|
| + sqlite3VdbeJumpHere(v, addr);
|
| + sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
|
| + }else{
|
| + sqlite3WhereEnd(pWInfo);
|
| + }
|
| }
|
| #endif /* SQLITE_OMIT_VIRTUALTABLE */
|
|
|