]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Minimal support for oracle8 outer join syntax. (CVS 771)
authordrh <drh@noemail.net>
Sun, 27 Oct 2002 19:35:33 +0000 (19:35 +0000)
committerdrh <drh@noemail.net>
Sun, 27 Oct 2002 19:35:33 +0000 (19:35 +0000)
FossilOrigin-Name: 31df3690d0fe4bd4a293cbe8ca9a26c98c3ed3ce

manifest
manifest.uuid
src/parse.y
src/select.c
src/sqliteInt.h
src/tokenize.c
src/where.c
test/join.test

index 104ab04818e78834c8f4c7fc14940d72e5c95040..bcbe5aa229e661086b8279203156fa874caf4466 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Correctly\shandle\scolumn\snames\sand\sstring\sconstants\sin\sparentheses.\nFix\sfor\sticket\s#179.\s(CVS\s770)
-D 2002-10-22T23:38:04
+C Minimal\ssupport\sfor\soracle8\souter\sjoin\ssyntax.\s(CVS\s771)
+D 2002-10-27T19:35:34
 F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -34,27 +34,27 @@ F src/os.c ede7346dffdbab40c6de20d2f325981f3f1168fd
 F src/os.h 3009379b06941e7796a9812d1b6cbc59b26248c8
 F src/pager.c 592e5931fdc65e952a6c3e152bc822580856532a
 F src/pager.h 6991c9c2dc5e4c7f2df4d4ba47d1c6458f763a32
-F src/parse.y e37e66aa0aee94d235058c7a7b7aaa71bb401324
+F src/parse.y 469c9636ff713e63c00234662209f11668671ae9
 F src/printf.c 5c50fc1da75c8f5bf432b1ad17d91d6653acd167
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 4a6f3fb86a9e25f6c74ee95c5d0e26a0b8648592
+F src/select.c ce82596a2eaaf418edba45b2426f41065e49578b
 F src/shell.c 9e9a6eb6bca07f01e6472a603f908a0127ea50ff
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in d3999a9c6374675779058d6cfe5431131618e92b
-F src/sqliteInt.h 9a5fea861133f1913cfebd3bbbf6a4e9ace47fef
+F src/sqliteInt.h a001c52dfb10ec38f18d6b9ed7dd8b3f42ca8c72
 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c fa646506f02509455c1e4a878d1303bd2d4c3ead
 F src/test1.c a46e9f61915b32787c5d5a05a4b92e4dacc437d9
 F src/test2.c 5fa694d130b3309e3f9c852f0a437750fcb5a006
 F src/test3.c 540fa7fc3cb3732517b779b5f90ad9cc4303d0ab
 F src/threadtest.c 72bce0a284647314847bbea44616ceb056bfb77f
-F src/tokenize.c 0000d3555af749cfc2d85f9686fdab82eedb77c4
+F src/tokenize.c 75e3bb37305b64e118e709752066f494c4f93c30
 F src/trigger.c 5ba917fc226b96065108da28186c2efaec53e481
 F src/update.c 881e4c8e7c786545da4fd2d95da19252b2e31137
 F src/util.c ca7650ef2cc2d50241e48029fca109a3016144ee
 F src/vdbe.c b5d25c18f306cdb1da145dd555b056057920521f
 F src/vdbe.h b7584044223104ba7896a7f87b66daebdd6022ba
-F src/where.c 8ff2acfcb9bb15a057512bd6207b259feed57a2c
+F src/where.c 615a0f0bed305bcb27073c69347ea75018e8b58d
 F test/all.test efd958d048c70a3247997c482f0b33561f7759f0
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test 10e75aec120ecefc0edc4c912a0980a43db1b6c2
@@ -73,7 +73,7 @@ F test/insert.test a122afb86911e77c181d912348866a5b1a61eeab
 F test/insert2.test c288375a64dad3295044714f0dfed4a193cf067f
 F test/intpkey.test f3620158fd7963af1306b01047277f10ae91a30b
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
-F test/join.test 05d2cd1af855e3720e0465668d2923b228d29556
+F test/join.test 178b25dc3c5be6cbdd195b904e66bee62475d0bf
 F test/limit.test 9f26f874bc765df5b3f5c92d26d1b12eac6d4cf9
 F test/lock.test 388a3a10962d2d571c0c1821cc35bf069ee73473
 F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
@@ -149,7 +149,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 562da534bbb605a8ce15824135b012ef2d86bbeb
-R df476aa0a67bb0f1bc7353bd04cb345e
+P 3b68aa25c451b7c09ece457ac2b70a9a5d93508a
+R f078ec26197d58bed051ed1d4802e2fc
 U drh
