SQLite

Check-in [c5c53152d6]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initialize all constants at the very beginning of a prepared statement. Do not allow constant initialization to occur once control flow has a chance to diverge, to avoid the possibility of having uninitialized registers. Ticket [80ba201079ea60807].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c5c53152d68218bb5e7f922271dd7c50da2361c1
User & Date: drh 2010-12-06 18:50:32.000
Context
2010-12-06
18:59
Back out part of the previous change that was not really necessary in order to fix [80ba201079ea60], and which in fact serves no useful purpose. (check-in: fa9eef865f user: drh tags: trunk)
18:50
Initialize all constants at the very beginning of a prepared statement. Do not allow constant initialization to occur once control flow has a chance to diverge, to avoid the possibility of having uninitialized registers. Ticket [80ba201079ea60807]. (check-in: c5c53152d6 user: drh tags: trunk)
17:11
Have sqlite3_blob_bytes() return 0 following a failed call to sqlite3_reopen_blob(). (check-in: 476a8b4921 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/expr.c.
1651
1652
1653
1654
1655
1656
1657

1658
1659
1660
1661
1662
1663
1664
        SelectDest dest;
        ExprList *pEList;

        assert( !isRowid );
        sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
        dest.affinity = (u8)affinity;
        assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );

        if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
          return 0;
        }
        pEList = pExpr->x.pSelect->pEList;
        if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ 
          keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
              pEList->a[0].pExpr);







>







1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
        SelectDest dest;
        ExprList *pEList;

        assert( !isRowid );
        sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
        dest.affinity = (u8)affinity;
        assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
        pExpr->x.pSelect->iLimit = 0;
        if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
          return 0;
        }
        pEList = pExpr->x.pSelect->pEList;
        if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ 
          keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
              pEList->a[0].pExpr);
1751
1752
1753
1754
1755
1756
1757

1758
1759
1760
1761
1762
1763
1764
        dest.eDest = SRT_Exists;
        sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm);
        VdbeComment((v, "Init EXISTS result"));
      }
      sqlite3ExprDelete(pParse->db, pSel->pLimit);
      pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
                                  &sqlite3IntTokens[1]);

      if( sqlite3Select(pParse, pSel, &dest) ){
        return 0;
      }
      rReg = dest.iParm;
      ExprSetIrreducible(pExpr);
      break;
    }







>







1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
        dest.eDest = SRT_Exists;
        sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm);
        VdbeComment((v, "Init EXISTS result"));
      }
      sqlite3ExprDelete(pParse->db, pSel->pLimit);
      pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
                                  &sqlite3IntTokens[1]);
      pSel->iLimit = 0;
      if( sqlite3Select(pParse, pSel, &dest) ){
        return 0;
      }
      rReg = dest.iParm;
      ExprSetIrreducible(pExpr);
      break;
    }
3029
3030
3031
3032
3033
3034
3035











3036
3037
3038
3039
3040
3041
3042
3043

3044
3045
3046
3047
3048
3049
3050
3051
3052
    pExpr->op2 = pExpr->op;
    pExpr->op = TK_REGISTER;
    pExpr->iTable = r2;
    return WRC_Prune;
  }
  return WRC_Continue;
}












/*
** Preevaluate constant subexpressions within pExpr and store the
** results in registers.  Modify pExpr so that the constant subexpresions
** are TK_REGISTER opcodes that refer to the precomputed values.
*/
void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
  Walker w;

  w.xExprCallback = evalConstExpr;
  w.xSelectCallback = 0;
  w.pParse = pParse;
  sqlite3WalkExpr(&w, pExpr);
}


/*
** Generate code that pushes the value of every element of the given







>
>
>
>
>
>
>
>
>
>
>








>

|







3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
    pExpr->op2 = pExpr->op;
    pExpr->op = TK_REGISTER;
    pExpr->iTable = r2;
    return WRC_Prune;
  }
  return WRC_Continue;
}

/* This routine is part of the parse-tree walker for
** sqlite3ExprCodeConstants().  Simply return WRC_Continue so that
** tree walker logic will extend constant extraction and precoding
** into subqueires.
*/
static int evalConstSelect(Walker *pNotUsed1, Select *pNotUsed2){
  UNUSED_PARAMETER(pNotUsed1);
  UNUSED_PARAMETER(pNotUsed2);
  return WRC_Continue;
}

