aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2009-11-17 18:31:47 +0000
committerdrh <drh@noemail.net>2009-11-17 18:31:47 +0000
commit039fc32e8488cba77d12d1fc5038ce6cfd7dcf40 (patch)
treea3db12f7a3872f1338926b141355ecaea39f91cc /src
parent68ff78b1b04bad32f67d5d0bb12c4893d8aedefb (diff)
downloadsqlite-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.c65
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/vdbe.c20
-rw-r--r--src/where.c92
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++;
}