Add support for the full SQL join syntax.  This is just a parser enhancement.
We now recognize all kinds of joins, but we don't actually do anything with
them yet. (CVS 586)

FossilOrigin-Name: e238643efdbe1394c7ff85e34e486f7c6082b6cc
diff --git a/src/parse.y b/src/parse.y
index 4ef3dd3..f78a999 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -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.68 2002/05/24 02:04:33 drh Exp $
+** @(#) $Id: parse.y,v 1.69 2002/05/24 16:14:15 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -128,6 +128,7 @@
 id(A) ::= ID(X).         {A = X;}
 id(A) ::= IGNORE(X).     {A = X;}
 id(A) ::= INSTEAD(X).    {A = X;}
+id(A) ::= JOIN(X).       {A = X;}
 id(A) ::= KEY(X).        {A = X;}
 id(A) ::= OF(X).         {A = X;}
 id(A) ::= OFFSET(X).     {A = X;}
@@ -272,8 +273,9 @@
 %destructor sclp {sqliteExprListDelete($$);}
 sclp(A) ::= selcollist(X) COMMA.             {A = X;}
 sclp(A) ::= .                                {A = 0;}
-selcollist(A) ::= sclp(P) expr(X).           {A = sqliteExprListAppend(P,X,0);}
-selcollist(A) ::= sclp(P) expr(X) as ids(Y). {A = sqliteExprListAppend(P,X,&Y);}
+selcollist(A) ::= sclp(P) expr(X) as(Y).     {
+   A = sqliteExprListAppend(P,X,Y.n?&Y:0);
+}
 selcollist(A) ::= sclp(P) STAR. {
   A = sqliteExprListAppend(P, sqliteExpr(TK_ALL, 0, 0, 0), 0);
 }
@@ -282,8 +284,13 @@
   Expr *pLeft = sqliteExpr(TK_ID, 0, 0, &X);
   A = sqliteExprListAppend(P, sqliteExpr(TK_DOT, pLeft, pRight, 0), 0);
 }
-as ::= .
-as ::= AS.
+
+// An option "AS <id>" phrase that can follow one of the expressions that
+// define the result set, or one of the tables in the FROM clause.
+//
+%type as {Token}
+as(X) ::= AS ids(Y).    { X = Y; }
+as(X) ::= .             { X.n = 0; }
 
 
 %type seltablist {SrcList*}
@@ -293,33 +300,68 @@
 %type from {SrcList*}
 %destructor from {sqliteSrcListDelete($$);}
 
+// A complete FROM clause.
+//
 from(A) ::= .                                 {A = sqliteMalloc(sizeof(*A));}
 from(A) ::= FROM seltablist(X).               {A = X;}
-stl_prefix(A) ::= seltablist(X) COMMA.        {A = X;}
+
+// "seltablist" is a "Select Table List" - the content of the FROM clause
+// in a SELECT statement.  "stl_prefix" is a prefix of this list.
+//
+stl_prefix(A) ::= seltablist(X) joinop(Y).    {
+   A = X;
+   if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y;
+}
 stl_prefix(A) ::= .                           {A = 0;}
-seltablist(A) ::= stl_prefix(X) ids(Y).       {A = sqliteSrcListAppend(X,&Y);}
-seltablist(A) ::= stl_prefix(X) ids(Y) as ids(Z). {
+seltablist(A) ::= stl_prefix(X) ids(Y) as(Z) on_opt(N) using_opt(U). {
   A = sqliteSrcListAppend(X,&Y);
-  sqliteSrcListAddAlias(A,&Z);
+  if( Z.n ) sqliteSrcListAddAlias(A,&Z);
+  if( N ){
+    if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; }
+    else { sqliteExprDelete(N); }
+  }
+  if( U ){
+    if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; }
+    else { sqliteIdListDelete(U); }
+  }
 }
-seltablist(A) ::= stl_prefix(X) LP select(S) RP. {
+seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). {
   A = sqliteSrcListAppend(X,0);
   A->a[A->nSrc-1].pSelect = S;
   if( S->pOrderBy ){
     sqliteExprListDelete(S->pOrderBy);
     S->pOrderBy = 0;
   }
-}
-seltablist(A) ::= stl_prefix(X) LP select(S) RP as ids(Z). {
-  A = sqliteSrcListAppend(X,0);
-  A->a[A->nSrc-1].pSelect = S;
-  if( S->pOrderBy ){
-    sqliteExprListDelete(S->pOrderBy);
-    S->pOrderBy = 0;
+  if( Z.n ) sqliteSrcListAddAlias(A,&Z);
+  if( N ){
+    if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; }
+    else { sqliteExprDelete(N); }
   }
-  sqliteSrcListAddAlias(A,&Z);
+  if( U ){
+    if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; }
+    else { sqliteIdListDelete(U); }
+  }
 }
 
