diff options
author | drh <drh@noemail.net> | 2018-01-12 23:38:10 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2018-01-12 23:38:10 +0000 |
commit | 9df81a2989fb8f441f1db5a71c6d3b1b3c9ca33c (patch) | |
tree | 696331869d065a55674257bbd448ff87e3cd0f9a /src | |
parent | 4ec6f3a075b8b0214f567e5fab7bf653e42eb31b (diff) | |
parent | 41fb367a2c5aec8973fa004ba030f2aa55bbad5c (diff) | |
download | sqlite-9df81a2989fb8f441f1db5a71c6d3b1b3c9ca33c.tar.gz sqlite-9df81a2989fb8f441f1db5a71c6d3b1b3c9ca33c.zip |
Add the sqlite3_value_nochange() API, usable from within the xUpdate method
of a virtual table to discover whether or not a column was unchanged at the
SQL level.
FossilOrigin-Name: dec3ea4e4e6c4b1761ddc883a29eaa50dcd663ce6199667cc0ff82f7849d4f2a
Diffstat (limited to 'src')
-rw-r--r-- | src/sqlite.h.in | 24 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/update.c | 8 | ||||
-rw-r--r-- | src/vdbe.c | 31 | ||||
-rw-r--r-- | src/vdbeInt.h | 2 | ||||
-rw-r--r-- | src/vdbeapi.c | 7 | ||||
-rw-r--r-- | src/vdbeaux.c | 6 | ||||
-rw-r--r-- | src/vdbemem.c | 2 |
8 files changed, 69 insertions, 12 deletions
diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ba673748e..2661c8f35 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4799,6 +4799,9 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), ** datatype of the value ** <tr><td><b>sqlite3_value_numeric_type </b> ** <td>→ <td>Best numeric datatype of the value +** <tr><td><b>sqlite3_value_nochange </b> +** <td>→ <td>True if the column is unchanged in an UPDATE +** against a virtual table. ** </table></blockquote> ** ** <b>Details:</b> @@ -4847,6 +4850,19 @@ SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** +** ^Within the [xUpdate] method of a [virtual table], the +** sqlite3_value_nochange(X) interface returns true if and only if +** the column corresponding to X is unchanged by the UPDATE operation +** that the xUpdate method call was invoked to implement and if +** and the prior [xColumn] method call that was invoked to extracted +** the value for that column returned without setting a result (probably +** because it queried [sqlite3_vtab_nochange()] and found that the column +** was unchanging). ^Within an [xUpdate] method, any value for which +** sqlite3_value_nochange(X) is true will in all other respects appear +** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other +** than within an [xUpdate] method call for an UPDATE statement, then +** the return value is arbitrary and meaningless. +** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to @@ -4869,6 +4885,7 @@ int sqlite3_value_bytes(sqlite3_value*); int sqlite3_value_bytes16(sqlite3_value*); int sqlite3_value_type(sqlite3_value*); int sqlite3_value_numeric_type(sqlite3_value*); +int sqlite3_value_nochange(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values @@ -8306,6 +8323,13 @@ int sqlite3_vtab_on_conflict(sqlite3 *); ** 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. +** +** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that +** the column is not changed by the UPDATE statement, they the xColumn +** method can optionally return without setting a result, without calling +** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces]. +** In that case, [sqlite3_value_nochange(X)] will return true for the +** same column in the [xUpdate] method. */ int sqlite3_vtab_nochange(sqlite3_context*); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8e7913320..b7b402b8e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3117,6 +3117,7 @@ struct AuthContext { #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ #define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */ #define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */ +#define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */ /* * Each trigger present in the database schema is stored as an instance of diff --git a/src/update.c b/src/update.c index 3de36fe21..32c1a8371 100644 --- a/src/update.c +++ b/src/update.c @@ -827,7 +827,8 @@ static void updateVirtualTable( if( aXRef[i]>=0 ){ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); }else{ - sqlite3VdbeAddOp4Int(v, OP_VColumn, iCsr, i, regArg+2+i, 1); + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); + sqlite3VdbeChangeP5(v, 1); /* Enable sqlite3_vtab_nochange() */ } } if( HasRowid(pTab) ){ @@ -862,6 +863,11 @@ static void updateVirtualTable( /* Create a record from the argument register contents and insert it into ** the ephemeral table. */ sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec); +#ifdef SQLITE_DEBUG + /* Signal an assert() within OP_MakeRecord that it is allowed to + ** accept no-change records with serial_type 10 */ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); +#endif sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid); } diff --git a/src/vdbe.c b/src/vdbe.c index d66387d23..d878ca8e3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -464,7 +464,7 @@ static void memTracePrint(Mem *p){ if( p->flags & MEM_Undefined ){ printf(" undefined"); }else if( p->flags & MEM_Null ){ - printf(" NULL"); + printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL"); }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ printf(" si:%lld", p->u.i); }else if( p->flags & MEM_Int ){ @@ -2792,9 +2792,18 @@ case OP_MakeRecord: { pRec = pLast; do{ assert( memIsValid(pRec) ); - pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format, &len); + serial_type = sqlite3VdbeSerialType(pRec, file_format, &len); if( pRec->flags & MEM_Zero ){ - if( nData ){ + if( serial_type==0 ){ + /* Values with MEM_Null and MEM_Zero are created by xColumn virtual + ** table methods that never invoke sqlite3_result_xxxxx() while + ** computing an unchanging column value in an UPDATE statement. + ** Give such values a special internal-use-only serial-type of 10 + ** so that they can be passed through to xUpdate and have + ** a true sqlite3_value_nochange(). */ + assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB ); + serial_type = 10; + }else if( nData ){ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem; }else{ nZero += pRec->u.nZero; @@ -2805,6 +2814,7 @@ case OP_MakeRecord: { testcase( serial_type==127 ); testcase( serial_type==128 ); nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type); + pRec->uTemp = serial_type; if( pRec==pData0 ) break; pRec--; }while(1); @@ -6697,15 +6707,15 @@ case OP_VFilter: { /* jump */ #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VColumn P1 P2 P3 P4 * +/* Opcode: VColumn P1 P2 P3 * P5 ** Synopsis: r[P3]=vcolumn(P2) ** ** 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 +** an unchanging column during an UPDATE operation, then the P5 +** value is 1. Otherwise, P5 is 0. The P5 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. @@ -6730,8 +6740,13 @@ case OP_VColumn: { assert( pModule->xColumn ); memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; - sContext.bVtabNoChng = pOp->p4.i!=0; - MemSetTypeFlag(pDest, MEM_Null); + if( pOp->p5 ){ + sqlite3VdbeMemSetNull(pDest); + pDest->flags = MEM_Null|MEM_Zero; + pDest->u.nZero = 0; + }else{ + MemSetTypeFlag(pDest, MEM_Null); + } rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2); sqlite3VtabImportErrmsg(p, pVtab); if( sContext.isError ){ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index f646a4036..b7e324c94 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -224,6 +224,8 @@ struct sqlite3_value { ** If the MEM_Null flag is set, then the value is an SQL NULL value. ** For a pointer type created using sqlite3_bind_pointer() or ** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set. +** If both MEM_Null and MEM_Zero are set, that means that the value is +** an unchanging column value from VColumn. ** ** If the MEM_Str flag is set then Mem.z points at a string representation. ** Usually this is encoded in the same unicode encoding as the main diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 19aa783bb..bdd6d1cd0 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -268,6 +268,11 @@ int sqlite3_value_type(sqlite3_value* pVal){ return aType[pVal->flags&MEM_AffMask]; } +/* Return true if a parameter to xUpdate represents an unchanged column */ +int sqlite3_value_nochange(sqlite3_value *pVal){ + return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); +} + /* Make a copy of an sqlite3_value object */ sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ @@ -761,7 +766,7 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ */ int sqlite3_vtab_nochange(sqlite3_context *p){ assert( p ); - return p->bVtabNoChng; + return sqlite3_value_nochange(p->pOut); } /* diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 1780d37b8..72a5b96bb 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3454,7 +3454,11 @@ u32 sqlite3VdbeSerialGet( Mem *pMem /* Memory cell to write value into */ ){ switch( serial_type ){ - case 10: /* Reserved for future use */ + case 10: { /* Internal use only: NULL with virtual table + ** UPDATE no-change flag set */ + pMem->flags = MEM_Null|MEM_Zero; + break; + } case 11: /* Reserved for future use */ case 0: { /* Null */ /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */ diff --git a/src/vdbemem.c b/src/vdbemem.c index 107d831f4..d8f1e6432 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -43,7 +43,7 @@ int sqlite3VdbeCheckMemInvariants(Mem *p){ if( p->flags & MEM_Null ){ /* Cannot be both MEM_Null and some other type */ assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob - |MEM_RowSet|MEM_Frame|MEM_Agg|MEM_Zero))==0 ); + |MEM_RowSet|MEM_Frame|MEM_Agg))==0 ); /* If MEM_Null is set, then either the value is a pure NULL (the usual ** case) or it is a pointer set using sqlite3_bind_pointer() or |