aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <>2022-02-02 14:36:58 +0000
committerdrh <>2022-02-02 14:36:58 +0000
commit30e314e4cbc7614d322b13703295af961c5e38c6 (patch)
tree802d85da18f04e493159acbeaa9716fa059d37e5 /src
parentb30298d3ea05b73fb9c7d1556f6b2b054dd33d7a (diff)
downloadsqlite-30e314e4cbc7614d322b13703295af961c5e38c6.tar.gz
sqlite-30e314e4cbc7614d322b13703295af961c5e38c6.zip
Refactor sqlite3_vtab_in() to make use of the existing
sqlite3_value_pointer() mechanism for passing the list of IN operator RHS values into xFilter, for improved memory safety. FossilOrigin-Name: 8965929be236fe1a6994f31b94c1b7590c7c1e809470c542a76f3e0e275d032f
Diffstat (limited to 'src')
-rw-r--r--src/vdbe.c23
-rw-r--r--src/vdbeInt.h25
-rw-r--r--src/vdbeapi.c79
3 files changed, 65 insertions, 62 deletions
diff --git a/src/vdbe.c b/src/vdbe.c
index 5643d43ce..5410a7912 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -7736,20 +7736,27 @@ case OP_VOpen: {
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VInitIn P1 P2 P3 * *
-** Synopsis: r[P2]=cursor over eph table P1.
+** Synopsis: r[P2]=ValueList(P1,P3)
**
-** Initialize register P2 as a value that can be used as an iterator over
-** the contents of ephemeral table P1 by an xFilter callback implementation.
-** Register P3 is used as a cache by the iterator.
+** Set register P2 to be a pointer to a ValueList object for cursor P1
+** with cache register P3 and output register P3+1. This ValueList object
+** can be used as the first argument to sqlite3_vtab_in_first() and
+** sqlite3_vtab_in_next() to extract all of the values stored in the P1
+** cursor. Register P3 is used to hold the values returned by
+** sqlite3_vtab_in_first() and sqlite3_vtab_in_next().
*/
case OP_VInitIn: { /* out2 */
- VdbeCursor *pC;
+ VdbeCursor *pC; /* The cursor containing the RHS values */
+ ValueList *pRhs; /* New ValueList object to put in reg[P2] */
+
pC = p->apCsr[pOp->p1];
+ pRhs = sqlite3_malloc64( sizeof(*pRhs) );
+ if( pRhs==0 ) goto no_mem;
+ pRhs->pCsr = pC->uc.pCursor;
+ pRhs->pOut = &aMem[pOp->p3];
pOut = out2Prerelease(p, pOp);
- pOut->z = (char*)(pC->uc.pCursor);
- pOut->u.pVal = &aMem[pOp->p3];
- pOut->uTemp = SQLITE_VTAB_IN_MAGIC;
pOut->flags = MEM_Null;
+ sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3_free);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 9ddb742a4..f02b37c6a 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -195,12 +195,6 @@ struct VdbeFrame {
*/
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
-/* Magic number for Mem.uTemp when it is acting as as the cache for the
-** IN(...) iterator for sqlite3_vtab_in_next()
-*/
-#define SQLITE_VTAB_IN_MAGIC 0xd3ab12ec
-
-
/*
** Internally, the vdbe manipulates nearly all SQL values as Mem
** structures. Each Mem struct may cache multiple representations (string,
@@ -213,7 +207,6 @@ struct sqlite3_value {
int nZero; /* Extra zero bytes when MEM_Zero and MEM_Blob set */
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
- sqlite3_value *pVal;/* Current value for xFilter IN(...) iterator */
} u;
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
@@ -490,6 +483,24 @@ struct PreUpdate {
};
/*
+** An instance of this object is used to pass an vector of values into
+** OP_VFilter, the xFilter method of a virtual table. The vector is the
+** set of values on the right-hand side of an IN constraint.
+**
+** The value as passed into xFilter is an sqlite3_value with a "pointer"
+** type, such as is generated by sqlite3_result_pointer() and read by
+** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null
+** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces
+** know how to use this object to step through all the values in the
+** right operand of the IN constraint.
+*/
+typedef struct ValueList ValueList;
+struct ValueList {
+ BtCursor *pCsr; /* An ephemeral table holding all values */
+ sqlite3_value *pOut; /* Register to hold each decoded output value */
+};
+
+/*
** Function prototypes
*/
void sqlite3VdbeError(Vdbe*, const char *, ...);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 46b466b6b..75d7bf15c 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -847,62 +847,47 @@ int sqlite3_vtab_nochange(sqlite3_context *p){
}
/*
-** The first argument is an iterator value created by VDBE instruction
-** OP_VInitIn. The iterator is guaranteed to point to a valid entry. This
-** function attempts to load the current value from the iterator into
-** object pVal->u.pVal. If successful, (*ppOut) is set to point to
-** pVal->u.pVal and SQLITE_OK is returned. Otherwise, if an error
-** occurs, an SQLite error code is returned and (*ppOut) is left unchanged.
-*/
-static int vtabInLoadValue(sqlite3_value *pVal, sqlite3_value **ppOut){
- BtCursor *pCsr = (BtCursor*)pVal->z;
- sqlite3_value *pOut = pVal->u.pVal;
- int sz;
- int rc;
-
- sz = (int)sqlite3BtreePayloadSize(pCsr);
- if( sz>pVal->szMalloc ){
- if( pVal->szMalloc==0 ) pVal->zMalloc = 0;
- pVal->zMalloc = sqlite3DbReallocOrFree(pVal->db, pVal->zMalloc, sz*2);
- if( pVal->zMalloc ){
- pVal->szMalloc = sqlite3DbMallocSize(pVal->db, pVal->zMalloc);
- }else{
- pVal->szMalloc = 0;
- return SQLITE_NOMEM_BKPT;
- }
- }
-
- rc = sqlite3BtreePayload(pCsr, 0, sz, pVal->zMalloc);
- if( rc==SQLITE_OK ){
- u32 iSerial;
- int iOff = 1 + getVarint32((const u8*)&pVal->zMalloc[1], iSerial);
- sqlite3VdbeSerialGet((const u8*)&pVal->zMalloc[iOff], iSerial, pOut);
- pOut->enc = ENC(pVal->db);
- *ppOut = pOut;
- }
- return rc;
-}
-
-/*
** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
** sqlite3_vtab_in_next() (if bNext!=0).
*/
-static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){
+static int valueFromValueList(
+ sqlite3_value *pVal, /* Pointer to the ValueList object */
+ sqlite3_value **ppOut, /* Store the next value from the list here */
+ int bNext /* 1 for _next(). 0 for _first() */
+){
int rc;
- BtCursor *pCsr;
+ ValueList *pRhs;
+
*ppOut = 0;
if( pVal==0 ) return SQLITE_MISUSE;
- if( pVal->uTemp!=SQLITE_VTAB_IN_MAGIC ) return SQLITE_MISUSE;
- pCsr = (BtCursor*)pVal->z;
+ pRhs = (ValueList*)sqlite3_value_pointer(pVal, "ValueList");
+ if( pRhs==0 ) return SQLITE_MISUSE;
if( bNext ){
- rc = sqlite3BtreeNext(pCsr, 0);
+ rc = sqlite3BtreeNext(pRhs->pCsr, 0);
}else{
int dummy = 0;
- rc = sqlite3BtreeFirst(pCsr, &dummy);
- if( rc==SQLITE_OK && sqlite3BtreeEof(pCsr) ) rc = SQLITE_DONE;
+ rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy);
+ if( rc==SQLITE_OK && sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE;
}
if( rc==SQLITE_OK ){
- rc = vtabInLoadValue(pVal, ppOut);
+ u32 sz; /* Size of current row in bytes */
+ Mem sMem; /* Raw content of current row */
+ memset(&sMem, 0, sizeof(sMem));
+ sz = sqlite3BtreePayloadSize(pRhs->pCsr);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem);
+ if( rc==SQLITE_OK ){
+ u8 *zBuf = (u8*)sMem.z;
+ u32 iSerial;
+ sqlite3_value *pOut = pRhs->pOut;
+ int iOff = 1 + getVarint32(&zBuf[1], iSerial);
+ sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut);
+ if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){
+ rc = SQLITE_NOMEM;
+ }else{
+ *ppOut = pOut;
+ }
+ }
+ sqlite3VdbeMemRelease(&sMem);
}
return rc;
}
@@ -912,7 +897,7 @@ static int vtabInOp(sqlite3_value *pVal, sqlite3_value **ppOut, int bNext){
** Set (*ppOut) to point to this value before returning.
*/
int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){
- return vtabInOp(pVal, ppOut, 0);
+ return valueFromValueList(pVal, ppOut, 0);
}
/*
@@ -920,7 +905,7 @@ int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){
** Set (*ppOut) to point to this value before returning.
*/
int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){
- return vtabInOp(pVal, ppOut, 1);
+ return valueFromValueList(pVal, ppOut, 1);
}
/*