mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-22 20:22:44 +03:00
Code generator tries to avoid pointless OP_IsNull and OP_Affinity opcodes.
FossilOrigin-Name: ebb0c8a3e977dc741704e733b5a5d931d9b27028
This commit is contained in:
65
src/expr.c
65
src/expr.c
@@ -1258,6 +1258,71 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
|
||||
@@ -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*);
|
||||
|
||||
20
src/vdbe.c
20
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;
|
||||
|
||||
92
src/where.c
92
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++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user