SQLite

Check-in [9be208a6]
Login

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: 9be208a6d70582c6808abe6e016ab9cd8d10f0deefb8c7a8c0543372cca15b12
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
Hide Diffs Unified Diffs Ignore Whitespace Patch

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
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
  }
  if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){
    constInsert(pConst,pLeft,pRight,pExpr);
  }
}

/*
** This is a Walker expression callback.  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 its
** corresponding value.


*/
static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){




  int i;
  WhereConst *pConst;
  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;
  }
  pConst = pWalker->u.pConst;
  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;



    /* 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;
}




































/*
** 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







|
>
|
|
|
>
>

|
>
>
>
>

<






<





>
>
>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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