]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
ON-clause terms in a LEFT JOIN that restrict only the left table, should not
authordrh <drh@noemail.net>
Mon, 19 Sep 2005 21:05:48 +0000 (21:05 +0000)
committerdrh <drh@noemail.net>
Mon, 19 Sep 2005 21:05:48 +0000 (21:05 +0000)
really restrict the left table but instead rows that fail to meet the condition
to be joined with NULL rows from the right table. (CVS 2725)

FossilOrigin-Name: ea10f9785e3c5248dafcc297f3a2c5465b6e0dba

manifest
manifest.uuid
src/select.c
src/sqliteInt.h
src/where.c
test/join5.test [new file with mode: 0644]

index 0e25caa4c806419ce17f9468b610e73fa1d0788c..e85c2407bce7f9ca0c6065d9b7d88bad6effdc3c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Set\sthe\sdefault\smaximum\spage\ssize\sto\s32768,\snot\s8192.\s(CVS\s2724)
-D 2005-09-19T19:05:21
+C ON-clause\sterms\sin\sa\sLEFT\sJOIN\sthat\srestrict\sonly\sthe\sleft\stable,\sshould\snot\nreally\srestrict\sthe\sleft\stable\sbut\sinstead\srows\sthat\sfail\sto\smeet\sthe\scondition\nto\sbe\sjoined\swith\sNULL\srows\sfrom\sthe\sright\stable.\s(CVS\s2725)
+D 2005-09-19T21:05:49
 F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -63,10 +63,10 @@ F src/pragma.c 6d773e25e8af13ef0820531ad2793417f8a8959d
 F src/prepare.c fc098db25d2a121affb08686cf04833fd50452d4
 F src/printf.c bd421c1ad5e01013c89af63c60eab02852ccd15e
 F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
-F src/select.c 459fb935f1b3f6f0011c548a7664b4d7d062a041
+F src/select.c c3fe1994da81df0e09c4604ef75180f249cabe10
 F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3
 F src/sqlite.h.in 461b2535550cf77aedfd44385da11ef7d63e57a2
-F src/sqliteInt.h f59e1ceccfca73737016b8912ac5f23995dd40c6
+F src/sqliteInt.h 53daa72541b4336c5e89773cf39717ed695bd523
 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
 F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
 F src/test1.c 0f1a66f65a54fba029f7e93b7500d49443dc959b
@@ -87,7 +87,7 @@ F src/vdbeapi.c 85bbe1d0243a89655433d60711b4bd71979b59cd
 F src/vdbeaux.c 57a6ced8417bdc6f06c391d9c560ecbbed644ef3
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c fea0744936008831daa17cdc75056c3ca1469690
-F src/where.c 728051085e8c782370a00ab36b494a244b1e9f4e
+F src/where.c 2b7b4dd112a027021d9c53e25d221761d572a925
 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42
 F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3
 F test/alter.test 9d6837a3d946b73df692b7cef2a7644d2e2f6bc6
@@ -162,6 +162,7 @@ F test/join.test db3802739fb695bdfa2e88805e3d64ec5ffbebd1
 F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
+F test/join5.test 2646825693a6e066ea89b498176d4a68df45ab68
 F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19
 F test/lastinsert.test eaa89c6ee1f13062d87139fd32c1e56753d2fd89
 F test/laststmtchanges.test 19a6d0c11f7a31dc45465b495f7b845a62cbec17
@@ -310,7 +311,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 6cc57fcf15cfa3ce73c78b1cac90f7806e5bae40
-R 263668ef8fd093a2fb233495f6df04ab
+P b32e9ec2480f9da2c2ea705ed3c88ca77b77cf2a
+R 30beedb46fba1ce2f76a3f1396f53cb5
 U drh
