aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2018-01-12 23:38:10 +0000
committerdrh <drh@noemail.net>2018-01-12 23:38:10 +0000
commit9df81a2989fb8f441f1db5a71c6d3b1b3c9ca33c (patch)
tree696331869d065a55674257bbd448ff87e3cd0f9a /src
parent4ec6f3a075b8b0214f567e5fab7bf653e42eb31b (diff)
parent41fb367a2c5aec8973fa004ba030f2aa55bbad5c (diff)
downloadsqlite-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.in24
-rw-r--r--src/sqliteInt.h1
-rw-r--r--src/update.c8
-rw-r--r--src/vdbe.c31
-rw-r--r--src/vdbeInt.h2
-rw-r--r--src/vdbeapi.c7
-rw-r--r--src/vdbeaux.c6
-rw-r--r--src/vdbemem.c2
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&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>Best numeric datatype of the value
+** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
+** <td>&rarr;&nbsp;&nbsp;<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