/*
** Preevaluate constant subexpressions within pExpr and store the
** results in registers.  Modify pExpr so that the constant subexpresions
** are TK_REGISTER opcodes that refer to the precomputed values.
*/
void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
  Walker w;
  if( pParse->cookieGoto ) return;
  w.xExprCallback = evalConstExpr;
  w.xSelectCallback = evalConstSelect;
  w.pParse = pParse;
  sqlite3WalkExpr(&w, pExpr);
}


/*
** Generate code that pushes the value of every element of the given
Added test/tkt-80ba201079.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
# 2010 December 6
#
# 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.
#
#***********************************************************************
# This file implements regression tests for SQLite library. Specifically,
# it tests that ticket [80ba201079ea608071d22a57856b940ea3ac53ce] is
# resolved.  That ticket is about an incorrect result that appears when
# an index is added.  The root cause is that a constant is being used
# without initialization when the OR optimization applies in the WHERE clause.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test tkt-80ba2-100 {
  db eval {
    CREATE TABLE t1(a);
    INSERT INTO t1 VALUES('A');
    CREATE TABLE t2(b);
    INSERT INTO t2 VALUES('B');
    CREATE TABLE t3(c);
    INSERT INTO t3 VALUES('C');
    SELECT * FROM t1, t2
     WHERE (a='A' AND b='X')
        OR (a='A' AND EXISTS (SELECT * FROM t3 WHERE c='C'));
  }
} {A B}
do_test tkt-80ba2-101 {
  db eval {
    CREATE INDEX i1 ON t1(a);
    SELECT * FROM t1, t2
     WHERE (a='A' AND b='X')
        OR (a='A' AND EXISTS (SELECT * FROM t3 WHERE c='C'));
  }
} {A B}

do_test tkt-80ba2-200 {
  db eval {
    CREATE TABLE entry_types (
                        id     integer primary key,
                        name   text
                    );
    INSERT INTO "entry_types" VALUES(100,'cli_command');
    INSERT INTO "entry_types" VALUES(300,'object_change');
    CREATE TABLE object_changes (
                        change_id    integer primary key,
                        system_id    int,
                        obj_id       int,
                        obj_context  text,
                        change_type  int,
                        command_id   int
                    );
    INSERT INTO "object_changes" VALUES(1551,1,114608,'exported_pools',1,2114);
    INSERT INTO "object_changes" VALUES(2048,1,114608,'exported_pools',2,2319);
    CREATE TABLE timeline (
                        rowid        integer primary key,
                        timestamp    text,
                        system_id    int,
                        entry_type   int,
                        entry_id     int
                    );
    INSERT INTO "timeline" VALUES(6735,'2010-11-21 17:08:27.000',1,300,2048);
    INSERT INTO "timeline" VALUES(6825,'2010-11-21 17:09:21.000',1,300,2114);
    SELECT entry_type,
           entry_types.name,
           entry_id
      FROM timeline JOIN entry_types ON entry_type = entry_types.id
     WHERE (entry_types.name = 'cli_command' AND entry_id=2114)
        OR (entry_types.name = 'object_change'
             AND entry_id IN (SELECT change_id
                              FROM object_changes
                               WHERE obj_context = 'exported_pools'));
  }
} {300 object_change 2048}
do_test tkt-80ba2-201 {
  db eval {
    CREATE INDEX timeline_entry_id_idx on timeline(entry_id);
    SELECT entry_type,
           entry_types.name,
           entry_id
      FROM timeline JOIN entry_types ON entry_type = entry_types.id
     WHERE (entry_types.name = 'cli_command' AND entry_id=2114)
        OR (entry_types.name = 'object_change'
             AND entry_id IN (SELECT change_id
                              FROM object_changes
                               WHERE obj_context = 'exported_pools'));
  }
} {300 object_change 2048}

finish_test