-Z dfe987a06f47431345e9f24e1070f391
+Z a986dad9151bb581e7854782adc6d5e8
index b6309cb933e4484b5ada714c3ff235ac340bb968..a570b9b2ee3f96120dff824f93dc28fab4a1c443 100644 (file)
@@ -1 +1 @@
-b32e9ec2480f9da2c2ea705ed3c88ca77b77cf2a
\ No newline at end of file
+ea10f9785e3c5248dafcc297f3a2c5465b6e0dba
\ No newline at end of file
index 369b782fe4ad6017b1273024e7485a460329f935..12379f53cae56ec8827211492310fe6700599a9d 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.271 2005/09/19 17:35:53 drh Exp $
+** $Id: select.c,v 1.272 2005/09/19 21:05:49 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -179,6 +179,7 @@ static void addWhereTerm(
   const char *zAlias1,     /* Alias for first table.  May be NULL */
   const Table *pTab2,      /* Second table */
   const char *zAlias2,     /* Alias for second table.  May be NULL */
+  int iRightJoinTable,     /* VDBE cursor for the right table */
   Expr **ppExpr            /* Add the equality term to this expression */
 ){
   Expr *pE1a, *pE1b, *pE1c;
@@ -199,11 +200,14 @@ static void addWhereTerm(
   pE2c = sqlite3Expr(TK_DOT, pE2b, pE2a, 0);
   pE = sqlite3Expr(TK_EQ, pE1c, pE2c, 0);
   ExprSetProperty(pE, EP_FromJoin);
+  pE->iRightJoinTable = iRightJoinTable;
   *ppExpr = sqlite3ExprAnd(*ppExpr, pE);
 }
 
 /*
 ** Set the EP_FromJoin property on all terms of the given expression.
+** And set the Expr.iRightJoinTable to iTable for every term in the
+** expression.
 **
 ** The EP_FromJoin property is used on terms of an expression to tell
 ** the LEFT OUTER JOIN processing logic that this term is part of the
@@ -211,11 +215,26 @@ static void addWhereTerm(
 ** of the more general WHERE clause.  These terms are moved over to the
 ** WHERE clause during join processing but we need to remember that they
 ** originated in the ON or USING clause.
+**
+** The Expr.iRightJoinTable tells the WHERE clause processing that the
+** expression depends on table iRightJoinTable even if that table is not
+** explicitly mentioned in the expression.  That information is needed
+** for cases like this:
+**
+**    SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.b AND t1.x=5
+**
+** The where clause needs to defer the handling of the t1.x=5
+** term until after the t2 loop of the join.  In that way, a
+** NULL t2 row will be inserted whenever t1.x!=5.  If we do not
+** defer the handling of t1.x=5, it will be processed immediately
+** after the t1 loop and rows with t1.x!=5 will never appear in
+** the output, which is incorrect.
 */
-static void setJoinExpr(Expr *p){
+static void setJoinExpr(Expr *p, int iTable){
   while( p ){
     ExprSetProperty(p, EP_FromJoin);
-    setJoinExpr(p->pLeft);
+    p->iRightJoinTable = iTable;
+    setJoinExpr(p->pLeft, iTable);
     p = p->pRight;
   } 
 }
@@ -262,7 +281,9 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
         char *zName = pLeftTab->aCol[j].zName;
         if( columnIndex(pRightTab, zName)>=0 ){
           addWhereTerm(zName, pLeftTab, pLeft->zAlias, 
-                              pRightTab, pRight->zAlias, &p->pWhere);
+                              pRightTab, pRight->zAlias,
+                              pRight->iCursor, &p->pWhere);
+          
         }
       }
     }
@@ -279,7 +300,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
     ** an AND operator.
     */
     if( pLeft->pOn ){
-      setJoinExpr(pLeft->pOn);
+      setJoinExpr(pLeft->pOn, pRight->iCursor);
       p->pWhere = sqlite3ExprAnd(p->pWhere, pLeft->pOn);
       pLeft->pOn = 0;
     }
@@ -301,7 +322,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
           return 1;
         }
         addWhereTerm(zName, pLeftTab, pLeft->zAlias, 
-                            pRightTab, pRight->zAlias, &p->pWhere);
+                            pRightTab, pRight->zAlias,
+                            pRight->iCursor, &p->pWhere);
       }
     }
   }
