Skip to content

Commit

Permalink
Also free up the MEM_RowSet bit in the Mem.flags field and have RowSe…
Browse files Browse the repository at this point in the history
…t objects

be destroyed using Mem.xDel.  This change results in faster code.
  • Loading branch information
D. Richard Hipp committed Aug 29, 2018
1 parent 778aa44 commit 304407e
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 76 deletions.
52 changes: 28 additions & 24 deletions src/rowset.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,30 +124,23 @@ struct RowSet {
#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */

/*
** Turn bulk memory into a RowSet object. N bytes of memory
** are available at pSpace. The db pointer is used as a memory context
** for any subsequent allocations that need to occur.
** Return a pointer to the new RowSet object.
**
** It must be the case that N is sufficient to make a Rowset. If not
** an assertion fault occurs.
**
** If N is larger than the minimum, use the surplus as an initial
** allocation of entries available to be filled.
** Allocate a RowSet object. Return NULL if a memory allocation
** error occurs.
*/
RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
RowSet *p;
assert( N >= ROUND8(sizeof(*p)) );
p = pSpace;
p->pChunk = 0;
p->db = db;
p->pEntry = 0;
p->pLast = 0;
p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
p->rsFlags = ROWSET_SORTED;
p->iBatch = 0;
RowSet *sqlite3RowSetInit(sqlite3 *db){
RowSet *p = sqlite3DbMallocRawNN(db, sizeof(*p));
if( p ){
int N = sqlite3DbMallocSize(db, p);
p->pChunk = 0;
p->db = db;
p->pEntry = 0;
p->pLast = 0;
p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
p->rsFlags = ROWSET_SORTED;
p->iBatch = 0;
}
return p;
}

Expand All @@ -156,7 +149,8 @@ RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
** the RowSet has allocated over its lifetime. This routine is
** the destructor for the RowSet.
*/
void sqlite3RowSetClear(RowSet *p){
void sqlite3RowSetClear(void *pArg){
RowSet *p = (RowSet*)pArg;
struct RowSetChunk *pChunk, *pNextChunk;
for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){
pNextChunk = pChunk->pNextChunk;
Expand All @@ -170,6 +164,16 @@ void sqlite3RowSetClear(RowSet *p){
p->rsFlags = ROWSET_SORTED;
}

/*
** Deallocate all chunks from a RowSet. This frees all memory that
** the RowSet has allocated over its lifetime. This routine is
** the destructor for the RowSet.
*/
void sqlite3RowSetDelete(void *pArg){
sqlite3RowSetClear(pArg);
sqlite3DbFree(((RowSet*)pArg)->db, pArg);
}

/*
** Allocate a new RowSetEntry object that is associated with the
** given RowSet. Return a pointer to the new and completely uninitialized
Expand Down
5 changes: 3 additions & 2 deletions src/sqliteInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -3825,8 +3825,9 @@ u32 sqlite3BitvecSize(Bitvec*);
int sqlite3BitvecBuiltinTest(int,int*);
#endif

RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
void sqlite3RowSetClear(RowSet*);
RowSet *sqlite3RowSetInit(sqlite3*);
void sqlite3RowSetDelete(void*);
void sqlite3RowSetClear(void*);
void sqlite3RowSetInsert(RowSet*, i64);
int sqlite3RowSetTest(RowSet*, int iBatch, i64);
int sqlite3RowSetNext(RowSet*, i64*);
Expand Down
26 changes: 13 additions & 13 deletions src/vdbe.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ static void memTracePrint(Mem *p){
}else if( p->flags & MEM_Real ){
printf(" r:%g", p->u.r);
#endif
}else if( p->flags & MEM_RowSet ){
}else if( sqlite3VdbeMemIsRowSet(p) ){
printf(" (rowset)");
}else{
char zBuf[200];
Expand Down Expand Up @@ -5903,11 +5903,11 @@ case OP_RowSetAdd: { /* in1, in2 */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
assert( (pIn2->flags & MEM_Int)!=0 );
if( (pIn1->flags & MEM_RowSet)==0 ){
sqlite3VdbeMemSetRowSet(pIn1);
if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
if( (pIn1->flags & MEM_Blob)==0 ){
if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
}
sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i);
assert( sqlite3VdbeMemIsRowSet(pIn1) );
sqlite3RowSetInsert((RowSet*)pIn1->z, pIn2->u.i);
break;
}

Expand All @@ -5923,8 +5923,9 @@ case OP_RowSetRead: { /* jump, in1, out3 */
i64 val;

pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_RowSet)==0
|| sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0
assert( (pIn1->flags & MEM_Blob)==0 || sqlite3VdbeMemIsRowSet(pIn1) );
if( (pIn1->flags & MEM_Blob)==0
|| sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0
){
/* The boolean index is empty */
sqlite3VdbeMemSetNull(pIn1);
Expand Down Expand Up @@ -5973,20 +5974,19 @@ case OP_RowSetTest: { /* jump, in1, in3 */
/* If there is anything other than a rowset object in memory cell P1,
** delete it now and initialize P1 with an empty rowset
*/
if( (pIn1->flags & MEM_RowSet)==0 ){
sqlite3VdbeMemSetRowSet(pIn1);
if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem;
if( (pIn1->flags & MEM_Blob)==0 ){
if( sqlite3VdbeMemSetRowSet(pIn1) ) goto no_mem;
}

assert( sqlite3VdbeMemIsRowSet(pIn1) );
assert( pOp->p4type==P4_INT32 );
assert( iSet==-1 || iSet>=0 );
if( iSet ){
exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i);
exists = sqlite3RowSetTest((RowSet*)pIn1->z, iSet, pIn3->u.i);
VdbeBranchTaken(exists!=0,2);
if( exists ) goto jump_to_p2;
}
if( iSet>=0 ){
sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
sqlite3RowSetInsert((RowSet*)pIn1->z, pIn3->u.i);
}
break;
}
Expand Down
10 changes: 6 additions & 4 deletions src/vdbeInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,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 */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
} u;
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
Expand Down Expand Up @@ -247,7 +246,7 @@ struct sqlite3_value {
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_AffMask 0x001f /* Mask of affinity bits */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
/* Available 0x0020 */
/* Available 0x0040 */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
Expand Down Expand Up @@ -275,7 +274,7 @@ struct sqlite3_value {
** that needs to be deallocated to avoid a leak.
*/
#define VdbeMemDynamic(X) \
(((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet))!=0)
(((X)->flags&(MEM_Agg|MEM_Dyn))!=0)

/*
** Clear any existing type flags from a Mem and replace them with f
Expand Down Expand Up @@ -488,7 +487,10 @@ void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*));
void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
void sqlite3VdbeMemSetNull(Mem*);
void sqlite3VdbeMemSetZeroBlob(Mem*,int);
void sqlite3VdbeMemSetRowSet(Mem*);
#ifdef SQLITE_DEBUG
int sqlite3VdbeMemIsRowSet(const Mem*);
#endif
int sqlite3VdbeMemSetRowSet(Mem*);
int sqlite3VdbeMemMakeWriteable(Mem*);
int sqlite3VdbeMemStringify(Mem*, u8, u8);
i64 sqlite3VdbeIntValue(Mem*);
Expand Down
4 changes: 2 additions & 2 deletions src/vdbeaux.c
Original file line number Diff line number Diff line change
Expand Up @@ -1663,7 +1663,7 @@ static void releaseMemArray(Mem *p, int N){
testcase( p->flags & MEM_Dyn );
testcase( p->xDel==sqlite3VdbeFrameMemDel );
testcase( p->flags & MEM_RowSet );
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){
if( p->flags&(MEM_Agg|MEM_Dyn) ){
sqlite3VdbeMemRelease(p);
}else if( p->szMalloc ){
sqlite3DbFreeNN(db, p->zMalloc);
Expand Down Expand Up @@ -3991,7 +3991,7 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
f1 = pMem1->flags;
f2 = pMem2->flags;
combined_flags = f1|f2;
assert( (combined_flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem1) && !sqlite3VdbeMemIsRowSet(pMem2) );

/* If one value is NULL, it is less than the other. If both values
** are NULL, return 0.
Expand Down
68 changes: 37 additions & 31 deletions src/vdbemem.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,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_Agg))==0 );
assert( (p->flags & (MEM_Int|MEM_Real|MEM_Str|MEM_Blob|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
Expand Down Expand Up @@ -156,7 +155,7 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
#ifndef SQLITE_OMIT_UTF16
int rc;
#endif
assert( (pMem->flags&MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
Expand Down Expand Up @@ -189,7 +188,7 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
*/
SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
testcase( pMem->db==0 );

/* If the bPreserve flag is set to true, then the memory cell must already
Expand Down Expand Up @@ -277,7 +276,7 @@ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
*/
int sqlite3VdbeMemMakeWriteable(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags&MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
Expand All @@ -302,7 +301,7 @@ int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
assert( pMem->flags & MEM_Zero );
assert( pMem->flags&MEM_Blob );
assert( (pMem->flags&MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );

/* Set nByte to the number of bytes required to store the expanded blob. */
Expand Down Expand Up @@ -357,7 +356,7 @@ int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
assert( !(fg&MEM_Zero) );
assert( !(fg&(MEM_Str|MEM_Blob)) );
assert( fg&(MEM_Int|MEM_Real) );
assert( (pMem->flags&MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );


Expand Down Expand Up @@ -462,11 +461,8 @@ static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){
testcase( p->flags & MEM_Dyn );
}
if( p->flags&MEM_Dyn ){
assert( (p->flags&MEM_RowSet)==0 );
assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
p->xDel((void *)p->z);
}else if( p->flags&MEM_RowSet ){
sqlite3RowSetClear(p->u.pRowSet);
}
p->flags = MEM_Null;
}
Expand Down Expand Up @@ -614,7 +610,7 @@ int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
void sqlite3VdbeIntegerAffinity(Mem *pMem){
i64 ix;
assert( pMem->flags & MEM_Real );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );

Expand All @@ -641,7 +637,7 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
*/
int sqlite3VdbeMemIntegerify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );

pMem->u.i = sqlite3VdbeIntValue(pMem);
Expand Down Expand Up @@ -859,26 +855,36 @@ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
}
#endif

#ifdef SQLITE_DEBUG
/*
** Return true if the Mem holds a RowSet object. This routine is intended
** for use inside of assert() statements.
*/
int sqlite3VdbeMemIsRowSet(const Mem *pMem){
return (pMem->flags&(MEM_Blob|MEM_Dyn))==(MEM_Blob|MEM_Dyn)
&& pMem->xDel==sqlite3RowSetDelete;
}
#endif

/*
** Delete any previous value and set the value of pMem to be an
** empty boolean index.
**
** Return SQLITE_OK on success and SQLITE_NOMEM if a memory allocation
** error occurs.
*/
void sqlite3VdbeMemSetRowSet(Mem *pMem){
int sqlite3VdbeMemSetRowSet(Mem *pMem){
sqlite3 *db = pMem->db;
RowSet *p;
assert( db!=0 );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
sqlite3VdbeMemRelease(pMem);
pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
if( db->mallocFailed ){
pMem->flags = MEM_Null;
pMem->szMalloc = 0;
}else{
assert( pMem->zMalloc );
pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
assert( pMem->u.pRowSet!=0 );
pMem->flags = MEM_RowSet;
}
p = sqlite3RowSetInit(db);
if( p==0 ) return SQLITE_NOMEM;
pMem->z = (char*)p;
pMem->flags = MEM_Blob|MEM_Dyn;
pMem->xDel = sqlite3RowSetDelete;
return SQLITE_OK;
}

/*
Expand Down Expand Up @@ -946,7 +952,7 @@ static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){
sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
}
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pFrom) );
assert( pTo->db==pFrom->db );
if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
memcpy(pTo, pFrom, MEMCELLSIZE);
Expand All @@ -964,7 +970,7 @@ void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;

assert( (pFrom->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pFrom) );
if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
Expand Down Expand Up @@ -1022,7 +1028,7 @@ int sqlite3VdbeMemSetStr(
u16 flags = 0; /* New value for pMem->flags */

assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );

/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
Expand Down Expand Up @@ -1144,7 +1150,7 @@ int sqlite3VdbeMemFromBtree(

/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
** that both the BtShared and database handle mutexes are held. */
assert( (pMem->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
assert( zData!=0 );

Expand All @@ -1168,7 +1174,7 @@ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
assert( pVal!=0 );
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
assert( (pVal->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pVal) );
assert( (pVal->flags & (MEM_Null))==0 );
if( pVal->flags & (MEM_Blob|MEM_Str) ){
if( ExpandBlob(pVal) ) return 0;
Expand Down Expand Up @@ -1211,7 +1217,7 @@ const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
if( !pVal ) return 0;
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
assert( (pVal->flags & MEM_RowSet)==0 );
assert( !sqlite3VdbeMemIsRowSet(pVal) );
if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
assert( sqlite3VdbeMemConsistentDualRep(pVal) );
return pVal->z;
Expand Down

0 comments on commit 304407e

Please sign in to comment.