diff options
author | drh <drh@noemail.net> | 2009-11-17 18:31:47 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2009-11-17 18:31:47 +0000 |
commit | 039fc32e8488cba77d12d1fc5038ce6cfd7dcf40 (patch) | |
tree | a3db12f7a3872f1338926b141355ecaea39f91cc /src | |
parent | 68ff78b1b04bad32f67d5d0bb12c4893d8aedefb (diff) | |
download | sqlite-039fc32e8488cba77d12d1fc5038ce6cfd7dcf40.tar.gz sqlite-039fc32e8488cba77d12d1fc5038ce6cfd7dcf40.zip |
Code generator tries to avoid pointless OP_IsNull and OP_Affinity opcodes.
FossilOrigin-Name: ebb0c8a3e977dc741704e733b5a5d931d9b27028
Diffstat (limited to 'src')
-rw-r--r-- | src/expr.c | 65 | ||||
-rw-r--r-- | src/sqliteInt.h | 2 | ||||
-rw-r--r-- | src/vdbe.c | 20 | ||||
-rw-r--r-- | src/where.c | 92 |
4 files changed, 143 insertions, 36 deletions
diff --git a/src/expr.c b/src/expr.c index a5e5edd32..db74e8759 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1259,6 +1259,71 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){ } /* +** Return FALSE if there is no chance that the expression can be NULL. +** +** If the expression might be NULL or if the expression is too complex +** to tell return TRUE. +** +** This routine is used as an optimization, to skip OP_IsNull opcodes +** when we know that a value cannot be NULL. Hence, a false positive +** (returning TRUE when in fact the expression can never be NULL) might +** be a small performance hit but is otherwise harmless. On the other +** hand, a false negative (returning FALSE when the result could be NULL) +** will likely result in an incorrect answer. So when in doubt, return +** TRUE. +*/ +int sqlite3ExprCanBeNull(const Expr *p){ + u8 op; + while( p->op==TK_UPLUS || p->op==TK_MINUS ){ p = p->pLeft; } + op = p->op; + if( op==TK_REGISTER ) op = p->op2; + switch( op ){ + case TK_INTEGER: + case TK_STRING: + case TK_FLOAT: + case TK_BLOB: + return 0; + default: + return 1; + } +} + +/* +** Return TRUE if the given expression is a constant which would be +** unchanged by OP_Affinity with the affinity given in the second +** argument. +** +** This routine is used to determine if the OP_Affinity operation +** can be omitted. When in doubt return FALSE. A false negative +** is harmless. A false positive, however, can result in the wrong +** answer. +*/ +int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){ + u8 op; + if( aff==SQLITE_AFF_NONE ) return 1; + while( p->op==TK_UPLUS || p->op==TK_MINUS ){ p = p->pLeft; } + op = p->op; + if( op==TK_REGISTER ) op = p->op2; + switch( op ){ + case TK_INTEGER: { + return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC; + } + case TK_FLOAT: { + return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC; + } + case TK_STRING: { + return aff==SQLITE_AFF_TEXT; + } + case TK_BLOB: { + return 1; + } + default: { + return 0; + } + } +} + +/* ** Return TRUE if the given string is a row-id column name. */ int sqlite3IsRowid(const char *z){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f98baf1aa..64c7c356f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2674,6 +2674,8 @@ int sqlite3ExprIsConstant(Expr*); int sqlite3ExprIsConstantNotJoin(Expr*); int sqlite3ExprIsConstantOrFunction(Expr*); int sqlite3ExprIsInteger(Expr*, int*); +int sqlite3ExprCanBeNull(const Expr*); +int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); int sqlite3IsRowid(const char*); void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); diff --git a/src/vdbe.c b/src/vdbe.c index 99a29c46d..4b5c6a1fa 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2292,17 +2292,18 @@ op_column_out: ** memory cell in the range. */ case OP_Affinity: { - char *zAffinity; /* The affinity to be applied */ - Mem *pData0; /* First register to which to apply affinity */ - Mem *pLast; /* Last register to which to apply affinity */ - Mem *pRec; /* Current register */ + const char *zAffinity; /* The affinity to be applied */ + char cAff; /* A single character of affinity */ zAffinity = pOp->p4.z; - pData0 = &aMem[pOp->p1]; - pLast = &pData0[pOp->p2-1]; - for(pRec=pData0; pRec<=pLast; pRec++){ - ExpandBlob(pRec); - applyAffinity(pRec, zAffinity[pRec-pData0], encoding); + assert( zAffinity!=0 ); + assert( zAffinity[pOp->p2]==0 ); + pIn1 = &aMem[pOp->p1]; + while( (cAff = *(zAffinity++))!=0 ){ + assert( pIn1 <= &p->aMem[p->nMem] ); + ExpandBlob(pIn1); + applyAffinity(pIn1, cAff, encoding); + pIn1++; } break; } @@ -3255,6 +3256,7 @@ case OP_SeekGt: { /* jump, in3 */ assert( oc!=OP_SeekLt || r.flags==0 ); r.aMem = &aMem[pOp->p3]; + ExpandBlob(r.aMem); rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; diff --git a/src/where.c b/src/where.c index 84cbd4636..7abf7d582 100644 --- a/src/where.c +++ b/src/where.c @@ -2594,14 +2594,39 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ ** Code an OP_Affinity opcode to apply the column affinity string zAff ** to the n registers starting at base. ** -** This routine assumes that zAff is dynamic and makes its own copy. +** As an optimization, SQLITE_AFF_NONE entries (which are no-ops) at the +** beginning and end of zAff are ignored. If all entries in zAff are +** SQLITE_AFF_NONE, then no code gets generated. +** +** This routine makes its own copy of zAff so that the caller is free +** to modify zAff after this routine returns. */ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ Vdbe *v = pParse->pVdbe; + if( zAff==0 ){ + assert( pParse->db->mallocFailed ); + return; + } assert( v!=0 ); - sqlite3VdbeAddOp2(v, OP_Affinity, base, n); - sqlite3VdbeChangeP4(v, -1, zAff, 0); - sqlite3ExprCacheAffinityChange(pParse, base, n); + + /* Adjust base and n to skip over SQLITE_AFF_NONE entries at the beginning + ** and end of the affinity string. + */ + while( n>0 && zAff[0]==SQLITE_AFF_NONE ){ + n--; + base++; + zAff++; + } + while( n>1 && zAff[n-1]==SQLITE_AFF_NONE ){ + n--; + } + + /* Code the OP_Affinity opcode if there is anything left to do. */ + if( n>0 ){ + sqlite3VdbeAddOp2(v, OP_Affinity, base, n); + sqlite3VdbeChangeP4(v, -1, zAff, n); + sqlite3ExprCacheAffinityChange(pParse, base, n); + } } @@ -2672,7 +2697,7 @@ static int codeEqualityTerm( /* ** Generate code that will evaluate all == and IN constraints for an -** index. The values for all constraints are left on the stack. +** index. ** ** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c). ** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10 @@ -2684,7 +2709,8 @@ static int codeEqualityTerm( ** ** In the example above nEq==2. But this subroutine works for any value ** of nEq including 0. If nEq==0, this routine is nearly a no-op. -** The only thing it does is allocate the pLevel->iMem memory cell. +** The only thing it does is allocate the pLevel->iMem memory cell and +** compute the affinity string. ** ** This routine always allocates at least one memory cell and returns ** the index of that memory cell. The code that @@ -2762,11 +2788,17 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); - if( zAff - && sqlite3CompareAffinity(pTerm->pExpr->pRight, zAff[j])==SQLITE_AFF_NONE - ){ - zAff[j] = SQLITE_AFF_NONE; + Expr *pRight = pTerm->pExpr->pRight; + if( sqlite3ExprCanBeNull(pRight) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + } + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){ + zAff[j] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ + zAff[j] = SQLITE_AFF_NONE; + } } } } @@ -3093,14 +3125,17 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); - if( zAff - && sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE - ){ - /* Since the comparison is to be performed with no conversions applied - ** to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zAff[nConstraint] = SQLITE_AFF_NONE; - } + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE){ + /* Since the comparison is to be performed with no conversions + ** applied to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[nConstraint]) ){ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + } nConstraint++; }else if( isMinQuery ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); @@ -3128,14 +3163,17 @@ static Bitmask codeOneLoopStart( sqlite3ExprCacheRemove(pParse, regBase+nEq); sqlite3ExprCode(pParse, pRight, regBase+nEq); sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); - if( zAff - && sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE - ){ - /* Since the comparison is to be performed with no conversions applied - ** to the operands, set the affinity to apply to pRight to - ** SQLITE_AFF_NONE. */ - zAff[nConstraint] = SQLITE_AFF_NONE; - } + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE){ + /* Since the comparison is to be performed with no conversions + ** applied to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[nConstraint]) ){ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + } codeApplyAffinity(pParse, regBase, nEq+1, zAff); nConstraint++; } |