index b987608b4e2be126a29d23368d6635a76cd6c62a..beb198f087c76d5f86ae5c197baccba623034af1 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.420 2005/09/19 17:35:53 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.421 2005/09/19 21:05:49 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -903,6 +903,7 @@ struct Expr {
                          ** iColumn-th field of the iTable-th table. */
   AggInfo *pAggInfo;     /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
   int iAgg;              /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
+  int iRightJoinTable;   /* If EP_FromJoin, the right table of the join */
   Select *pSelect;       /* When the expression is a sub-select.  Also the
                          ** right side of "<expr> IN (<select>)" */
   Table *pTab;           /* Table for OP_Column expressions. */
index 12590557dcf83dadea7317d0df39f65f0f59ed38..a32f5c76df68eb9685fa376aac4c9a9a52c0e5a7 100644 (file)
@@ -16,7 +16,7 @@
 ** so is applicable.  Because this module is responsible for selecting
 ** indices, you might also think of this module as the "query optimizer".
 **
-** $Id: where.c,v 1.176 2005/09/19 13:15:23 drh Exp $
+** $Id: where.c,v 1.177 2005/09/19 21:05:49 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -556,14 +556,17 @@ static void exprAnalyze(
   }else{
     pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
   }
-  pTerm->prereqAll = prereqAll = exprTableUsage(pMaskSet, pExpr);
+  prereqAll = exprTableUsage(pMaskSet, pExpr);
+  if( ExprHasProperty(pExpr, EP_FromJoin) ){
+    prereqAll |= getMask(pMaskSet, pExpr->iRightJoinTable);
+  }
+  pTerm->prereqAll = prereqAll;
   pTerm->leftCursor = -1;
   pTerm->iParent = -1;
   pTerm->operator = 0;
   if( allowedOp(pExpr->op) && (pTerm->prereqRight & prereqLeft)==0 ){
     Expr *pLeft = pExpr->pLeft;
     Expr *pRight = pExpr->pRight;
-    assert( prereqAll == (pTerm->prereqRight | prereqLeft) ); /* ticket 1433 */
     if( pLeft->op==TK_COLUMN ){
       pTerm->leftCursor = pLeft->iTable;
       pTerm->leftColumn = pLeft->iColumn;
diff --git a/test/join5.test b/test/join5.test
new file mode 100644 (file)
index 0000000..7d7b62e
--- /dev/null
@@ -0,0 +1,62 @@
+# 2005 September 19
+#
+# 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.
+#
+# This file implements tests for left outer joins containing ON
+# clauses that restrict the scope of the left term of the join.
+#
+# $Id: join5.test,v 1.1 2005/09/19 21:05:50 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_test join5-1.1 {
+  execsql {
+    BEGIN;
+    CREATE TABLE t1(a integer primary key, b integer, c integer);
+    CREATE TABLE t2(x integer primary key, y);
+    CREATE TABLE t3(p integer primary key, q);
+    INSERT INTO t3 VALUES(11,'t3-11');
+    INSERT INTO t3 VALUES(12,'t3-12');
+    INSERT INTO t2 VALUES(11,'t2-11');
+    INSERT INTO t2 VALUES(12,'t2-12');
+    INSERT INTO t1 VALUES(1, 5, 0);
+    INSERT INTO t1 VALUES(2, 11, 2);
+    INSERT INTO t1 VALUES(3, 12, 1);
+    COMMIT;
+  }
+} {}
+do_test join5-1.2 {
+  execsql {
+    select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+  }
+} {1 5 0 {} {} 2 11 2 {} {} 3 12 1 12 t2-12}
+do_test join5-1.3 {
+  execsql {
+    select * from t1 left join t2 on t1.b=t2.x where t1.c=1
+  }
+} {3 12 1 12 t2-12}
+do_test join5-1.4 {
+  execsql {
+    select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+                     left join t3 on t1.b=t3.p and t1.c=2
+  }
+} {1 5 0 {} {} {} {} 2 11 2 {} {} 11 t3-11 3 12 1 12 t2-12 {} {}}
+do_test join5-1.5 {
+  execsql {
+    select * from t1 left join t2 on t1.b=t2.x and t1.c=1
+                     left join t3 on t1.b=t3.p where t1.c=2
+  }
+} {2 11 2 {} {} 11 t3-11}
+
+
+finish_test