1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-24 08:21:29 +03:00

Add the OP_Undef and OP_IsUndef opcodes. With these, use the first register

in the result register range as the flag to indicate EOF on an INSERT from
a SELECT, rather than allocating a separate boolean register for that task.

FossilOrigin-Name: 6fb7448550f28a3c93053e125faeaf11de1011d0
This commit is contained in:
drh
2014-02-07 13:20:31 +00:00
parent 21a919f630
commit a5750cfe01
7 changed files with 65 additions and 53 deletions

View File

@@ -349,17 +349,13 @@ void sqlite3AutoincrementEnd(Parse *pParse){
** co-routine. Run the co-routine to its next breakpoint
** by calling "OP_Yield $X" where $X is pDest->iSDParm.
**
** pDest->iSDParm+1 The register holding the "completed" flag for the
** co-routine. This register is 0 if the previous Yield
** generated a new result row, or 1 if the subquery
** has completed. If the Yield is called again
** after this register becomes 1, then the VDBE will
** halt with an SQLITE_INTERNAL error.
**
** pDest->iSdst First result register.
**
** pDest->nSdst Number of result registers.
**
** At EOF the first result register will be marked as "undefined" so that
** the caller can know when to stop reading results.
**
** This routine handles all of the register allocation and fills in the
** pDest structure appropriately.
**
@@ -370,7 +366,6 @@ void sqlite3AutoincrementEnd(Parse *pParse){
** reg[pDest->iSdst+pDest->nSdst-1]:
**
** X <- A
** EOF <- 0
** goto B
** A: setup for the SELECT
** loop rows in the SELECT
@@ -378,7 +373,7 @@ void sqlite3AutoincrementEnd(Parse *pParse){
** yield X
** end loop
** cleanup after the SELECT
** EOF <- 1
** R <- undefined (signals EOF)
** yield X
** halt-error
** B:
@@ -387,7 +382,7 @@ void sqlite3AutoincrementEnd(Parse *pParse){
**
** [ Co-routine generated by this subroutine, shown above ]
** S: yield X
** if EOF goto E
** if R==undefined goto E
** if skip this row, goto C
** if terminate loop, goto E
** deal with this row
@@ -396,28 +391,24 @@ void sqlite3AutoincrementEnd(Parse *pParse){
*/
int sqlite3CodeCoroutine(Parse *pParse, Select *pSelect, SelectDest *pDest){
int regYield; /* Register holding co-routine entry-point */
int regEof; /* Register holding co-routine completion flag */
int addrTop; /* Top of the co-routine */
int j1; /* Jump instruction */
int rc; /* Result code */
Vdbe *v; /* VDBE under construction */
regYield = ++pParse->nMem;
regEof = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
addrTop = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_Integer, addrTop+2, regYield); /* X <- A */
sqlite3VdbeAddOp2(v, OP_Integer, addrTop+1, regYield); /* X <- A */
VdbeComment((v, "Co-routine entry point"));
sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */
VdbeComment((v, "Co-routine completion flag"));
sqlite3SelectDestInit(pDest, SRT_Coroutine, regYield);
j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
rc = sqlite3Select(pParse, pSelect, pDest);
assert( pParse->nErr==0 || rc );
if( pParse->db->mallocFailed && rc==SQLITE_OK ) rc = SQLITE_NOMEM;
if( rc ) return rc;
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
sqlite3VdbeAddOp1(v, OP_Yield, regYield); /* yield X */
sqlite3VdbeAddOp1(v, OP_Undef, pDest->iSdst); /* Signal EOF */
sqlite3VdbeAddOp1(v, OP_Yield, regYield); /* yield X */
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort);
VdbeComment((v, "End of coroutine"));
sqlite3VdbeJumpHere(v, j1); /* label B: */
@@ -488,7 +479,6 @@ static int xferOptimization(
** and the SELECT clause does not read from <table> at any time.
** The generated code follows this template:
**
** EOF <- 0
** X <- A
** goto B
** A: setup for the SELECT
@@ -497,12 +487,12 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
** EOF <- 1
** R <- undefined (signals EOF)
** yield X
** goto A
** B: open write cursor to <table> and its indices
** C: yield X
** if EOF goto D
** if R=undefined goto D
** insert the select result into <table> from R..R+n
** goto C
** D: cleanup
@@ -513,7 +503,6 @@ static int xferOptimization(
** we have to use a intermediate table to store the results of
** the select. The template is like this:
**
** EOF <- 0
** X <- A
** goto B
** A: setup for the SELECT
@@ -522,12 +511,12 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
** EOF <- 1
** R <- undefined (signals EOF)
** yield X
** halt-error
** B: open temp table
** L: yield X
** if EOF goto M
** if R=undefined goto M
** insert row from R..R+n into temp table
** goto L
** M: open write cursor to <table> and its indices
@@ -576,7 +565,6 @@ void sqlite3Insert(
int regIns; /* Block of regs holding rowid+data being inserted */
int regRowid; /* registers holding insert rowid */
int regData; /* register holding first column to insert */
int regEof = 0; /* Register recording end of SELECT data */
int *aRegIdx = 0; /* One register allocated to each index */
#ifndef SQLITE_OMIT_TRIGGER
@@ -689,7 +677,6 @@ void sqlite3Insert(
int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest);
if( rc ) goto insert_cleanup;
regEof = dest.iSDParm + 1;
regFromSelect = dest.iSdst;
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
@@ -715,7 +702,7 @@ void sqlite3Insert(
**
** B: open temp table
** L: yield X
** if EOF goto M
** if R=undefined goto M
** insert row from R..R+n into temp table
** goto L
** M: ...
@@ -730,7 +717,7 @@ void sqlite3Insert(
regTempRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
addrIf = sqlite3VdbeAddOp1(v, OP_IsUndef, regFromSelect);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid);
@@ -860,13 +847,13 @@ void sqlite3Insert(
** following pseudocode (template 3):
**
** C: yield X
** if EOF goto D
** if R=undefined goto D
** insert the select result into <table> from R..R+n
** goto C
** D: ...
*/
addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof);
addrInsTop = sqlite3VdbeAddOp1(v, OP_IsUndef, dest.iSdst);
}
/* Allocate registers for holding the rowid of the new row,

View File

@@ -393,7 +393,7 @@ void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
** Print the value of a register for tracing purposes:
*/
static void memTracePrint(Mem *p){
if( p->flags & MEM_Invalid ){
if( p->flags & MEM_Undefined ){
printf(" undefined");
}else if( p->flags & MEM_Null ){
printf(" NULL");
@@ -702,7 +702,6 @@ check_for_interrupt:
}
/* Opcode: Gosub P1 P2 * * *
** Synopsis: r[P1]=pc; pc=P2
**
** Write the current address onto register P1
** and then jump to address P2.
@@ -720,7 +719,6 @@ case OP_Gosub: { /* jump */
}
/* Opcode: Return P1 * * * *
** Synopsis: pc=r[P1]+1
**
** Jump to the next instruction after the address in register P1.
*/
@@ -732,7 +730,6 @@ case OP_Return: { /* in1 */
}
/* Opcode: Yield P1 * * * *
** Synopsis: swap(pc,r[P1])
**
** Swap the program counter with the value in register P1.
*/
@@ -974,7 +971,7 @@ case OP_Null: { /* out2-prerelease */
}
/* Opcode: Blob P1 P2 * P4
/* Opcode: Blob P1 P2 * P4 *
** Synopsis: r[P2]=P4 (len=P1)
**
** P4 points to a blob of data P1 bytes long. Store this
@@ -988,6 +985,21 @@ case OP_Blob: { /* out2-prerelease */
break;
}
/* Opcode: Undef P1 * * * *
** Synopsis: r[P1]=undef
**
** Mark register P1 as undefined.
*/
case OP_Undef: {
assert( pOp->p1>0 );
assert( pOp->p1<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p1];
memAboutToChange(p, pOut);
VdbeMemRelease(pOut);
pOut->flags = MEM_Undefined;
break;
}
/* Opcode: Variable P1 P2 * P4 *
** Synopsis: r[P2]=parameter(P1,P4)
**
@@ -2142,6 +2154,19 @@ case OP_IfNot: { /* jump, in1 */
break;
}
/* Opcode: IsUndef P1 P2 * * *
** Synopsis: if r[P1]==undefined goto P2
**
** Jump to P2 if the value in register P1 is undefined
*/
case OP_IsUndef: { /* jump */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Undefined)!=0 ){
pc = pOp->p2 - 1;
}
break;
}
/* Opcode: IsNull P1 P2 * * *
** Synopsis: if r[P1]==NULL goto P2
**
@@ -5191,7 +5216,7 @@ case OP_Program: { /* jump */
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
pMem->flags = MEM_Invalid;
pMem->flags = MEM_Undefined;
pMem->db = db;
}
}else{

View File

@@ -198,7 +198,7 @@ struct Mem {
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
#define MEM_Invalid 0x0080 /* Value is undefined */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
#define MEM_TypeMask 0x01ff /* Mask of type bits */
@@ -230,7 +230,7 @@ struct Mem {
** is for use inside assert() statements only.
*/
#ifdef SQLITE_DEBUG
#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
#define memIsValid(M) ((M)->flags & MEM_Undefined)==0
#endif
/*

View File

@@ -1234,7 +1234,7 @@ static void releaseMemArray(Mem *p, int N){
p->zMalloc = 0;
}
p->flags = MEM_Invalid;
p->flags = MEM_Undefined;
}
db->mallocFailed = malloc_failed;
}
@@ -1702,7 +1702,7 @@ void sqlite3VdbeMakeReady(
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){
p->aMem[n].flags = MEM_Invalid;
p->aMem[n].flags = MEM_Undefined;
p->aMem[n].db = db;
}
}
@@ -1814,7 +1814,7 @@ static void Cleanup(Vdbe *p){
int i;
if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
if( p->aMem ){
for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
}
#endif

View File

@@ -587,7 +587,7 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
Mem *pX;
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
pX->flags |= MEM_Invalid;
pX->flags |= MEM_Undefined;
pX->pScopyFrom = 0;
}
}