Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -8295,10 +8295,22 @@ ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ int sqlite3_vtab_on_conflict(sqlite3 *); +/* +** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE +** +** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn] +** method of a [virtual table], then it returns true if and only if the +** column is being fetched as part of an UPDATE operation during which the +** column value will not change. Applications might use this to substitute +** a lighter-weight value to return that the corresponding [xUpdate] method +** understands as a "no-change" value. +*/ +int sqlite3_vtab_nochange(sqlite3_context*); + /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint ** ** This function may only be called from within a call to the [xBestIndex] ** method of a [virtual table]. Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -825,11 +825,11 @@ /* Populate the argument registers. */ for(i=0; inCol; i++){ if( aXRef[i]>=0 ){ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); }else{ - sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); + sqlite3VdbeAddOp4Int(v, OP_VColumn, iCsr, i, regArg+2+i, 1); } } if( HasRowid(pTab) ){ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); if( pRowid ){ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -6691,16 +6691,22 @@ break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VColumn P1 P2 P3 * * +/* Opcode: VColumn P1 P2 P3 P4 * ** Synopsis: r[P3]=vcolumn(P2) ** -** Store the value of the P2-th column of -** the row of the virtual-table that the -** P1 cursor is pointing to into register P3. +** Store in register P3 the value of the P2-th column of +** the current row of the virtual-table of cursor P1. +** +** If the VColumn opcode is being used to fetch the value of +** an unchanging column during an UPDATE operation, then the P4 +** value is 1. Otherwise, P4 is 0. The P4 value is returned +** by sqlite3_vtab_nochange() routine can can be used +** by virtual table implementations to return special "no-change" +** marks which can be more efficient, depending on the virtual table. */ case OP_VColumn: { sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; @@ -6718,10 +6724,11 @@ pVtab = pCur->uc.pVCur->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; + sContext.bVtabNoChng = pOp->p4.i!=0; MemSetTypeFlag(pDest, MEM_Null); rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); sqlite3VtabImportErrmsg(p, pVtab); if( sContext.isError ){ rc = sContext.isError; Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -316,10 +316,11 @@ Vdbe *pVdbe; /* The VM that owns this context */ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 skipFlag; /* Skip accumulator loading if true */ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */ + u8 bVtabNoChng; /* Fetching an unchanging column in a vtab UPDATE */ u8 argc; /* Number of arguments */ sqlite3_value *argv[1]; /* Argument set */ }; /* A bitfield type for use inside of structures. Always follow with :N where Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -742,10 +742,29 @@ */ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pOut ); return p->pOut->db; } + +/* +** If this routine is invoked from within an xColumn method of a virtual +** table, then it returns true if and only if the the call is during an +** UPDATE operation and the value of the column will not be modified +** by the UPDATE. +** +** If this routine is called from any context other than within the +** xColumn method of a virtual table, then the return value is meaningless +** and arbitrary. +** +** Virtual table implements might use this routine to optimize their +** performance by substituting a NULL result, or some other light-weight +** value, as a signal to the xUpdate routine that the column is unchanged. +*/ +int sqlite3_vtab_nochange(sqlite3_context *p){ + assert( p ); + return p->bVtabNoChng; +} /* ** Return the current time for a statement. If the current time ** is requested more than once within the same run of a single prepared ** statement, the exact same time is returned for each invocation regardless