mirror of https://github.com/sqlite/sqlite
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
This commit is contained in:
commit
9df81a2989
27
manifest
27
manifest
|
@ -1,5 +1,5 @@
|
|||
C Simplification\sto\sthe\simplementation\sof\sOP_Insert.
|
||||
D 2018-01-12T19:33:18.062
|
||||
C Add\sthe\ssqlite3_value_nochange()\sAPI,\susable\sfrom\swithin\sthe\sxUpdate\smethod\nof\sa\svirtual\stable\sto\sdiscover\swhether\sor\snot\sa\scolumn\swas\sunchanged\sat\sthe\nSQL\slevel.
|
||||
D 2018-01-12T23:38:10.473
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 38f84f301cbef443b2d269f67a74b8cc536469831f70df7c3e912acc04932cc2
|
||||
|
@ -487,10 +487,10 @@ F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
|
|||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 8b22abe193e4d8243befa2038e4ae2405802fed1c446e5e502d11f652e09ba74
|
||||
F src/shell.c.in b87abffd0db09203ad8a133d56fe8f154ace5ec0a14197a153fb7d80b1438c01
|
||||
F src/sqlite.h.in f83e63a48fb31fefc69c83bbe8700b4b44acdd64e440219087b0f14e35eeb8d4
|
||||
F src/sqlite.h.in 9daf78e8f3cecc9ea0c3a82201f75bb74f789ecbfcda28d2e47fa80b3d956961
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
|
||||
F src/sqliteInt.h fd8702c65994d7de3e2d8f7d85d958731da1ed29476571fdfa2290fd8ec0bf80
|
||||
F src/sqliteInt.h 9c70315598b34810a83e4894455acb18e95cf63ce4e6cbb451ac2d17eabc2544
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
|
@ -550,17 +550,17 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
|||
F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2
|
||||
F src/treeview.c eae35972ff44f67064de2eaf35f04afe94e7aea3271a8b3bcebb3f954880fec3
|
||||
F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
|
||||
F src/update.c 8bd52c38d6d426925be4488ee106db26d9ee344406315671ed246ddace8d6091
|
||||
F src/update.c a90a32ffc0100265b0693dbbdbe490756447af181f5ea2c138cce515b08c8795
|
||||
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c 7315e97a8dc2c8e19ca64196c652cf0a65d13fd0a211b2cec082062372dc6261
|
||||
F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739
|
||||
F src/vdbe.c e660530c799acf41616200373cdaec7af3fce5a25de40d900e8319d50971df73
|
||||
F src/vdbe.c ccc1e17a30325068ae4f0292e8601997946886d23acc989c68f2a261a2795c70
|
||||
F src/vdbe.h 134beb7a12a6213c00eba58febaede33447cc4441bc568a0d9c144b33fc3720a
|
||||
F src/vdbeInt.h a0969c14950bcc61b2dfa19c6f62b42c5012013fcda342ca927a192ed37e6592
|
||||
F src/vdbeapi.c f519346f6db99a1eb3f85ca3ae8aede9ff9c4d90fec809dae7cb946786b2e270
|
||||
F src/vdbeaux.c 7d635c30e55196ee8e0af605aceab0c3036ed6d6ed55dd6639c4b2a2a4593b4f
|
||||
F src/vdbeInt.h 5442fc816b6cf19c8801724199fd6b77a02eb31a7a174021713f8c59b30e51fa
|
||||
F src/vdbeapi.c 02f773681d06e46454b0606339068d4d4490873dc4a7334bc0c6030552bb2c8c
|
||||
F src/vdbeaux.c fc124962c9900b19d201e57084a4302d07136a77f51fdbdfabd625a3ca3b6dc1
|
||||
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
|
||||
F src/vdbemem.c 8478f7fb1948bf8fdeec7c2cb59ea58155c31258b9cd43c56d485e03ed40bd07
|
||||
F src/vdbemem.c 7548dd5af03d24d534a5dbc41e3bbdf1fab83e9c8856a8d2549ed2ccf33d0e80
|
||||
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
|
||||
F src/vdbetrace.c 48e11ebe040c6b41d146abed2602e3d00d621d7ebe4eb29b0a0f1617fd3c2f6c
|
||||
F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a
|
||||
|
@ -1699,7 +1699,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 874285e477dd9bd164e25ddb08b6b80daaa8cfd111b4180ecce59b1ce26f77a5
|
||||
R 0ce0db05c5816c8a3f6d5399a0318468
|
||||
P 6acbdba59e9df4313a6232d925a70390acdc43dfa380b4fba7bb8bd442d6e728 a1b3f28569f2a8d82b2931527fdfe191b421f3e1ea18ee30e04211e1ad645993
|
||||
R 33c612cb07bc4c3b03c1437b3563b788
|
||||
T +closed a1b3f28569f2a8d82b2931527fdfe191b421f3e1ea18ee30e04211e1ad645993
|
||||
U drh
|
||||
Z 101b1825b326975007694adf2a0d837c
|
||||
Z 1fe0c448d0c954d66825b437c30d556c
|
||||
|
|
|
@ -1 +1 @@
|
|||
6acbdba59e9df4313a6232d925a70390acdc43dfa380b4fba7bb8bd442d6e728
|
||||
dec3ea4e4e6c4b1761ddc883a29eaa50dcd663ce6199667cc0ff82f7849d4f2a
|
|
@ -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*);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
31
src/vdbe.c
31
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 ){
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue