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
This commit is contained in:
parent
2e392e2c53
commit
01f3f25376
22
manifest
22
manifest
@ -1,5 +1,5 @@
|
||||
C Added\stests\sfor\smulti-column\sprimary\skeys.\s(CVS\s585)
|
||||
D 2002-05-24T02:14:50
|
||||
C Add\ssupport\sfor\sthe\sfull\sSQL\sjoin\ssyntax.\s\sThis\sis\sjust\sa\sparser\senhancement.\nWe\snow\srecognize\sall\skinds\sof\sjoins,\sbut\swe\sdon't\sactually\sdo\sanything\swith\nthem\syet.\s(CVS\s586)
|
||||
D 2002-05-24T16:14:15
|
||||
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
|
||||
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
|
||||
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
|
||||
@ -34,21 +34,21 @@ F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
|
||||
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
|
||||
F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded
|
||||
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
|
||||
F src/parse.y 8053fead169ad8345b5a2cfc39749b63995daffb
|
||||
F src/parse.y c681da701bf142967325b8791f22418e2d81552d
|
||||
F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
|
||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||
F src/select.c c46f4e0784175f2a8ec9fde26b7b4f8090cab9cb
|
||||
F src/select.c 6e9f995908a98c21486e25010408ab2fc2e84517
|
||||
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
|
||||
F src/sqliteInt.h 02d9d16c3eea463ff9bcbd13426627cb79d6d190
|
||||
F src/sqliteInt.h 179d1562e272d3c038a8f46cabfbfd74be46a1dc
|
||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
|
||||
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
|
||||
F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
|
||||
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
|
||||
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
|
||||
F src/tokenize.c 5892a8eb8f0063718f5bec1a1b24f4c6029eba9b
|
||||
F src/tokenize.c facec7dc0b4a13e17ad67702f548dac2f7c6a732
|
||||
F src/trigger.c bf1a4f6653e482be0052bc9ade9261cf814c705b
|
||||
F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
|
||||
F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3
|
||||
@ -71,7 +71,7 @@ F test/insert.test 58d44c19b3557f67f4aeb5110ed9ef02038c3684
|
||||
F test/insert2.test eb8481878a7f52ccb4e3346f87550f5afdd77f76
|
||||
F test/intpkey.test 31b5f28b2c44273e6695cf36ab2e4133aee7753c
|
||||
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
|
||||
F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
|
||||
F test/limit.test 6f98bcefc92209103bb3764c81975a6ec21d6702
|
||||
F test/lock.test 3fcfd46a73119f6a18094673328a32c7b3047a8f
|
||||
F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
|
||||
F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd
|
||||
@ -88,7 +88,7 @@ F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
|
||||
F test/select1.test c19617be69fb1322c71e100b5882c469729c4bf1
|
||||
F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4
|
||||
F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
|
||||
F test/select4.test a8f1d97fbd6049211ddd0ed50899e8afb1cb4508
|
||||
F test/select4.test c2313f8c16ca298b0b1ce9cc3c0cfed0939ffea9
|
||||
F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
|
||||
F test/select6.test efb8d0c07a440441db87db2c4ade6904e1407e85
|
||||
F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
|
||||
@ -134,7 +134,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
|
||||
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
|
||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||
P a167b71d8c27e870bc3079c6132e483bffc83298
|
||||
R bc68016f19e54f17abf3f7534099036e
|
||||
P ffc49e56b13096b35e6cbb1a2f7d546843d4a91d
|
||||
R f47635ed868634887aa46a05f1852f4d
|
||||
U drh
|
||||
Z 0889a09c44f0ca7641771392f552bb9a
|
||||
Z 43e7677c78bedc42f512941bca78b9ad
|
||||
|
@ -1 +1 @@
|
||||
ffc49e56b13096b35e6cbb1a2f7d546843d4a91d
|
||||
e238643efdbe1394c7ff85e34e486f7c6082b6cc
|
78
src/parse.y
78
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) ::= FOR(X). {A = X;}
|
||||
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 @@ distinct(A) ::= . {A = 0;}
|
||||
%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 @@ selcollist(A) ::= sclp(P) ids(X) DOT STAR. {
|
||||
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 @@ as ::= AS.
|
||||
%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); }
|
||||
}
|
||||
if( U ){
|
||||
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pUsing = U; }
|
||||
else { sqliteIdListDelete(U); }
|
||||
}
|
||||
sqliteSrcListAddAlias(A,&Z);
|
||||
}
|
||||
|
||||
%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*}
|
||||
|
66
src/select.c
66
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"
|
||||
|
||||
@ -55,6 +55,70 @@ Select *sqliteSelectNew(
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
*/
|
||||
|
@ -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"
|
||||
@ -456,6 +456,17 @@ struct SrcList {
|
||||
} *a; /* One entry for each identifier on the list */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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
|
||||
@ -858,3 +869,4 @@ TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
|
||||
TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
|
||||
TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
|
||||
void sqliteDeleteTrigger(Trigger*);
|
||||
int sqliteJoinType(Parse*, Token*, Token*, Token*);
|
||||
|
@ -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 @@ static Keyword aKeywordTable[] = {
|
||||
{ "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 },
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this file is testing the LIMIT ... OFFSET ... clause
|
||||
# of SELECT statements.
|
||||
#
|
||||
# $Id: limit.test,v 1.1 2001/11/06 14:10:42 drh Exp $
|
||||
# $Id: limit.test,v 1.2 2002/05/24 16:14:16 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -50,10 +50,10 @@ do_test limit-1.5 {
|
||||
execsql {SELECT x FROM t1 ORDER BY x LIMIT 50 OFFSET 50}
|
||||
} {}
|
||||
do_test limit-1.6 {
|
||||
execsql {SELECT * FROM t1 a, t1 b ORDER BY a.x, b.x LIMIT 5}
|
||||
execsql {SELECT * FROM t1 AS a, t1 AS b ORDER BY a.x, b.x LIMIT 5}
|
||||
} {0 5 0 5 0 5 1 5 0 5 2 5 0 5 3 5 0 5 4 5}
|
||||
do_test limit-1.7 {
|
||||
execsql {SELECT * FROM t1 a, t1 b ORDER BY a.x, b.x LIMIT 5 OFFSET 32}
|
||||
execsql {SELECT * FROM t1 AS a, t1 AS b ORDER BY a.x, b.x LIMIT 5 OFFSET 32}
|
||||
} {1 5 0 5 1 5 1 5 1 5 2 5 1 5 3 5 1 5 4 5}
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
# focus of this file is testing UNION, INTERSECT and EXCEPT operators
|
||||
# in SELECT statements.
|
||||
#
|
||||
# $Id: select4.test,v 1.5 2002/04/23 17:10:19 drh Exp $
|
||||
# $Id: select4.test,v 1.6 2002/05/24 16:14:16 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -175,7 +175,7 @@ do_test select4-5.2 {
|
||||
} {0 {0 1 2 3 4 5 5 6 7 8}}
|
||||
do_test select4-5.2b {
|
||||
set v [catch {execsql {
|
||||
SELECT DISTINCT log xyzzy FROM t1
|
||||
SELECT DISTINCT log AS xyzzy FROM t1
|
||||
UNION ALL
|
||||
SELECT n FROM t1 WHERE log=3
|
||||
ORDER BY 'xyzzy';
|
||||
|
Loading…
Reference in New Issue
Block a user