-Z f9c703cf01e9f1a25c5301f84c45f9b9
+Z e77ead48c863ebb46d0070eee477e05f
index 9e6c29e0402cda41cafd9b2fb88c8c6b16b1df33..27dda7cc5ff763a423d217bcbe2b6b403bb9d95f 100644 (file)
@@ -1 +1 @@
-3b68aa25c451b7c09ece457ac2b70a9a5d93508a
\ No newline at end of file
+31df3690d0fe4bd4a293cbe8ca9a26c98c3ed3ce
\ No newline at end of file
index e2dd57f5a4955856153c0c8651bdd3161ad28332..d6f07ad65af7566bba6c6533a0aa907fa64e772e 100644 (file)
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.84 2002/10/22 23:38:04 drh Exp $
+** @(#) $Id: parse.y,v 1.85 2002/10/27 19:35:34 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -499,6 +499,7 @@ inscollist(A) ::= nm(Y).                      {A = sqliteIdListAppend(0,&Y);}
 %left STAR SLASH REM.
 %left CONCAT.
 %right UMINUS UPLUS BITNOT.
+%right ORACLE_OUTER_JOIN.
 
 %type expr {Expr*}
 %destructor expr {sqliteExprDelete($$);}
@@ -512,6 +513,8 @@ expr(A) ::= nm(X) DOT nm(Y). {
   Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &Y);
   A = sqliteExpr(TK_DOT, temp1, temp2, 0);
 }
+expr(A) ::= expr(B) ORACLE_OUTER_JOIN. 
+                             {A = B; ExprSetProperty(A,EP_Oracle8Join);}
 expr(A) ::= INTEGER(X).      {A = sqliteExpr(TK_INTEGER, 0, 0, &X);}
 expr(A) ::= FLOAT(X).        {A = sqliteExpr(TK_FLOAT, 0, 0, &X);}
 expr(A) ::= STRING(X).       {A = sqliteExpr(TK_STRING, 0, 0, &X);}
