diff options
author | drh <> | 2022-02-02 14:36:58 +0000 |
---|---|---|
committer | drh <> | 2022-02-02 14:36:58 +0000 |
commit | 30e314e4cbc7614d322b13703295af961c5e38c6 (patch) | |
tree | 802d85da18f04e493159acbeaa9716fa059d37e5 /src | |
parent | b30298d3ea05b73fb9c7d1556f6b2b054dd33d7a (diff) | |
download | sqlite-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.c | 23 | ||||
-rw-r--r-- | src/vdbeInt.h | 25 | ||||
-rw-r--r-- | src/vdbeapi.c | 79 |
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); } /* |