/ Check-in [ef4059e3]
Login

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

Overview
Comment:Make sure that the use of a double-quoted string literal does not trick the optimizer into using a correlated subquery when a static subquery would suffice. (CVS 2477)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:ef4059e3afa1a61a9e59df00cdfedc57d8df9fec
User & Date: drh 2005-05-23 15:06:39
Context
2005-05-23
17:26
The REGEXP operator is recognized. It tries to invoke a function named regexp() which does not exist in the native build. But users who want to can add an appropriate regexp() function using sqlite3_create_function(). (CVS 2478) check-in: 42a626ac user: drh tags: trunk
15:06
Make sure that the use of a double-quoted string literal does not trick the optimizer into using a correlated subquery when a static subquery would suffice. (CVS 2477) check-in: ef4059e3 user: drh tags: trunk
13:00
Retain the error string if an error is generated by SSE during a VACUUM. (CVS 2476) check-in: f7b76d02 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
...
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
...
915
916
917
918
919
920
921



922
923
924
925
926
927
928
...
961
962
963
964
965
966
967
968
969
970
971
972
973


974
975
976
977
978
979







980
981



982
983
984
985
986
987
988
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.198 2005/04/22 02:38:38 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Return the 'affinity' of the expression pExpr if any.
**
................................................................................
  }

  pExpr->iTable = -1;
  while( pNC && cnt==0 ){
    SrcList *pSrcList = pNC->pSrcList;
    ExprList *pEList = pNC->pEList;

    pNC->nRef++;
    /* assert( zTab==0 || pEList==0 ); */
    if( pSrcList ){
      for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
        Table *pTab = pItem->pTab;
        Column *pCol;
  
        if( pTab==0 ) continue;
................................................................................
      for(j=0; j<pEList->nExpr; j++){
        char *zAs = pEList->a[j].zName;
        if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
          assert( pExpr->pLeft==0 && pExpr->pRight==0 );
          pExpr->op = TK_AS;
          pExpr->iColumn = j;
          pExpr->pLeft = sqlite3ExprDup(pEList->a[j].pExpr);
          sqliteFree(zCol);
          assert( zTab==0 && zDb==0 );
          return 0;
        }
      } 
    }

    /* Advance to the next name context.  The loop will exit when either
    ** we have a match (cnt>0) or when we run out of name contexts.
    */
................................................................................

  /*
  ** If X and Y are NULL (in other words if only the column name Z is
  ** supplied) and the value of Z is enclosed in double-quotes, then
  ** Z is a string literal if it doesn't match any column names.  In that
  ** case, we need to return right away and not make any changes to
  ** pExpr.



  */
  if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
    sqliteFree(zCol);
    return 0;
  }

  /*
................................................................................
  }

lookupname_end:
  /* Clean up and return
  */
  sqliteFree(zDb);
  sqliteFree(zTab);
  sqliteFree(zCol);
  sqlite3ExprDelete(pExpr->pLeft);
  pExpr->pLeft = 0;
  sqlite3ExprDelete(pExpr->pRight);
  pExpr->pRight = 0;
  pExpr->op = TK_COLUMN;


  if( cnt==1 ){
    assert( pNC!=0 );
    sqlite3AuthRead(pParse, pExpr, pNC->pSrcList);
    if( pMatch && !pMatch->pSelect ){
      pExpr->pTab = pMatch->pTab;
    }







  }
  return cnt!=1;



}

/*
** pExpr is a node that defines a function of some kind.  It might
** be a syntactic function like "count(x)" or it might be a function
** that implements an operator, like "a LIKE b".  
**







|







 







<







 







|

|







 







>
>
>







 







<





>
>






>
>
>
>
>
>
>
|
|
>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
786
787
788
789
790
791
792

793
794
795
796
797
798
799
...
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
...
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
...
963
964
965
966
967
968
969

970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.199 2005/05/23 15:06:39 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Return the 'affinity' of the expression pExpr if any.
**
................................................................................
  }

  pExpr->iTable = -1;
  while( pNC && cnt==0 ){
    SrcList *pSrcList = pNC->pSrcList;
    ExprList *pEList = pNC->pEList;


    /* assert( zTab==0 || pEList==0 ); */
    if( pSrcList ){
      for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
        Table *pTab = pItem->pTab;
        Column *pCol;
  
        if( pTab==0 ) continue;
................................................................................
      for(j=0; j<pEList->nExpr; j++){
        char *zAs = pEList->a[j].zName;
        if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
          assert( pExpr->pLeft==0 && pExpr->pRight==0 );
          pExpr->op = TK_AS;
          pExpr->iColumn = j;
          pExpr->pLeft = sqlite3ExprDup(pEList->a[j].pExpr);
          cnt = 1;
          assert( zTab==0 && zDb==0 );
          goto lookupname_end_2;
        }
      } 
    }

    /* Advance to the next name context.  The loop will exit when either
    ** we have a match (cnt>0) or when we run out of name contexts.
    */