+%type joinop {int}
+%type joinop2 {int}
+joinop(X) ::= COMMA.                   { X = JT_INNER; }
+joinop(X) ::= JOIN.                    { X = JT_INNER; }
+joinop(X) ::= ID(A) JOIN.              { X = sqliteJoinType(pParse,&A,0,0); }
+joinop(X) ::= ID(A) ID(B) JOIN.        { X = sqliteJoinType(pParse,&A,&B,0); }
+joinop(X) ::= ID(A) ID(B) ID(C) JOIN.  { X = sqliteJoinType(pParse,&A,&B,&C); }
+
+%type on_opt {Expr*}
+%destructor on_opt {sqliteExprDelete($$);}
+on_opt(N) ::= ON expr(E).   {N = E;}
+on_opt(N) ::= .             {N = 0;}
+
+%type using_opt {IdList*}
+%destructor using_opt {sqliteIdListDelete($$);}
+using_opt(U) ::= USING LP idxlist(L) RP.  {U = L;}
+using_opt(U) ::= .                        {U = 0;}
+
+
 %type orderby_opt {ExprList*}
 %destructor orderby_opt {sqliteExprListDelete($$);}
 %type sortlist {ExprList*}
diff --git a/src/select.c b/src/select.c
index d6cbff5..9541ef1 100644
--- a/src/select.c
+++ b/src/select.c
@@ -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.82 2002/05/24 02:04:33 drh Exp $
+** $Id: select.c,v 1.83 2002/05/24 16:14:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -56,6 +56,70 @@
 }
 
 /*
+** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
+** type of join.  Return an integer constant that expresses that type
+** in terms of the following bit values:
+**
+**     JT_INNER
+**     JT_OUTER
+**     JT_NATURAL
+**     JT_LEFT
+**     JT_RIGHT
+**
+** A full outer join is the combination of JT_LEFT and JT_RIGHT.
+**
+** If an illegal or unsupported join type is seen, then still return
+** a join type, but put an error in the pParse structure.
+*/
+int sqliteJoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
+  int jointype = 0;
+  Token *apAll[3];
+  Token *p;
+  static struct {
+    const char *zKeyword;
+    int nChar;
+    int code;
+  } keywords[] = {
+    { "natural", 7, JT_NATURAL },
+    { "left",    4, JT_LEFT },
+    { "right",   5, JT_RIGHT },
+    { "full",    4, JT_FULL },
+    { "outer",   5, JT_OUTER },
+    { "inner",   5, JT_INNER },
+    { "cross",   5, JT_INNER },
+  };
+  int i, j;
+  apAll[0] = pA;
+  apAll[1] = pB;
+  apAll[2] = pC;
+  for(i=0; apAll[i]; i++){
+    p = apAll[i];
+    for(j=0; j<sizeof(keywords)/sizeof(keywords[0]); j++){
+      if( p->n==keywords[j].nChar 
+          && sqliteStrNICmp(p->z, keywords[j].zKeyword, p->n)==0 ){
+        jointype |= keywords[j].code;
+        break;
+      }
+    }
+    if( j>=sizeof(keywords)/sizeof(keywords[0]) ){
+      jointype |= JT_ERROR;
+      break;
+    }
+  }
+  if( (jointype & ~JT_INNER)!=0 ){
+    static Token dummy = { 0, 0 };
+    char *zSp1 = " ", *zSp2 = " ";
+    if( pB==0 ){ pB = &dummy; zSp1 = 0; }
+    if( pC==0 ){ pC = &dummy; zSp2 = 0; }
+    sqliteSetNString(&pParse->zErrMsg, "unknown or unsupported join type: ", 0,
+       pA->z, pA->n, zSp1, 1, pB->z, pB->n, zSp2, 1, pC->z, pC->n, 0);
+    pParse->nErr++;
+    jointype = JT_INNER;
+  }
+  return jointype;
+}
+
+/*
 ** Delete the given Select structure and all of its substructures.
 */
 void sqliteSelectDelete(Select *p){
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 04cb42d..c413b52 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.113 2002/05/24 02:04:33 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.114 2002/05/24 16:14:15 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -457,6 +457,17 @@
 };
 
 /*
+** Permitted values of the SrcList.a.jointype field
+*/
+#define JT_INNER     0x0001    /* Any kind of inner or cross join */
+#define JT_NATURAL   0x0002    /* True for a "natural" join */
+#define JT_LEFT      0x0014    /* Left outer join */
+#define JT_RIGHT     0x0018    /* Right outer join */
+#define JT_FULL      0x001a    /* Combination of left and right outer join */
+#define JT_OUTER     0x0010    /* The "OUTER" keyword is present */
+#define JT_ERROR     0x0020    /* unknown or unsupported join type */
+
+/*
 ** For each nested loop in a WHERE clause implementation, the WhereInfo
 ** structure contains a single instance of this structure.  This structure
 ** is intended to be private the the where.c module and should not be
@@ -858,3 +869,4 @@
 TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
 TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
 void sqliteDeleteTrigger(Trigger*);
+int sqliteJoinType(Parse*, Token*, Token*, Token*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 306dc34..b566f09 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.42 2002/05/23 00:30:31 drh Exp $
+** $Id: tokenize.c,v 1.43 2002/05/24 16:14:15 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -82,6 +82,7 @@
   { "INTO",              0, TK_INTO,             0 },
   { "IS",                0, TK_IS,               0 },
   { "ISNULL",            0, TK_ISNULL,           0 },
+  { "JOIN",              0, TK_JOIN,             0 },
   { "KEY",               0, TK_KEY,              0 },
   { "LIKE",              0, TK_LIKE,             0 },
   { "LIMIT",             0, TK_LIMIT,            0 },