index e964f48fd77d23fce0d72d62b2e3048c9074dd55..caf0adcd371dbe5fceac833128249a0a9b17118e 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.114 2002/10/22 23:38:04 drh Exp $
+** $Id: select.c,v 1.115 2002/10/27 19:35:35 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -169,7 +169,7 @@ static void addWhereTerm(
   pE1c = sqliteExpr(TK_DOT, pE1b, pE1a, 0);
   pE2c = sqliteExpr(TK_DOT, pE2b, pE2a, 0);
   pE = sqliteExpr(TK_EQ, pE1c, pE2c, 0);
-  pE->isJoinExpr = 1;
+  ExprSetProperty(pE, EP_FromJoin);
   if( *ppExpr ){
     *ppExpr = sqliteExpr(TK_AND, *ppExpr, pE, 0);
   }else{
@@ -178,15 +178,18 @@ static void addWhereTerm(
 }
 
 /*
-** Set the Expr.isJoinExpr flag on all terms of the given expression.
+** Set the EP_FromJoin property on all terms of the given expression.
 **
-** The Expr.isJoinExpr flag is used at on terms of an expression to tell
+** The EP_FromJoin property is used at on terms of an expression to tell
 ** the LEFT OUTER JOIN processing logic that this term is part of the
-** join restriction and not a part of the more general WHERE clause.
+** join restriction specified in the ON or USING clause and not a part
+** 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.
 */
 static void setJoinExpr(Expr *p){
   while( p ){
-    p->isJoinExpr = 1;
+    ExprSetProperty(p, EP_FromJoin);
     setJoinExpr(p->pLeft);
     p = p->pRight;
   } 
@@ -277,6 +280,60 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
   return 0;
 }
 
+/*
+** This routine implements a minimal Oracle8 join syntax immulation.
+** The precise oracle8 syntax is not implemented - it is easy enough
+** to get this routine confused.  But this routine does make it possible
+** to write a single SQL statement that does a left outer join in both
+** oracle8 and in SQLite.
+**
+** This routine looks for TK_COLUMN expression nodes that are marked
+** with the EP_Oracle8Join property.  Such nodes are generated by a
+** column name (either "column" or "table.column") that is followed by
+** the special "(+)" operator.  If the table of the column marked with
+** the (+) operator is the second are subsequent table in a join, then
+** that table becomes the left table in a LEFT OUTER JOIN.  The expression
+** that uses that table becomes part of the ON clause for the join.
+**
+** It is important to enphasize that this is not exactly how oracle8
+** works.  But it is close enough so that one can construct queries that
+** will work correctly for both SQLite and Oracle8.
+*/
+static int sqliteOracle8JoinFixup(
+  int base,         /* VDBE cursor number for first table in pSrc */
+  SrcList *pSrc,    /* List of tables being joined */
+  Expr *pWhere      /* The WHERE clause of the SELECT statement */
+){
+  int rc = 0;
+  if( ExprHasProperty(pWhere, EP_Oracle8Join) && pWhere->op==TK_COLUMN ){
+    int idx = pWhere->iTable - base;
+    assert( idx>=0 && idx<pSrc->nSrc );
+    if( idx>0 ){
+      pSrc->a[idx-1].jointype &= ~JT_INNER;
+      pSrc->a[idx-1].jointype |= JT_OUTER|JT_LEFT;
+      return 1;
+    }
+  }
+  if( pWhere->pRight ){
+    rc = sqliteOracle8JoinFixup(base, pSrc, pWhere->pRight);
+  }
+  if( pWhere->pLeft ){
+    rc |= sqliteOracle8JoinFixup(base, pSrc, pWhere->pLeft);
+  }
+  if( pWhere->pList ){
+    int i;
+    ExprList *pList = pWhere->pList;
+    for(i=0; i<pList->nExpr && rc==0; i++){
+      rc |= sqliteOracle8JoinFixup(base, pSrc, pList->a[i].pExpr);
+    }
+  }
+  if( rc==1 && (pWhere->op==TK_AND || pWhere->op==TK_EQ) ){
+    setJoinExpr(pWhere);
+    rc = 0;
+  }
+  return rc;
+}
+
 /*
 ** Delete the given Select structure and all of its substructures.
 */
@@ -1801,6 +1858,7 @@ int sqliteSelect(
     if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
       goto select_end;
     }
+    sqliteOracle8JoinFixup(base, pTabList, pWhere);
   }
   if( pOrderBy ){
     for(i=0; i<pOrderBy->nExpr; i++){
index 60ec0dadce6adaec66282ba5633b261b7ac1af71..313ae5fdd174cd88177a025e3a28a361c365fc6a 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.147 2002/10/22 23:38:04 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.148 2002/10/27 19:35:35 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -493,7 +493,7 @@ struct Token {
 struct Expr {
   u8 op;                 /* Operation performed by this node */
   u8 dataType;           /* Either SQLITE_SO_TEXT or SQLITE_SO_NUM */
-  u8 isJoinExpr;         /* Origin is the ON or USING phrase of a join */
+  u16 flags;             /* Various flags.  See below */
   Expr *pLeft, *pRight;  /* Left and right subnodes */
   ExprList *pList;       /* A list of expressions used as function arguments
                          ** or in "<expr> IN (<expr-list)" */
@@ -507,6 +507,21 @@ struct Expr {
                          ** right side of "<expr> IN (<select>)" */
 };
 
+/*
+** The following are the meanings of bits in the Expr.flags field.
+*/
+#define EP_FromJoin     0x0001  /* Originated in ON or USING clause of a join */
+#define EP_Oracle8Join  0x0002  /* Carries the Oracle8 "(+)" join operator */
+
+/*
+** These macros can be used to test, set, or clear bits in the 
+** Expr.flags field.
+*/
+#define ExprHasProperty(E,P)     (((E)->flags&(P))==(P))
+#define ExprHasAnyProperty(E,P)  (((E)->flags&(P))!=0)
+#define ExprSetProperty(E,P)     (E)->flags|=(P)
+#define ExprClearProperty(E,P)   (E)->flags&=~(P)
+
 /*
 ** A list of expressions.  Each expression may optionally have a
 ** name.  An expr/name combination can be used in several ways, such
index 8b03bd94e077715cac8681adda04b56203e7f7cf..f792b74fdc82c3ff59969bd4b36666f56269de91 100644 (file)
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.50 2002/10/22 23:38:04 drh Exp $
+** $Id: tokenize.c,v 1.51 2002/10/27 19:35:35 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -234,8 +234,13 @@ static int sqliteGetToken(const unsigned char *z, int *tokenType){
       return 1;
     }
     case '(': {
-      *tokenType = TK_LP;
-      return 1;
+      if( z[1]=='+' && z[2]==')' ){
+        *tokenType = TK_ORACLE_OUTER_JOIN;
+        return 3;
+      }else{
+        *tokenType = TK_LP;
+        return 1;
+      }
     }
     case ')': {
       *tokenType = TK_RP;
index 0e893a1e2abcf0fd39720e24e824f6c9f3e3ca73..7470a669ce187137da224284a55b56a68b05f4c7 100644 (file)
@@ -13,7 +13,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.65 2002/09/30 12:36:26 drh Exp $
+** $Id: where.c,v 1.66 2002/10/27 19:35:35 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -26,6 +26,8 @@ typedef struct ExprInfo ExprInfo;
 struct ExprInfo {
   Expr *p;                /* Pointer to the subexpression */
   u8 indexable;           /* True if this subexprssion is usable by an index */
+  u8 oracle8join;         /* -1 if left side contains "(+)".  +1 if right side
+                          ** contains "(+)".  0 if neither contains "(+)" */
   short int idxLeft;      /* p->pLeft is a column in this table number. -1 if
                           ** p->pLeft is not the column of any table */
   short int idxRight;     /* p->pRight is a column in this table number. -1 if
@@ -1019,7 +1021,9 @@ WhereInfo *sqliteWhereBegin(
     for(j=0; j<nExpr; j++){
       if( aExpr[j].p==0 ) continue;
       if( (aExpr[j].prereqAll & loopMask)!=aExpr[j].prereqAll ) continue;
-      if( pLevel->iLeftJoin && aExpr[j].p->isJoinExpr==0 ) continue;
+      if( pLevel->iLeftJoin && !ExprHasProperty(aExpr[j].p,EP_FromJoin) ){
+        continue;
+      }
       if( haveKey ){
         haveKey = 0;
         sqliteVdbeAddOp(v, OP_MoveTo, base+idx, 0);
index b50af6a02a394dd7df76c13d9469ef09e6221099..4562184f1abceda62dfa9ce6f2e030d53f99d004 100644 (file)
@@ -12,7 +12,7 @@
 #
 # This file implements tests for joins, including outer joins.
 #
-# $Id: join.test,v 1.6 2002/09/30 12:36:26 drh Exp $
+# $Id: join.test,v 1.7 2002/10/27 19:35:35 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -300,4 +300,56 @@ do_test join-5.1 {
   }
 } {1 a xxx 2 b xxx 3 c {}}
 
+# Test the goofy Oracle8 outer join syntax.
+#
+do_test join-6.1 {
+  execsql {
+    DELETE FROM t1;
+    INSERT INTO t1 VALUES(1,2,3);
+    INSERT INTO t1 VALUES(2,3,4);
+    INSERT INTO t1 VALUES(3,4,5);
+    SELECT * FROM t1;
+  }  
+} {1 2 3 2 3 4 3 4 5}
+do_test join-6.2 {
+  execsql {
+    DELETE FROM t2;
+    INSERT INTO t2 VALUES(1,2,3);
+    INSERT INTO t2 VALUES(2,3,4);
+    INSERT INTO t2 VALUES(3,4,5);
+    SELECT * FROM t2;
+  }  
+} {1 2 3 2 3 4 3 4 5}
+do_test join-6.3 {
+  execsql {
+    SELECT * FROM t1 LEFT OUTER JOIN t2 ON (t1.a=t2.c);
+  }
+} {1 2 3 {} {} {} 2 3 4 1 2 3 3 4 5 2 3 4}
+do_test join-6.4 {
+  execsql {
+    SELECT * FROM t1, t2 WHERE t1.a=t2.c(+);
+  }
+} {1 2 3 {} {} {} 2 3 4 1 2 3 3 4 5 2 3 4}
+do_test join-6.5 {
+  execsql {
+    SELECT * FROM t1 LEFT OUTER JOIN t2 ON (t1.a=t2.c) WHERE t1.b=2
+  }
+} {1 2 3 {} {} {}}
+do_test join-6.6 {
+  execsql {
+    SELECT * FROM t1, t2 WHERE t1.a=t2.c(+) AND t1.b=2;
+  }
+} {1 2 3 {} {} {}}
+do_test join-6.7 {
+  execsql {
+    SELECT * FROM t1 LEFT OUTER JOIN t2 ON (t1.b=t2.b AND t1.c=t2.c)
+  }
+} {1 2 3 2 3 4 2 3 4 3 4 5 3 4 5 {} {} {}}
+do_test join-6.8 {
+  execsql {
+    SELECT * FROM t1, t2 WHERE t1.b=t2.b(+) AND t1.c=t2.c(+);
+  }
+} {1 2 3 2 3 4 2 3 4 3 4 5 3 4 5 {} {} {}}
+
+
 finish_test