................................................................................

  /*
  ** If X and Y are NULL (in other words if only the column name Z is
  ** supplied) and the value of Z is enclosed in double-quotes, then
  ** Z is a string literal if it doesn't match any column names.  In that
  ** case, we need to return right away and not make any changes to
  ** pExpr.
  **
  ** Because no reference was made to outer contexts, the pNC->nRef
  ** fields are not changed in any context.
  */
  if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
    sqliteFree(zCol);
    return 0;
  }

  /*
................................................................................
  }

lookupname_end:
  /* Clean up and return
  */
  sqliteFree(zDb);
  sqliteFree(zTab);

  sqlite3ExprDelete(pExpr->pLeft);
  pExpr->pLeft = 0;
  sqlite3ExprDelete(pExpr->pRight);
  pExpr->pRight = 0;
  pExpr->op = TK_COLUMN;
lookupname_end_2:
  sqliteFree(zCol);
  if( cnt==1 ){
    assert( pNC!=0 );
    sqlite3AuthRead(pParse, pExpr, pNC->pSrcList);
    if( pMatch && !pMatch->pSelect ){
      pExpr->pTab = pMatch->pTab;
    }
    /* Increment the nRef value on all name contexts from TopNC up to
    ** the point where the name matched. */
    for(;;){
      assert( pTopNC!=0 );
      pTopNC->nRef++;
      if( pTopNC==pNC ) break;
      pTopNC = pTopNC->pNext;
    }
    return 0;
  } else {
    return 1;
  }
}

/*
** pExpr is a node that defines a function of some kind.  It might
** be a syntactic function like "count(x)" or it might be a function
** that implements an operator, like "a LIKE b".  
**

Changes to test/subquery.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
331
332
333
334
335
336
337








338




339
340















341







342





#    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.  The
# focus of this script is testing correlated subqueries
#
# $Id: subquery.test,v 1.8 2005/02/14 06:38:40 danielk1977 Exp $
#

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

ifcapable !subquery {
  finish_test
................................................................................
  }
  execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
} {}
do_test subquery-4.2.2 {
  execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
} {}















finish_test




































|







 







>
>
>
>
>
>
>
>
|
>
>
>
>
|
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>

>
>
>
>
>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#    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.  The
# focus of this script is testing correlated subqueries
#
# $Id: subquery.test,v 1.9 2005/05/23 15:06:39 drh Exp $
#

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

ifcapable !subquery {
  finish_test
................................................................................
  }
  execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
} {}
do_test subquery-4.2.2 {
  execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)}
} {}

#------------------------------------------------------------------
# The subquery-5.* tests make sure string literals in double-quotes
# are handled efficiently.  Double-quote literals are first checked
# to see if they match any column names.  If there is not column name
# match then those literals are used a string constants.  When a
# double-quoted string appears, we want to make sure that the search
# for a matching column name did not cause an otherwise static subquery
# to become a dynamic (correlated) subquery.
#
do_test subquery-5.1 {
  proc callcntproc {n} {
    incr ::callcnt
    return $n
  }

  set callcnt 0
  db function callcnt callcntproc
  execsql {
    CREATE TABLE t4(x,y);
    INSERT INTO t4 VALUES('one',1);
    INSERT INTO t4 VALUES('two',2);
    INSERT INTO t4 VALUES('three',3);
    INSERT INTO t4 VALUES('four',4);
    CREATE TABLE t5(a,b);
    INSERT INTO t5 VALUES(1,11);
    INSERT INTO t5 VALUES(2,22);
    INSERT INTO t5 VALUES(3,33);
    INSERT INTO t5 VALUES(4,44);
    SELECT b FROM t5 WHERE a IN 
       (SELECT callcnt(y)+0 FROM t4 WHERE x="two")
  }
} {22}
do_test subquery-5.2 {
  # This is the key test.  The subquery should have only run once.  If
  # The double-quoted identifier "two" were causing the subquery to be
  # processed as a correlated subquery, then it would have run 4 times.
  set callcnt
} {1}





finish_test