Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improvements to the constant-propagation optimization in order to better deal with unusual affinities. See forum post 6a06202608 for more detail. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
9be208a6d70582c6808abe6e016ab9cd |
User & Date: | drh 2021-05-26 23:10:19 |
Context
2021-05-27
| ||
10:23 | Remove a NEVER that is reachable following OOM in writable_schema=ON mode with a corrupt schema. (check-in: 4eb80b0c user: drh tags: trunk) | |
2021-05-26
| ||
23:10 | Improvements to the constant-propagation optimization in order to better deal with unusual affinities. See forum post 6a06202608 for more detail. (check-in: 9be208a6 user: drh tags: trunk) | |
19:52 | Minor optimization to opcode comparison logic in the fixes to the constant-propagation optimization on this branch. (Closed-Leaf check-in: f17dec40 user: drh tags: constant-propagation-fix) | |
18:46 | Take care that the code is not generated for the same Select object more than once, as transformations that apply during the first pass might cause problems for the second pass. dbsqlfuzz 836b625cd8a41809ef80fc7ebaa6554357bcb463. (check-in: f30fb19f user: drh tags: trunk) | |
Changes
Changes to src/select.c.
︙ | ︙ | |||
4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 | ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. */ typedef struct WhereConst WhereConst; struct WhereConst { Parse *pParse; /* Parsing context */ int nConst; /* Number for COLUMN=CONSTANT terms */ int nChng; /* Number of times a constant is propagated */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; /* ** Add a new entry to the pConst object. Except, do not add duplicate ** pColumn entires. Also, do not add if doing so would not be appropriate. ** | > | 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 | ** a known value due to WHERE clause constraints of the form COLUMN=VALUE. */ typedef struct WhereConst WhereConst; struct WhereConst { Parse *pParse; /* Parsing context */ int nConst; /* Number for COLUMN=CONSTANT terms */ int nChng; /* Number of times a constant is propagated */ int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */ Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */ }; /* ** Add a new entry to the pConst object. Except, do not add duplicate ** pColumn entires. Also, do not add if doing so would not be appropriate. ** |
︙ | ︙ | |||
4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 | const Expr *pE2 = pConst->apExpr[i*2]; assert( pE2->op==TK_COLUMN ); if( pE2->iTable==pColumn->iTable && pE2->iColumn==pColumn->iColumn ){ return; /* Already present. Return without doing anything. */ } } pConst->nConst++; pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, pConst->nConst*2*sizeof(Expr*)); if( pConst->apExpr==0 ){ pConst->nConst = 0; | > > > | 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 | const Expr *pE2 = pConst->apExpr[i*2]; assert( pE2->op==TK_COLUMN ); if( pE2->iTable==pColumn->iTable && pE2->iColumn==pColumn->iColumn ){ return; /* Already present. Return without doing anything. */ } } if( sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ pConst->bHasAffBlob = 1; } pConst->nConst++; pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr, pConst->nConst*2*sizeof(Expr*)); if( pConst->apExpr==0 ){ pConst->nConst = 0; |
︙ | ︙ | |||
4492 4493 4494 4495 4496 4497 4498 | } if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ constInsert(pConst,pLeft,pRight,pExpr); } } /* | | > | | | > > | > > > > < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 | } if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ constInsert(pConst,pLeft,pRight,pExpr); } } /* ** This is a helper function for Walker callback propagateConstantExprRewrite(). ** ** Argument pExpr is a candidate expression to be replaced by a value. If ** pExpr is equivalent to one of the columns named in pWalker->u.pConst, ** then overwrite it with the corresponding value. Except, do not do so ** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr ** is SQLITE_AFF_BLOB. */ static int propagateConstantExprRewriteOne( WhereConst *pConst, Expr *pExpr, int bIgnoreAffBlob ){ int i; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){ testcase( ExprHasProperty(pExpr, EP_FixedCol) ); testcase( ExprHasProperty(pExpr, EP_FromJoin) ); return WRC_Continue; } for(i=0; i<pConst->nConst; i++){ Expr *pColumn = pConst->apExpr[i*2]; if( pColumn==pExpr ) continue; if( pColumn->iTable!=pExpr->iTable ) continue; if( pColumn->iColumn!=pExpr->iColumn ) continue; if( bIgnoreAffBlob && sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){ break; } /* A match is found. Add the EP_FixedCol property */ pConst->nChng++; ExprClearProperty(pExpr, EP_Leaf); ExprSetProperty(pExpr, EP_FixedCol); assert( pExpr->pLeft==0 ); pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0); break; } return WRC_Prune; } /* ** This is a Walker expression callback. pExpr is a node from the WHERE ** clause of a SELECT statement. This function examines pExpr to see if ** any substitutions based on the contents of pWalker->u.pConst should ** be made to pExpr or its immediate children. ** ** A substitution is made if: ** ** + pExpr is a column with an affinity other than BLOB that matches ** one of the columns in pWalker->u.pConst, or ** ** + pExpr is a binary comparison operator (=, <=, >=, <, >) that ** uses an affinity other than TEXT and one of its immediate ** children is a column that matches one of the columns in ** pWalker->u.pConst. */ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){ WhereConst *pConst = pWalker->u.pConst; assert( TK_GT==TK_EQ+1 ); assert( TK_LE==TK_EQ+2 ); assert( TK_LT==TK_EQ+3 ); assert( TK_GE==TK_EQ+4 ); if( pConst->bHasAffBlob ){ if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE) || pExpr->op==TK_IS ){ propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0); if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){ propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0); } } } return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob); } /* ** The WHERE-clause constant propagation optimization. ** ** If the WHERE clause contains terms of the form COLUMN=CONSTANT or ** CONSTANT=COLUMN that are top-level AND-connected terms that are not ** part of a ON clause from a LEFT JOIN, then throughout the query |
︙ | ︙ | |||
4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 | Walker w; int nChng = 0; x.pParse = pParse; do{ x.nConst = 0; x.nChng = 0; x.apExpr = 0; findConstInWhere(&x, p->pWhere); if( x.nConst ){ memset(&w, 0, sizeof(w)); w.pParse = pParse; w.xExprCallback = propagateConstantExprRewrite; w.xSelectCallback = sqlite3SelectWalkNoop; w.xSelectCallback2 = 0; | > | 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 | Walker w; int nChng = 0; x.pParse = pParse; do{ x.nConst = 0; x.nChng = 0; x.apExpr = 0; x.bHasAffBlob = 0; findConstInWhere(&x, p->pWhere); if( x.nConst ){ memset(&w, 0, sizeof(w)); w.pParse = pParse; w.xExprCallback = propagateConstantExprRewrite; w.xSelectCallback = sqlite3SelectWalkNoop; w.xSelectCallback2 = 0; |
︙ | ︙ |
Added test/whereM.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | # 2021 May 27 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # Tests focused on the "constant propagation" that occurs within the # WHERE clause of a SELECT statemente. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set testprefix whereM do_execsql_test 1.0 { CREATE TABLE t1(a, b INTEGER, c TEXT, d REAL, e BLOB); INSERT INTO t1 VALUES(10.0, 10.0, 10.0, 10.0, 10.0); SELECT * FROM t1; } { 10.0 10 10.0 10.0 10.0 } do_execsql_test 1.1.1 { SELECT a=10, a = '10.0', a LIKE '10.0' FROM t1; } {1 0 1} do_execsql_test 1.1.2 { SELECT count(*) FROM t1 WHERE a=10 AND a = '10.0' } {0} do_execsql_test 1.1.3 { SELECT count(*) FROM t1 WHERE a=10 AND a LIKE '10.0' } {1} do_execsql_test 1.1.4 { SELECT count(*) FROM t1 WHERE a='10.0' AND a LIKE '10.0' } {0} do_execsql_test 1.2.1 { SELECT b=10, b = '10.0', b LIKE '10.0', b LIKE '10' FROM t1; } {1 1 0 1} do_execsql_test 1.2.2 { SELECT count(*) FROM t1 WHERE b=10 AND b = '10.0' } {1} do_execsql_test 1.2.3 { SELECT count(*) FROM t1 WHERE b=10 AND b LIKE '10.0' } {0} do_execsql_test 1.2.4 { SELECT count(*) FROM t1 WHERE b='10.0' AND b LIKE '10.0' } {0} do_execsql_test 1.2.3 { SELECT count(*) FROM t1 WHERE b=10 AND b LIKE '10' } {1} do_execsql_test 1.2.4 { SELECT count(*) FROM t1 WHERE b='10.0' AND b LIKE '10' } {1} do_execsql_test 1.3.1 { SELECT c=10, c = 10.0, c = '10.0', c LIKE '10.0' FROM t1; } {0 1 1 1} do_execsql_test 1.3.2 { SELECT count(*) FROM t1 WHERE c=10 AND c = '10.0' } {0} do_execsql_test 1.3.3 { SELECT count(*) FROM t1 WHERE c=10 AND c LIKE '10.0' } {0} do_execsql_test 1.3.4 { SELECT count(*) FROM t1 WHERE c='10.0' AND c LIKE '10.0' } {1} do_execsql_test 1.3.5 { SELECT count(*) FROM t1 WHERE c=10.0 AND c = '10.0' } {1} do_execsql_test 1.3.6 { SELECT count(*) FROM t1 WHERE c=10.0 AND c LIKE '10.0' } {1} do_execsql_test 1.4.1 { SELECT d=10, d = 10.0, d = '10.0', d LIKE '10.0', d LIKE '10' FROM t1; } {1 1 1 1 0} do_execsql_test 1.4.2 { SELECT count(*) FROM t1 WHERE d=10 AND d = '10.0' } {1} do_execsql_test 1.4.3 { SELECT count(*) FROM t1 WHERE d=10 AND d LIKE '10.0' } {1} do_execsql_test 1.4.4 { SELECT count(*) FROM t1 WHERE d='10.0' AND d LIKE '10.0' } {1} do_execsql_test 1.4.5 { SELECT count(*) FROM t1 WHERE d='10' AND d LIKE '10.0' } {1} do_execsql_test 1.5.1 { SELECT e=10, e = '10.0', e LIKE '10.0', e LIKE '10' FROM t1; } {1 0 1 0} do_execsql_test 1.5.2 { SELECT count(*) FROM t1 WHERE e=10 AND e = '10.0' } {0} do_execsql_test 1.5.3 { SELECT count(*) FROM t1 WHERE e=10 AND e LIKE '10.0' } {1} do_execsql_test 1.5.4 { SELECT count(*) FROM t1 WHERE e='10.0' AND e LIKE '10.0' } {0} do_execsql_test 1.5.5 { SELECT count(*) FROM t1 WHERE e=10.0 AND e LIKE '10.0' } {1} finish_test |