aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/Makefile4
-rw-r--r--src/backend/parser/analyze.c242
-rw-r--r--src/backend/parser/gram.y383
-rw-r--r--src/backend/parser/parse_agg.c7
-rw-r--r--src/backend/parser/parse_clause.c993
-rw-r--r--src/backend/parser/parse_expr.c61
-rw-r--r--src/backend/parser/parse_func.c117
-rw-r--r--src/backend/parser/parse_node.c59
-rw-r--r--src/backend/parser/parse_relation.c637
-rw-r--r--src/backend/parser/parse_target.c142
-rw-r--r--src/backend/parser/parser.c66
-rw-r--r--src/backend/parser/scan.l14
12 files changed, 1493 insertions, 1232 deletions
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index 6ef084d04b6..68fed79d382 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -2,7 +2,7 @@
#
# Makefile for parser
#
-# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.29 2000/08/28 11:53:19 petere Exp $
+# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.30 2000/09/12 21:07:00 tgl Exp $
#
#-------------------------------------------------------------------------
@@ -31,7 +31,7 @@ $(srcdir)/gram.c $(srcdir)/parse.h: gram.y
$(srcdir)/scan.c: scan.l
ifdef FLEX
- $(FLEX) $(FLEXFLAGS) -o'$@' $<
+ $(FLEX) $(FLEXFLAGS) -Pbase_yy -o'$@' $<
else
@$(missing) flex $< $@
endif
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index fe21804a2c4..0165ef15c21 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.156 2000/08/29 04:20:44 momjian Exp $
+ * $Id: analyze.c,v 1.157 2000/09/12 21:07:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/relcache.h"
@@ -54,6 +55,8 @@ static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
+static void release_pstate_resources(ParseState *pstate);
+
/* kluge to return extra info from transformCreateStmt() */
static List *extras_before;
static List *extras_after;
@@ -71,28 +74,22 @@ List *
parse_analyze(List *pl, ParseState *parentParseState)
{
List *result = NIL;
- ParseState *pstate;
- Query *parsetree;
while (pl != NIL)
{
+ ParseState *pstate = make_parsestate(parentParseState);
+ Query *parsetree;
+
extras_before = extras_after = NIL;
- pstate = make_parsestate(parentParseState);
parsetree = transformStmt(pstate, lfirst(pl));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ release_pstate_resources(pstate);
while (extras_before != NIL)
{
result = lappend(result,
- transformStmt(pstate, lfirst(extras_before)));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ transformStmt(pstate, lfirst(extras_before)));
+ release_pstate_resources(pstate);
extras_before = lnext(extras_before);
}
@@ -102,10 +99,7 @@ parse_analyze(List *pl, ParseState *parentParseState)
{
result = lappend(result,
transformStmt(pstate, lfirst(extras_after)));
- if (pstate->p_target_relation != NULL)
- heap_close(pstate->p_target_relation, AccessShareLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL;
+ release_pstate_resources(pstate);
extras_after = lnext(extras_after);
}
@@ -116,6 +110,15 @@ parse_analyze(List *pl, ParseState *parentParseState)
return result;
}
+static void
+release_pstate_resources(ParseState *pstate)
+{
+ if (pstate->p_target_relation != NULL)
+ heap_close(pstate->p_target_relation, AccessShareLock);
+ pstate->p_target_relation = NULL;
+ pstate->p_target_rangetblentry = NULL;
+}
+
/*
* transformStmt -
* transform a Parse tree. If it is an optimizable statement, turn it
@@ -176,11 +179,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
Resdom *rd;
id = nth(i, n->aliases);
- Assert(nodeTag(id) == T_Ident);
+ Assert(IsA(id, Ident));
te = nth(i, targetList);
- Assert(nodeTag(te) == T_TargetEntry);
+ Assert(IsA(te, TargetEntry));
rd = te->resdom;
- Assert(nodeTag(rd) == T_Resdom);
+ Assert(IsA(rd, Resdom));
rd->resname = pstrdup(id->name);
}
}
@@ -290,15 +293,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->commandType = CMD_DELETE;
/* set up a range table */
- makeRangeTable(pstate, NULL);
- setTargetTable(pstate, stmt->relname, stmt->inh);
+ makeRangeTable(pstate, NIL);
+ setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->distinctClause = NIL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause);
+ /* done building the rtable */
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -387,12 +392,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*
* In particular, it's time to add the INSERT target to the rangetable.
* (We didn't want it there until now since it shouldn't be visible in
- * the SELECT part.)
+ * the SELECT part.) Note that the INSERT target is NOT added to the
+ * join tree, since we don't want to join over it.
*/
- setTargetTable(pstate, stmt->relname, FALSE);
+ setTargetTable(pstate, stmt->relname, false, false);
/* now the range table will not change */
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
@@ -908,7 +915,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
while (dlist != NIL)
{
constraint = lfirst(dlist);
- Assert(nodeTag(constraint) == T_Constraint);
+ Assert(IsA(constraint, Constraint));
Assert((constraint->contype == CONSTR_PRIMARY)
|| (constraint->contype == CONSTR_UNIQUE));
@@ -1427,17 +1434,68 @@ static Query *
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
{
Query *qry;
- Query *action;
- List *actions;
+ RangeTblEntry *oldrte;
+ RangeTblEntry *newrte;
qry = makeNode(Query);
qry->commandType = CMD_UTILITY;
+ qry->utilityStmt = (Node *) stmt;
+
+ /*
+ * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
+ * equal to 2. Set up their RTEs in the main pstate for use
+ * in parsing the rule qualification.
+ */
+ Assert(pstate->p_rtable == NIL);
+ oldrte = addRangeTableEntry(pstate, stmt->object->relname,
+ makeAttr("*OLD*", NULL),
+ false, true);
+ newrte = addRangeTableEntry(pstate, stmt->object->relname,
+ makeAttr("*NEW*", NULL),
+ false, true);
+ /*
+ * They must be in the jointree too for lookup purposes, but only add
+ * the one(s) that are relevant for the current kind of rule. In an
+ * UPDATE rule, quals must refer to OLD.field or NEW.field to be
+ * unambiguous, but there's no need to be so picky for INSERT & DELETE.
+ * (Note we marked the RTEs "inFromCl = true" above to allow unqualified
+ * references to their fields.)
+ */
+ switch (stmt->event)
+ {
+ case CMD_SELECT:
+ addRTEtoJoinTree(pstate, oldrte);
+ break;
+ case CMD_UPDATE:
+ addRTEtoJoinTree(pstate, oldrte);
+ addRTEtoJoinTree(pstate, newrte);
+ break;
+ case CMD_INSERT:
+ addRTEtoJoinTree(pstate, newrte);
+ break;
+ case CMD_DELETE:
+ addRTEtoJoinTree(pstate, oldrte);
+ break;
+ default:
+ elog(ERROR, "transformRuleStmt: unexpected event type %d",
+ (int) stmt->event);
+ break;
+ }
+
+ /* take care of the where clause */
+ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+
+ if (length(pstate->p_rtable) != 2) /* naughty, naughty... */
+ elog(ERROR, "Rule WHERE condition may not contain references to other relations");
+
+ /* save info about sublinks in where clause */
+ qry->hasSubLinks = pstate->p_hasSubLinks;
/*
- * 'instead nothing' rules with a qualification need a query a
+ * 'instead nothing' rules with a qualification need a query
* rangetable so the rewrite handler can add the negated rule
* qualification to the original query. We create a query with the new
- * command type CMD_NOTHING here that is treated special by the
+ * command type CMD_NOTHING here that is treated specially by the
* rewrite system.
*/
if (stmt->actions == NIL)
@@ -1445,54 +1503,95 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
Query *nothing_qry = makeNode(Query);
nothing_qry->commandType = CMD_NOTHING;
-
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*OLD*", NULL),
- FALSE, FALSE, FALSE);
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*NEW*", NULL),
- FALSE, FALSE, FALSE);
-
nothing_qry->rtable = pstate->p_rtable;
+ nothing_qry->jointree = NIL; /* no join actually wanted */
stmt->actions = lappend(NIL, nothing_qry);
}
-
- actions = stmt->actions;
-
- /*
- * transform each statment, like parse_analyze()
- */
- while (actions != NIL)
+ else
{
+ List *actions;
/*
- * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
- * equal to 2.
+ * transform each statement, like parse_analyze()
*/
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*OLD*", NULL),
- FALSE, FALSE, FALSE);
- addRangeTableEntry(pstate, stmt->object->relname,
- makeAttr("*NEW*", NULL),
- FALSE, FALSE, FALSE);
-
- pstate->p_last_resno = 1;
- pstate->p_is_rule = true; /* for expand all */
- pstate->p_hasAggs = false;
-
- action = (Query *) lfirst(actions);
- if (action->commandType != CMD_NOTHING)
- lfirst(actions) = transformStmt(pstate, lfirst(actions));
- actions = lnext(actions);
- }
+ foreach(actions, stmt->actions)
+ {
+ ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
+ Query *sub_qry;
+ bool has_old,
+ has_new;
- /* take care of the where clause */
- stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+ /*
+ * Set up OLD/NEW in the rtable for this statement. The entries
+ * are marked not inFromCl because we don't want them to be
+ * referred to by unqualified field names nor "*" in the rule
+ * actions. We don't need to add them to the jointree for
+ * qualified-name lookup, either (see qualifiedNameToVar()).
+ */
+ oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+ makeAttr("*OLD*", NULL),
+ false, false);
+ newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
+ makeAttr("*NEW*", NULL),
+ false, false);
- qry->hasSubLinks = pstate->p_hasSubLinks;
+ /* Transform the rule action statement */
+ sub_qry = transformStmt(sub_pstate, lfirst(actions));
+
+ /*
+ * Validate action's use of OLD/NEW, qual too
+ */
+ has_old =
+ rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
+ rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
+ has_new =
+ rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
+ rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
+
+ switch (stmt->event)
+ {
+ case CMD_SELECT:
+ if (has_old)
+ elog(ERROR, "ON SELECT rule may not use OLD");
+ if (has_new)
+ elog(ERROR, "ON SELECT rule may not use NEW");
+ break;
+ case CMD_UPDATE:
+ /* both are OK */
+ break;
+ case CMD_INSERT:
+ if (has_old)
+ elog(ERROR, "ON INSERT rule may not use OLD");
+ break;
+ case CMD_DELETE:
+ if (has_new)
+ elog(ERROR, "ON DELETE rule may not use NEW");
+ break;
+ default:
+ elog(ERROR, "transformRuleStmt: unexpected event type %d",
+ (int) stmt->event);
+ break;
+ }
+
+ /*
+ * For efficiency's sake, add OLD to the rule action's jointree
+ * only if it was actually referenced in the statement or qual.
+ * NEW is not really a relation and should never be added.
+ */
+ if (has_old)
+ {
+ addRTEtoJoinTree(sub_pstate, oldrte);
+ sub_qry->jointree = sub_pstate->p_jointree;
+ }
+
+ lfirst(actions) = sub_qry;
+
+ release_pstate_resources(sub_pstate);
+ pfree(sub_pstate);
+ }
+ }
- qry->utilityStmt = (Node *) stmt;
return qry;
}
@@ -1558,6 +1657,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->intersectClause = stmt->intersectClause;
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
if (stmt->forUpdate != NULL)
transformForUpdate(qry, stmt->forUpdate);
@@ -1585,17 +1685,17 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
* do this with REPLACE in POSTQUEL so we keep the feature.
*/
makeRangeTable(pstate, stmt->fromClause);
- setTargetTable(pstate, stmt->relname, stmt->inh);
+ setTargetTable(pstate, stmt->relname, stmt->inh, true);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause);
- qry->hasSubLinks = pstate->p_hasSubLinks;
-
qry->rtable = pstate->p_rtable;
+ qry->jointree = pstate->p_jointree;
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+ qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
if (pstate->p_hasAggs)
parseCheckAggregates(pstate, qry);
@@ -1689,7 +1789,7 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
transformColumnType(pstate, (ColumnDef *) stmt->def);
break;
case 'C':
- if (stmt->def && nodeTag(stmt->def) == T_FkConstraint)
+ if (stmt->def && IsA(stmt->def, FkConstraint))
{
CreateTrigStmt *fk_trigger;
List *fk_attr;
@@ -2085,7 +2185,7 @@ transformForUpdate(Query *qry, List *forUpdate)
i++;
}
if (l2 == NULL)
- elog(ERROR, "FOR UPDATE: relation '%s' not found in FROM clause",
+ elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
relname);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7e970ab1871..301be9eb9b9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.188 2000/09/12 05:09:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.189 2000/09/12 21:07:01 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -36,6 +36,7 @@
#include <ctype.h>
#include "postgres.h"
+
#include "access/htup.h"
#include "access/xact.h"
#include "catalog/catname.h"
@@ -77,15 +78,10 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
static Node *makeTypeCast(Node *arg, TypeName *typename);
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
static void mapTargetColumns(List *source, List *target);
-static void param_type_init(Oid *typev, int nargs);
static bool exprIsNullConstant(Node *arg);
static Node *doNegate(Node *n);
static void doNegateFloat(Value *v);
-/* old versions of flex define this as a macro */
-#if defined(yywrap)
-#undef yywrap
-#endif /* yywrap */
%}
@@ -95,6 +91,7 @@ static void doNegateFloat(Value *v);
char chr;
char *str;
bool boolean;
+ JoinType jtype;
List *list;
Node *node;
Value *value;
@@ -108,7 +105,6 @@ static void doNegateFloat(Value *v);
JoinExpr *jexpr;
IndexElem *ielem;
RangeVar *range;
- RelExpr *relexp;
A_Indices *aind;
ResTarget *target;
ParamNo *paramno;
@@ -194,19 +190,8 @@ static void doNegateFloat(Value *v);
%type <boolean> opt_table
%type <boolean> opt_chain, opt_trans
-%type <jexpr> from_expr, join_clause, join_expr
-%type <jexpr> join_clause_with_union, join_expr_with_union
%type <node> join_outer, join_qual
-%type <ival> join_type
-%type <list> using_list
-%type <ident> using_expr
-/***
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
-%type <list> oracle_list
-%type <jexpr> oracle_expr
-%type <boolean> oracle_outer
-#endif
-***/
+%type <jtype> join_type
%type <list> extract_list, position_list
%type <list> substr_list, substr_from, substr_for, trim_list
@@ -246,8 +231,9 @@ static void doNegateFloat(Value *v);
%type <attr> event_object, attr, alias_clause
%type <sortgroupby> sortby
%type <ielem> index_elem, func_index
-%type <range> table_expr
-%type <relexp> relation_expr
+%type <node> table_ref
+%type <jexpr> joined_table
+%type <range> relation_expr
%type <target> target_el, update_target_el
%type <paramno> ParamNo
@@ -356,6 +342,12 @@ static void doNegateFloat(Value *v);
TEMP, TOAST, TRUNCATE, TRUSTED,
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
+/* The grammar thinks these are keywords, but they are not in the keywords.c
+ * list and so can never be entered directly. The filter in parser.c
+ * creates these tokens when required.
+ */
+%token UNIONJOIN
+
/* Special keywords, not in the query language - see the "lex" file */
%token <str> IDENT, FCONST, SCONST, Op
%token <ival> ICONST, PARAM
@@ -364,7 +356,9 @@ static void doNegateFloat(Value *v);
%token OP
/* precedence: lowest to highest */
-%left UNION INTERSECT EXCEPT
+%left UNION EXCEPT
+%left INTERSECT
+%left JOIN UNIONJOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
%left OR
%left AND
%right NOT
@@ -800,7 +794,7 @@ VariableSetStmt: SET ColId TO var_value
n->value = $3;
$$ = (Node *) n;
#else
- elog(ERROR, "SET NAMES is not supported.");
+ elog(ERROR, "SET NAMES is not supported");
#endif
}
;
@@ -1031,7 +1025,6 @@ AlterTableStmt:
n->relname = $3;
$$ = (Node *)n;
}
-
/* ALTER TABLE <name> OWNER TO UserId */
| ALTER TABLE relation_name OWNER TO UserId
{
@@ -2956,7 +2949,7 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb
CreatedbStmt *n;
if ($5 == NULL && $6 == -1)
- elog(ERROR, "CREATE DATABASE WITH requires at least one option.");
+ elog(ERROR, "CREATE DATABASE WITH requires at least one option");
n = makeNode(CreatedbStmt);
n->dbname = $3;
@@ -3465,7 +3458,7 @@ SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
/* This rule parses Select statements that can appear within set operations,
* including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
* the ordering of the set operations. Without '(' and ')' we want the
- * operations to be left associative.
+ * operations to be ordered per the precedence specs at the head of this file.
*
* Note that sort clauses cannot be included at this level --- a sort clause
* can only appear at the end of the complete Select, and it will be handled
@@ -3486,10 +3479,12 @@ select_clause: '(' select_clause ')'
{
$$ = $1;
}
- | select_clause EXCEPT select_clause
+ | select_clause EXCEPT opt_all select_clause
{
$$ = (Node *)makeA_Expr(AND,NULL,$1,
- makeA_Expr(NOT,NULL,NULL,$3));
+ makeA_Expr(NOT,NULL,NULL,$4));
+ if ($3)
+ elog(ERROR, "EXCEPT ALL is not implemented yet");
}
| select_clause UNION opt_all select_clause
{
@@ -3506,9 +3501,11 @@ select_clause: '(' select_clause ')'
}
$$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
}
- | select_clause INTERSECT select_clause
+ | select_clause INTERSECT opt_all select_clause
{
- $$ = (Node *)makeA_Expr(AND,NULL,$1,$3);
+ $$ = (Node *)makeA_Expr(AND,NULL,$1,$4);
+ if ($3)
+ elog(ERROR, "INTERSECT ALL is not implemented yet");
}
;
@@ -3741,113 +3738,63 @@ update_list: OF va_list { $$ = $2; }
*****************************************************************************/
from_clause: FROM from_list { $$ = $2; }
-/***
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
- | FROM oracle_list { $$ = $2; }
-#endif
-***/
- | FROM from_expr { $$ = lcons($2, NIL); }
| /*EMPTY*/ { $$ = NIL; }
;
-from_list: from_list ',' table_expr { $$ = lappend($1, $3); }
- | table_expr { $$ = lcons($1, NIL); }
- ;
-
-/***********
- * This results in one shift/reduce conflict, presumably due to the trailing "(+)"
- * - Thomas 1999-09-20
- *
-#ifdef ENABLE_ORACLE_JOIN_SYNTAX
-oracle_list: oracle_expr { $$ = lcons($1, NIL); }
- ;
-
-oracle_expr: ColId ',' ColId oracle_outer
- {
- elog(ERROR,"Oracle OUTER JOIN not yet supported");
- $$ = NULL;
- }
- | oracle_outer ColId ',' ColId
- {
- elog(ERROR,"Oracle OUTER JOIN not yet supported");
- $$ = NULL;
- }
- ;
-
-oracle_outer: '(' '+' ')' { $$ = TRUE; }
- ;
-#endif
-***********/
-
-from_expr: '(' join_clause_with_union ')' alias_clause
- {
- JoinExpr *j = $2;
- j->alias = $4;
- $$ = j;
- }
- | join_clause
- { $$ = $1; }
- ;
-
-table_expr: relation_expr alias_clause
- {
- $$ = makeNode(RangeVar);
- $$->relExpr = $1;
- $$->name = $2;
-
-#ifdef DISABLE_JOIN_SYNTAX
- if (($2 != NULL) && ($2->attrs != NULL))
- elog(ERROR, "Column aliases in table expressions not yet supported");
-#endif
- }
+from_list: from_list ',' table_ref { $$ = lappend($1, $3); }
+ | table_ref { $$ = lcons($1, NIL); }
;
-alias_clause: AS ColId '(' name_list ')'
- {
- $$ = makeNode(Attr);
- $$->relname = $2;
- $$->attrs = $4;
- }
- | AS ColId
+/*
+ * table_ref is where an alias clause can be attached. Note we cannot make
+ * alias_clause have an empty production because that causes parse conflicts
+ * between table_ref := '(' joined_table ')' alias_clause
+ * and joined_table := '(' joined_table ')'. So, we must have the
+ * redundant-looking productions here instead.
+ */
+table_ref: relation_expr
{
- $$ = makeNode(Attr);
- $$->relname = $2;
+ $$ = (Node *) $1;
}
- | ColId '(' name_list ')'
+ | relation_expr alias_clause
{
- $$ = makeNode(Attr);
- $$->relname = $1;
- $$->attrs = $3;
+ $1->name = $2;
+ $$ = (Node *) $1;
}
- | ColId
+ | '(' select_clause ')'
{
- $$ = makeNode(Attr);
- $$->relname = $1;
+ RangeSubselect *n = makeNode(RangeSubselect);
+ n->subquery = $2;
+ n->name = NULL;
+ $$ = (Node *) n;
}
- | /*EMPTY*/
+ | '(' select_clause ')' alias_clause
{
- $$ = NULL; /* no qualifiers */
+ RangeSubselect *n = makeNode(RangeSubselect);
+ n->subquery = $2;
+ n->name = $4;
+ $$ = (Node *) n;
}
- ;
-
-/* A UNION JOIN is the same as a FULL OUTER JOIN which *omits*
- * all result rows which would have matched on an INNER JOIN.
- * Syntactically, must enclose the UNION JOIN in parens to avoid
- * conflicts with SELECT/UNION.
- */
-join_clause: join_clause join_expr
+ | joined_table
{
- $2->larg = (Node *)$1;
- $$ = $2;
+ $$ = (Node *) $1;
}
- | table_expr join_expr
+ | '(' joined_table ')' alias_clause
{
- $2->larg = (Node *)$1;
- $$ = $2;
+ $2->alias = $4;
+ $$ = (Node *) $2;
}
;
-/* This is everything but the left side of a join.
+/*
+ * It may seem silly to separate joined_table from table_ref, but there is
+ * method in SQL92's madness: if you don't do it this way you get reduce-
+ * reduce conflicts, because it's not clear to the parser generator whether
+ * to expect alias_clause after ')' or not. For the same reason we must
+ * treat 'JOIN' and 'join_type JOIN' separately, rather than allowing
+ * join_type to expand to empty; if we try it, the parser generator can't
+ * figure out when to reduce an empty join_type right after table_ref.
+ *
* Note that a CROSS JOIN is the same as an unqualified
* INNER JOIN, and an INNER JOIN/ON has the same shape
* but a qualification expression to limit membership.
@@ -3855,71 +3802,122 @@ join_clause: join_clause join_expr
* tables and the shape is determined by which columns are
* in common. We'll collect columns during the later transformations.
*/
-join_expr: join_type JOIN table_expr join_qual
+
+joined_table: '(' joined_table ')'
+ {
+ $$ = $2;
+ }
+ | table_ref CROSS JOIN table_ref
{
+ /* CROSS JOIN is same as unqualified inner join */
JoinExpr *n = makeNode(JoinExpr);
- n->jointype = $1;
- n->rarg = (Node *)$3;
- n->quals = (List *)$4;
+ n->jointype = JOIN_INNER;
+ n->isNatural = FALSE;
+ n->larg = $1;
+ n->rarg = $4;
+ n->using = NIL;
+ n->quals = NULL;
$$ = n;
}
- | NATURAL join_type JOIN table_expr
+ | table_ref UNIONJOIN table_ref
{
+ /* UNION JOIN is made into 1 token to avoid shift/reduce
+ * conflict against regular UNION keyword.
+ */
JoinExpr *n = makeNode(JoinExpr);
- n->jointype = $2;
- n->isNatural = TRUE;
- n->rarg = (Node *)$4;
- n->quals = NULL; /* figure out which columns later... */
+ n->jointype = JOIN_UNION;
+ n->isNatural = FALSE;
+ n->larg = $1;
+ n->rarg = $3;
+ n->using = NIL;
+ n->quals = NULL;
$$ = n;
}
- | CROSS JOIN table_expr
+ | table_ref join_type JOIN table_ref join_qual
{
JoinExpr *n = makeNode(JoinExpr);
- n->jointype = INNER_P;
+ n->jointype = $2;
n->isNatural = FALSE;
- n->rarg = (Node *)$3;
- n->quals = NULL;
+ n->larg = $1;
+ n->rarg = $4;
+ if ($5 != NULL && IsA($5, List))
+ n->using = (List *) $5; /* USING clause */
+ else
+ n->quals = $5; /* ON clause */
$$ = n;
}
- ;
-
-join_clause_with_union: join_clause_with_union join_expr_with_union
+ | table_ref JOIN table_ref join_qual
{
- $2->larg = (Node *)$1;
- $$ = $2;
+ /* letting join_type reduce to empty doesn't work */
+ JoinExpr *n = makeNode(JoinExpr);
+ n->jointype = JOIN_INNER;
+ n->isNatural = FALSE;
+ n->larg = $1;
+ n->rarg = $3;
+ if ($4 != NULL && IsA($4, List))
+ n->using = (List *) $4; /* USING clause */
+ else
+ n->quals = $4; /* ON clause */
+ $$ = n;
}
- | table_expr join_expr_with_union
+ | table_ref NATURAL join_type JOIN table_ref
{
- $2->larg = (Node *)$1;
- $$ = $2;
+ JoinExpr *n = makeNode(JoinExpr);
+ n->jointype = $3;
+ n->isNatural = TRUE;
+ n->larg = $1;
+ n->rarg = $5;
+ n->using = NIL; /* figure out which columns later... */
+ n->quals = NULL; /* fill later */
+ $$ = n;
}
- ;
-
-join_expr_with_union: join_expr
- { $$ = $1; }
- | UNION JOIN table_expr
+ | table_ref NATURAL JOIN table_ref
{
+ /* letting join_type reduce to empty doesn't work */
JoinExpr *n = makeNode(JoinExpr);
- n->jointype = UNION;
- n->rarg = (Node *)$3;
- n->quals = NULL;
+ n->jointype = JOIN_INNER;
+ n->isNatural = TRUE;
+ n->larg = $1;
+ n->rarg = $4;
+ n->using = NIL; /* figure out which columns later... */
+ n->quals = NULL; /* fill later */
$$ = n;
+ }
+ ;
- elog(ERROR,"UNION JOIN not yet implemented");
+alias_clause: AS ColId '(' name_list ')'
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $2;
+ $$->attrs = $4;
+ }
+ | AS ColId
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $2;
+ }
+ | ColId '(' name_list ')'
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $1;
+ $$->attrs = $3;
+ }
+ | ColId
+ {
+ $$ = makeNode(Attr);
+ $$->relname = $1;
}
;
-/* OUTER is just noise... */
-join_type: FULL join_outer { $$ = FULL; }
- | LEFT join_outer { $$ = LEFT; }
- | RIGHT join_outer { $$ = RIGHT; }
- | OUTER_P { $$ = LEFT; }
- | INNER_P { $$ = INNER_P; }
- | /*EMPTY*/ { $$ = INNER_P; }
+join_type: FULL join_outer { $$ = JOIN_FULL; }
+ | LEFT join_outer { $$ = JOIN_LEFT; }
+ | RIGHT join_outer { $$ = JOIN_RIGHT; }
+ | INNER_P { $$ = JOIN_INNER; }
;
+/* OUTER is just noise... */
join_outer: OUTER_P { $$ = NULL; }
- | /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
+ | /*EMPTY*/ { $$ = NULL; }
;
/* JOIN qualification clauses
@@ -3927,60 +3925,43 @@ join_outer: OUTER_P { $$ = NULL; }
* USING ( column list ) allows only unqualified column names,
* which must match between tables.
* ON expr allows more general qualifications.
- * - thomas 1999-01-07
+ *
+ * We return USING as a List node, while an ON-expr will not be a List.
*/
-join_qual: USING '(' using_list ')' { $$ = (Node *)$3; }
- | ON a_expr { $$ = (Node *)$2; }
+join_qual: USING '(' name_list ')' { $$ = (Node *) $3; }
+ | ON a_expr { $$ = $2; }
;
-using_list: using_list ',' using_expr { $$ = lappend($1, $3); }
- | using_expr { $$ = lcons($1, NIL); }
- ;
-
-using_expr: ColId
- {
- /* could be a column name or a relation_name */
- Ident *n = makeNode(Ident);
- n->name = $1;
- n->indirection = NULL;
- $$ = n;
- }
- ;
-
-where_clause: WHERE a_expr { $$ = $2; }
- | /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
- ;
relation_expr: relation_name
{
/* default inheritance */
- $$ = makeNode(RelExpr);
+ $$ = makeNode(RangeVar);
$$->relname = $1;
$$->inh = SQL_inheritance;
+ $$->name = NULL;
}
| relation_name '*' %prec '='
{
/* inheritance query */
- $$ = makeNode(RelExpr);
+ $$ = makeNode(RangeVar);
$$->relname = $1;
$$->inh = TRUE;
+ $$->name = NULL;
}
| ONLY relation_name %prec '='
{
/* no inheritance */
- $$ = makeNode(RelExpr);
+ $$ = makeNode(RangeVar);
$$->relname = $2;
$$->inh = FALSE;
+ $$->name = NULL;
}
;
-opt_array_bounds: '[' ']' opt_array_bounds
- { $$ = lcons(makeInteger(-1), $3); }
- | '[' Iconst ']' opt_array_bounds
- { $$ = lcons(makeInteger($2), $4); }
- | /*EMPTY*/
- { $$ = NIL; }
+where_clause: WHERE a_expr { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
;
@@ -4023,6 +4004,14 @@ Typename: SimpleTypename opt_array_bounds
}
;
+opt_array_bounds: '[' ']' opt_array_bounds
+ { $$ = lcons(makeInteger(-1), $3); }
+ | '[' Iconst ']' opt_array_bounds
+ { $$ = lcons(makeInteger($2), $4); }
+ | /*EMPTY*/
+ { $$ = NIL; }
+ ;
+
SimpleTypename: ConstTypename
| ConstInterval
;
@@ -6024,29 +6013,19 @@ xlateSqlType(char *name)
void parser_init(Oid *typev, int nargs)
{
+ saved_relname[0] = '\0';
QueryIsRule = FALSE;
- saved_relname[0]= '\0';
-
- param_type_init(typev, nargs);
-}
-
-
-/*
- * param_type_init()
- *
- * Keep enough information around to fill out the type of param nodes
- * used in postquel functions
- */
-static void
-param_type_init(Oid *typev, int nargs)
-{
- pfunc_num_args = nargs;
+ /*
+ * Keep enough information around to fill out the type of param nodes
+ * used in postquel functions
+ */
param_type_info = typev;
+ pfunc_num_args = nargs;
}
Oid param_type(int t)
{
- if ((t > pfunc_num_args) || (t == 0))
+ if ((t > pfunc_num_args) || (t <= 0))
return InvalidOid;
return param_type_info[t - 1];
}
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index bbc8f5c7076..955be022e4e 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.39 2000/07/17 03:05:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.40 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -152,6 +152,11 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
*/
if (contain_agg_clause(qry->qual))
elog(ERROR, "Aggregates not allowed in WHERE clause");
+ /*
+ * ON-conditions in JOIN expressions are like WHERE clauses.
+ */
+ if (contain_agg_clause((Node *) qry->jointree))
+ elog(ERROR, "Aggregates not allowed in JOIN conditions");
/*
* No aggregates allowed in GROUP BY clauses, either.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 3f874cc9643..c35b41b911b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.65 2000/06/15 03:32:19 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.66 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,7 +18,9 @@
#include "access/heapam.h"
#include "optimizer/tlist.h"
#include "nodes/makefuncs.h"
+#include "parser/analyze.h"
#include "parser/parse.h"
+#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
@@ -33,57 +35,81 @@
static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
+static void extractUniqueColumns(List *common_colnames,
+ List *src_colnames, List *src_colvars,
+ List **res_colnames, List **res_colvars);
+static Node *transformUsingClause(ParseState *pstate,
+ List *leftVars, List *rightVars);
+static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);
+static RangeTblRef *transformRangeSubselect(ParseState *pstate,
+ RangeSubselect *r);
+static Node *transformFromClauseItem(ParseState *pstate, Node *n);
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
List *tlist, int clause);
-static void parseFromClause(ParseState *pstate, List *frmList);
-static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
List *targetlist, char *opname);
static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
-#ifndef DISABLE_OUTER_JOINS
-static List *transformUsingClause(ParseState *pstate, List *using,
- List *left, List *right);
-#endif
-
/*
* makeRangeTable -
* Build the initial range table from the FROM clause.
+ *
+ * The range table constructed here may grow as we transform the expressions
+ * in the query's quals and target list. (Note that this happens because in
+ * POSTQUEL, we allow references to relations not specified in the
+ * from-clause. PostgreSQL keeps this extension to standard SQL.)
+ *
+ * Note: we assume that pstate's p_rtable and p_jointree lists were
+ * initialized to NIL when the pstate was created. We will add onto
+ * any entries already present --- this is needed for rule processing!
*/
void
makeRangeTable(ParseState *pstate, List *frmList)
{
- /* Currently, nothing to do except this: */
- parseFromClause(pstate, frmList);
+ List *fl;
+
+ /*
+ * The grammar will have produced a list of RangeVars, RangeSubselects,
+ * and/or JoinExprs. Transform each one, and then add it to the join tree.
+ */
+ foreach(fl, frmList)
+ {
+ Node *n = lfirst(fl);
+
+ n = transformFromClauseItem(pstate, n);
+ pstate->p_jointree = lappend(pstate->p_jointree, n);
+ }
}
/*
* setTargetTable
- * Add the target relation of INSERT or UPDATE to the range table,
+ * Add the target relation of INSERT/UPDATE/DELETE to the range table,
* and make the special links to it in the ParseState.
*
- * Note that the target is not marked as either inFromCl or inJoinSet.
+ * inJoinSet says whether to add the target to the join tree.
* For INSERT, we don't want the target to be joined to; it's a
* destination of tuples, not a source. For UPDATE/DELETE, we do
- * need to scan or join the target. This will happen without the
- * inJoinSet flag because the planner's preprocess_targetlist()
- * adds the destination's CTID attribute to the targetlist, and
- * therefore the destination will be a referenced table even if
- * there is no other use of any of its attributes. Tricky, eh?
+ * need to scan or join the target.
*/
void
-setTargetTable(ParseState *pstate, char *relname, bool inh)
+setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
{
RangeTblEntry *rte;
/* look for relname only at current nesting level... */
if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
- rte = addRangeTableEntry(pstate, relname,
- makeAttr(relname, NULL),
- inh, FALSE, FALSE);
+ {
+ rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
+ }
else
+ {
rte = refnameRangeTableEntry(pstate, relname);
+ /* XXX what if pre-existing entry has wrong inh setting? */
+ }
+
+ if (inJoinSet)
+ addRTEtoJoinTree(pstate, rte);
/* This could only happen for multi-action rules */
if (pstate->p_target_relation != NULL)
@@ -95,625 +121,500 @@ setTargetTable(ParseState *pstate, char *relname, bool inh)
}
-static Node *
-mergeInnerJoinQuals(ParseState *pstate, Node *clause)
-{
- List *jquals;
-
- foreach(jquals, pstate->p_join_quals)
- {
- Node *jqual = (Node *) lfirst(jquals);
-
- if (clause == NULL)
- clause = jqual;
- else
- {
- A_Expr *a = makeNode(A_Expr);
-
- a->oper = AND;
- a->opname = NULL;
- a->lexpr = clause;
- a->rexpr = jqual;
- clause = (Node *) a;
- }
- }
-
- /* Make sure that we don't add same quals twice... */
- pstate->p_join_quals = NIL;
-
- return clause;
-} /* mergeInnerJoinQuals() */
-
/*
- * transformWhereClause -
- * transforms the qualification and make sure it is of type Boolean
+ * Extract all not-in-common columns from column lists of a source table
*/
-Node *
-transformWhereClause(ParseState *pstate, Node *clause)
-{
- Node *qual;
-
- if (pstate->p_join_quals != NIL)
- clause = mergeInnerJoinQuals(pstate, clause);
-
- if (clause == NULL)
- return NULL;
-
- pstate->p_in_where_clause = true;
- qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
- pstate->p_in_where_clause = false;
-
- if (exprType(qual) != BOOLOID)
- {
- elog(ERROR, "WHERE clause must return type bool, not type %s",
- typeidTypeName(exprType(qual)));
- }
- return qual;
-}
-
-#ifndef DISABLE_JOIN_SYNTAX
-char *
- AttrString(Attr *attr);
-
-char *
-AttrString(Attr *attr)
-{
- Value *val;
-
- Assert(length(attr->attrs) == 1);
-
- val = lfirst(attr->attrs);
-
- Assert(IsA(val, String));
-
- return strVal(val);
-}
-
-List *
- ListTableAsAttrs(ParseState *pstate, char *table);
-List *
-ListTableAsAttrs(ParseState *pstate, char *table)
-{
- Attr *attr = expandTable(pstate, table, TRUE);
- List *rlist = NIL;
- List *col;
-
- foreach(col, attr->attrs)
- {
- Attr *a = makeAttr(table, strVal((Value *) lfirst(col)));
-
- rlist = lappend(rlist, a);
- }
-
- return rlist;
-}
-
-List *
- makeUniqueAttrList(List *candidates, List *idents);
-List *
-makeUniqueAttrList(List *attrs, List *filter)
+static void
+extractUniqueColumns(List *common_colnames,
+ List *src_colnames, List *src_colvars,
+ List **res_colnames, List **res_colvars)
{
- List *result = NULL;
- List *candidate;
+ List *new_colnames = NIL;
+ List *new_colvars = NIL;
+ List *lnames,
+ *lvars = src_colvars;
- foreach(candidate, attrs)
+ foreach(lnames, src_colnames)
{
- List *fmember;
- bool match = FALSE;
- Attr *cattr = lfirst(candidate);
+ char *colname = strVal(lfirst(lnames));
+ bool match = false;
+ List *cnames;
- Assert(IsA(cattr, Attr));
- Assert(length(cattr->attrs) == 1);
-
- foreach(fmember, filter)
+ foreach(cnames, common_colnames)
{
- Attr *fattr = lfirst(fmember);
-
- Assert(IsA(fattr, Attr));
- Assert(length(fattr->attrs) == 1);
+ char *ccolname = strVal(lfirst(cnames));
- if (strcmp(strVal(lfirst(cattr->attrs)), strVal(lfirst(fattr->attrs))) == 0)
+ if (strcmp(colname, ccolname) == 0)
{
- match = TRUE;
+ match = true;
break;
}
}
if (!match)
- result = lappend(result, cattr);
- }
-
- return result;
-}
-
-List *
- makeAttrList(Attr *attr);
-
-List *
-makeAttrList(Attr *attr)
-{
- List *result = NULL;
-
- char *name = attr->relname;
- List *col;
-
- foreach(col, attr->attrs)
- {
- Attr *newattr = makeAttr(name, strVal((Value *) lfirst(col)));
-
- result = lappend(result, newattr);
- }
-
- return result;
-}
-#ifdef NOT_USED
-/* ExpandAttrs()
- * Take an existing attribute node and return a list of attribute nodes
- * with one attribute name per node.
- */
-List *
-ExpandAttrs(Attr *attr)
-{
- List *col;
- char *relname = attr->relname;
- List *rlist = NULL;
-
- Assert(attr != NULL);
-
- if ((attr->attrs == NULL) || (length(attr->attrs) <= 1))
- return lcons(attr, NIL);
-
- foreach(col, attr->attrs)
- {
- Attr *attr = lfirst(col);
+ {
+ new_colnames = lappend(new_colnames, lfirst(lnames));
+ new_colvars = lappend(new_colvars, lfirst(lvars));
+ }
- rlist = lappend(rlist, makeAttr(relname, AttrString(attr)));
+ lvars = lnext(lvars);
}
- return rlist;
+ *res_colnames = new_colnames;
+ *res_colvars = new_colvars;
}
-#endif
/* transformUsingClause()
- * Take an ON or USING clause from a join expression and expand if necessary.
- * Result is an implicitly-ANDed list of untransformed qualification clauses.
+ * Build a complete ON clause from a partially-transformed USING list.
+ * We are given lists of Var nodes representing left and right match columns.
+ * Result is a transformed qualification expression.
*/
-static List *
-transformUsingClause(ParseState *pstate, List *usingList,
- List *leftList, List *rightList)
+static Node *
+transformUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
{
- List *result = NIL;
- List *using;
+ Node *result = NULL;
+ List *lvars,
+ *rvars = rightVars;
- foreach(using, usingList)
+ /*
+ * We cheat a little bit here by building an untransformed operator
+ * tree whose leaves are the already-transformed Vars. This is OK
+ * because transformExpr() won't complain about already-transformed
+ * subnodes.
+ */
+ foreach(lvars, leftVars)
{
- Attr *uattr = lfirst(using);
- Attr *lattr = NULL,
- *rattr = NULL;
- List *col;
+ Node *lvar = (Node *) lfirst(lvars);
+ Node *rvar = (Node *) lfirst(rvars);
A_Expr *e;
- /*
- * find the first instances of this column in the shape list and
- * the last table in the shape list...
- */
- foreach(col, leftList)
- {
- Attr *attr = lfirst(col);
+ e = makeNode(A_Expr);
+ e->oper = OP;
+ e->opname = "=";
+ e->lexpr = copyObject(lvar);
+ e->rexpr = copyObject(rvar);
- if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
- {
- lattr = attr;
- break;
- }
- }
- foreach(col, rightList)
+ if (result == NULL)
+ result = (Node *) e;
+ else
{
- Attr *attr = lfirst(col);
+ A_Expr *a = makeNode(A_Expr);
- if (strcmp(AttrString(attr), AttrString(uattr)) == 0)
- {
- rattr = attr;
- break;
- }
+ a->oper = AND;
+ a->opname = NULL;
+ a->lexpr = result;
+ a->rexpr = (Node *) e;
+ result = (Node *) a;
}
- Assert((lattr != NULL) && (rattr != NULL));
+ rvars = lnext(rvars);
+ }
- e = makeNode(A_Expr);
- e->oper = OP;
- e->opname = "=";
- e->lexpr = (Node *) lattr;
- e->rexpr = (Node *) rattr;
+ result = transformExpr(pstate, result, EXPR_COLUMN_FIRST);
- result = lappend(result, e);
+ if (exprType(result) != BOOLOID)
+ {
+ /* This could only happen if someone defines a funny version of '=' */
+ elog(ERROR, "USING clause must return type bool, not type %s",
+ typeidTypeName(exprType(result)));
}
return result;
} /* transformUsingClause() */
-#endif
-
-static RangeTblEntry *
+/*
+ * transformTableEntry --- transform a RangeVar (simple relation reference)
+ */
+static RangeTblRef *
transformTableEntry(ParseState *pstate, RangeVar *r)
{
- RelExpr *baserel = r->relExpr;
- char *relname = baserel->relname;
-
-#if 0
- char *refname;
- List *columns;
-
-#endif
+ char *relname = r->relname;
RangeTblEntry *rte;
-
-#if 0
- if (r->name != NULL)
- refname = r->name->relname;
- else
- refname = NULL;
-
- columns = ListTableAsAttrs(pstate, relname);
-
- /* alias might be specified... */
- if (r->name != NULL)
- {
-#ifndef DISABLE_JOIN_SYNTAX
- if (length(columns) > 0)
- {
- if (length(r->name->attrs) > 0)
- {
- if (length(columns) != length(r->name->attrs))
- elog(ERROR, "'%s' has %d columns but %d %s specified",
- relname, length(columns), length(r->name->attrs),
- ((length(r->name->attrs) != 1) ? "aliases" : "alias"));
-
- aliasList = nconc(aliasList, r->name->attrs);
- }
- else
- {
- r->name->attrs = columns;
-
- aliasList = nconc(aliasList, r->name->attrs);
- }
- }
- else
- elog(NOTICE, "transformTableEntry: column aliases not handled (internal error)");
-#else
- elog(ERROR, "Column aliases not yet supported");
-#endif
- }
- else
- {
- refname = relname;
- aliasList = nconc(aliasList, columns);
- }
-#endif
-
- if (r->name == NULL)
- r->name = makeAttr(relname, NULL);
+ RangeTblRef *rtr;
/*
- * marks this entry to indicate it comes from the FROM clause. In SQL,
+ * mark this entry to indicate it comes from the FROM clause. In SQL,
* the target list can only refer to range variables specified in the
* from clause but we follow the more powerful POSTQUEL semantics and
* automatically generate the range variable if not specified. However
* there are times we need to know whether the entries are legitimate.
- *
- * eg. select * from foo f where f.x = 1; will generate wrong answer if
- * we expand * to foo.x.
*/
+ rte = addRangeTableEntry(pstate, relname, r->name, r->inh, true);
- rte = addRangeTableEntry(pstate, relname, r->name,
- baserel->inh, TRUE, TRUE);
+ /*
+ * We create a RangeTblRef, but we do not add it to the jointree here.
+ * makeRangeTable will do so, if we are at top level of the FROM clause.
+ */
+ rtr = makeNode(RangeTblRef);
+ /* assume new rte is at end */
+ rtr->rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
- return rte;
-} /* transformTableEntry() */
+ return rtr;
+}
/*
- * parseFromClause -
- * turns the table references specified in the from-clause into a
- * range table. The range table may grow as we transform the expressions
- * in the target list. (Note that this happens because in POSTQUEL, we
- * allow references to relations not specified in the from-clause. We
- * also allow now as an extension.)
- *
- * The FROM clause can now contain JoinExpr nodes, which contain parsing info
- * for inner and outer joins. The USING clause must be expanded into a qualification
- * for an inner join at least, since that is compatible with the old syntax.
- * Not sure yet how to handle outer joins, but it will become clear eventually?
- * - thomas 1998-12-16
+ * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
*/
-static void
-parseFromClause(ParseState *pstate, List *frmList)
+static RangeTblRef *
+transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
{
- List *fl;
+ SelectStmt *subquery = (SelectStmt *) r->subquery;
+ List *parsetrees;
+ Query *query;
- foreach(fl, frmList)
- {
- Node *n = lfirst(fl);
+ /*
+ * subquery node might not be SelectStmt if user wrote something like
+ * FROM (SELECT ... UNION SELECT ...). Our current implementation of
+ * UNION/INTERSECT/EXCEPT is too messy to deal with here, so punt until
+ * we redesign querytrees to make it more reasonable.
+ */
+ if (subquery == NULL || !IsA(subquery, SelectStmt))
+ elog(ERROR, "Set operations not yet supported in subselects in FROM");
- /*
- * marks this entry to indicate it comes from the FROM clause. In
- * SQL, the target list can only refer to range variables
- * specified in the from clause but we follow the more powerful
- * POSTQUEL semantics and automatically generate the range
- * variable if not specified. However there are times we need to
- * know whether the entries are legitimate.
- *
- * eg. select * from foo f where f.x = 1; will generate wrong answer
- * if we expand * to foo.x.
- */
+ /*
+ * Analyze and transform the subquery as if it were an independent
+ * statement (we do NOT want it to see the outer query as a parent).
+ */
+ parsetrees = parse_analyze(lcons(subquery, NIL), NULL);
- /* Plain vanilla inner join, just like we've always had? */
- if (IsA(n, RangeVar))
- transformTableEntry(pstate, (RangeVar *) n);
+ /*
+ * Check that we got something reasonable. Some of these conditions
+ * are probably impossible given restrictions of the grammar, but
+ * check 'em anyway.
+ */
+ if (length(parsetrees) != 1)
+ elog(ERROR, "Unexpected parse analysis result for subselect in FROM");
+ query = (Query *) lfirst(parsetrees);
+ if (query == NULL || !IsA(query, Query))
+ elog(ERROR, "Unexpected parse analysis result for subselect in FROM");
- /* A newfangled join expression? */
- else if (IsA(n, JoinExpr))
- {
-#ifndef DISABLE_JOIN_SYNTAX
- RangeTblEntry *l_rte,
- *r_rte;
- Attr *l_name,
- *r_name = NULL;
- JoinExpr *j = (JoinExpr *) n;
-
- if (j->alias != NULL)
- elog(ERROR, "JOIN table aliases are not supported");
-
- /* nested join? then handle the left one first... */
- if (IsA(j->larg, JoinExpr))
- {
- parseFromClause(pstate, lcons(j->larg, NIL));
- l_name = ((JoinExpr *) j->larg)->alias;
- }
- else
- {
- Assert(IsA(j->larg, RangeVar));
- l_rte = transformTableEntry(pstate, (RangeVar *) j->larg);
- l_name = expandTable(pstate, l_rte->eref->relname, TRUE);
- }
+ if (query->commandType != CMD_SELECT)
+ elog(ERROR, "Expected SELECT query from subselect in FROM");
+ if (query->resultRelation != 0 || query->into != NULL)
+ elog(ERROR, "Subselect in FROM may not have SELECT INTO");
- if (IsA(j->rarg, JoinExpr))
- {
- parseFromClause(pstate, lcons(j->rarg, NIL));
- l_name = ((JoinExpr *) j->larg)->alias;
- }
- else
- {
- Assert(IsA(j->rarg, RangeVar));
- r_rte = transformTableEntry(pstate, (RangeVar *) j->rarg);
- r_name = expandTable(pstate, r_rte->eref->relname, TRUE);
- }
- /*
- * Natural join does not explicitly specify columns; must
- * generate columns to join. Need to run through the list of
- * columns from each table or join result and match up the
- * column names. Use the first table, and check every column
- * in the second table for a match.
- */
- if (j->isNatural)
- {
- List *lx,
- *rx;
- List *rlist = NULL;
+ elog(ERROR, "Subselect in FROM not done yet");
- foreach(lx, l_name->attrs)
- {
- Ident *id = NULL;
- Value *l_col = lfirst(lx);
+ return NULL;
+}
- Assert(IsA(l_col, String));
- foreach(rx, r_name->attrs)
- {
- Value *r_col = lfirst(rx);
+/*
+ * transformFromClauseItem -
+ * Transform a FROM-clause item, adding any required entries to the
+ * range table list being built in the ParseState, and return the
+ * transformed item ready to include in the jointree list.
+ * This routine can recurse to handle SQL92 JOIN expressions.
+ */
+static Node *
+transformFromClauseItem(ParseState *pstate, Node *n)
+{
+ if (IsA(n, RangeVar))
+ {
+ /* Plain relation reference */
+ return (Node *) transformTableEntry(pstate, (RangeVar *) n);
+ }
+ else if (IsA(n, RangeSubselect))
+ {
+ /* Plain relation reference */
+ return (Node *) transformRangeSubselect(pstate, (RangeSubselect *) n);
+ }
+ else if (IsA(n, JoinExpr))
+ {
+ /* A newfangled join expression */
+ JoinExpr *j = (JoinExpr *) n;
+ List *l_colnames,
+ *r_colnames,
+ *res_colnames,
+ *l_colvars,
+ *r_colvars,
+ *res_colvars;
- Assert(IsA(r_col, String));
+ /*
+ * Recursively process the left and right subtrees
+ */
+ j->larg = transformFromClauseItem(pstate, j->larg);
+ j->rarg = transformFromClauseItem(pstate, j->rarg);
- if (strcmp(strVal(l_col), strVal(r_col)) == 0)
- {
- id = (Ident *) makeNode(Ident);
- id->name = strVal(l_col);
- break;
- }
- }
+ /*
+ * Extract column name and var lists from both subtrees
+ */
+ if (IsA(j->larg, JoinExpr))
+ {
+ /* Make a copy of the subtree's lists so we can modify! */
+ l_colnames = copyObject(((JoinExpr *) j->larg)->colnames);
+ l_colvars = copyObject(((JoinExpr *) j->larg)->colvars);
+ }
+ else
+ {
+ RangeTblEntry *rte;
- /* right column matched? then keep as join column... */
- if (id != NULL)
- rlist = lappend(rlist, id);
- }
- j->quals = rlist;
+ Assert(IsA(j->larg, RangeTblRef));
+ rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex,
+ pstate->p_rtable);
+ expandRTE(pstate, rte, &l_colnames, &l_colvars);
+ /* expandRTE returns new lists, so no need for copyObject */
+ }
+ if (IsA(j->rarg, JoinExpr))
+ {
+ /* Make a copy of the subtree's lists so we can modify! */
+ r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames);
+ r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars);
+ }
+ else
+ {
+ RangeTblEntry *rte;
- printf("NATURAL JOIN columns are %s\n", nodeToString(rlist));
- }
+ Assert(IsA(j->rarg, RangeTblRef));
+ rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex,
+ pstate->p_rtable);
+ expandRTE(pstate, rte, &r_colnames, &r_colvars);
+ /* expandRTE returns new lists, so no need for copyObject */
+ }
- if (j->jointype == INNER_P)
+ /*
+ * Natural join does not explicitly specify columns; must
+ * generate columns to join. Need to run through the list of
+ * columns from each table or join result and match up the
+ * column names. Use the first table, and check every column
+ * in the second table for a match. (We'll check that the
+ * matches were unique later on.)
+ * The result of this step is a list of column names just like an
+ * explicitly-written USING list.
+ */
+ if (j->isNatural)
+ {
+ List *rlist = NIL;
+ List *lx,
+ *rx;
+
+ Assert(j->using == NIL); /* shouldn't have USING() too */
+
+ foreach(lx, l_colnames)
{
- /* CROSS JOIN */
- if (j->quals == NULL)
- printf("CROSS JOIN...\n");
+ char *l_colname = strVal(lfirst(lx));
+ Value *m_name = NULL;
- /*
- * JOIN/USING This is an inner join, so rip apart the join
- * node and transform into a traditional FROM list.
- * NATURAL JOIN and JOIN USING both change the shape of
- * the result. Need to generate a list of result columns
- * to use for target list expansion and validation.
- */
- else if (IsA(j->quals, List))
+ foreach(rx, r_colnames)
{
+ char *r_colname = strVal(lfirst(rx));
- /*
- * List of Ident nodes means column names from a real
- * USING clause. Determine the shape of the joined
- * table.
- */
- List *ucols,
- *ucol;
- List *shape = NULL;
- List *alias = NULL;
- List *l_shape,
- *r_shape;
-
- List *l_cols = makeAttrList(l_name);
- List *r_cols = makeAttrList(r_name);
-
- printf("USING input tables are:\n %s\n %s\n",
- nodeToString(l_name), nodeToString(r_name));
-
- printf("USING expanded tables are:\n %s\n %s\n",
- nodeToString(l_cols), nodeToString(r_cols));
-
- /* Columns from the USING clause... */
- ucols = (List *) j->quals;
- foreach(ucol, ucols)
+ if (strcmp(l_colname, r_colname) == 0)
{
- List *col;
- Attr *l_attr = NULL,
- *r_attr = NULL;
- Ident *id = lfirst(ucol);
-
- Attr *attr = makeAttr("", id->name);
-
- foreach(col, l_cols)
- {
- attr = lfirst(col);
- if (strcmp(AttrString(attr), id->name) == 0)
- {
- l_attr = attr;
- break;
- }
- }
-
- foreach(col, r_cols)
- {
- attr = lfirst(col);
- if (strcmp(AttrString(attr), id->name) == 0)
- {
- r_attr = attr;
- break;
- }
- }
-
- if (l_attr == NULL)
- elog(ERROR, "USING column '%s' not found in table '%s'",
- id->name, l_name->relname);
- if (r_attr == NULL)
- elog(ERROR, "USING column '%s' not found in table '%s'",
- id->name, r_name->relname);
-
- shape = lappend(shape, l_attr);
- alias = lappend(alias, makeAttr("", AttrString(l_attr)));
+ m_name = makeString(l_colname);
+ break;
}
- printf("JOIN/USING join columns are %s\n", nodeToString(shape));
-
- /* Remaining columns from the left side... */
- l_shape = makeUniqueAttrList(makeAttrList(l_name), shape);
-
- printf("JOIN/USING left columns are %s\n", nodeToString(l_shape));
-
- r_shape = makeUniqueAttrList(makeAttrList(r_name), shape);
+ }
- printf("JOIN/USING right columns are %s\n", nodeToString(r_shape));
+ /* matched a right column? then keep as join column... */
+ if (m_name != NULL)
+ rlist = lappend(rlist, m_name);
+ }
- printf("JOIN/USING input quals are %s\n", nodeToString(j->quals));
+ j->using = rlist;
+ }
- j->quals = transformUsingClause(pstate, shape, l_cols, r_cols);
+ /*
+ * Now transform the join qualifications, if any.
+ */
+ res_colnames = NIL;
+ res_colvars = NIL;
- printf("JOIN/USING transformed quals are %s\n", nodeToString(j->quals));
+ if (j->using)
+ {
+ /*
+ * JOIN/USING (or NATURAL JOIN, as transformed above).
+ * Transform the list into an explicit ON-condition,
+ * and generate a list of result columns.
+ */
+ List *ucols = j->using;
+ List *l_usingvars = NIL;
+ List *r_usingvars = NIL;
+ List *ucol;
- alias = nconc(nconc(alias, listCopy(l_shape)), listCopy(r_shape));
- shape = nconc(nconc(shape, l_shape), r_shape);
+ Assert(j->quals == NULL); /* shouldn't have ON() too */
- printf("JOIN/USING shaped table is %s\n", nodeToString(shape));
- printf("JOIN/USING alias list is %s\n", nodeToString(alias));
+ foreach(ucol, ucols)
+ {
+ char *u_colname = strVal(lfirst(ucol));
+ List *col;
+ Node *l_colvar,
+ *r_colvar,
+ *colvar;
+ int ndx;
+ int l_index = -1;
+ int r_index = -1;
+
+ ndx = 0;
+ foreach(col, l_colnames)
+ {
+ char *l_colname = strVal(lfirst(col));
- pstate->p_shape = shape;
- pstate->p_alias = alias;
+ if (strcmp(l_colname, u_colname) == 0)
+ {
+ if (l_index >= 0)
+ elog(ERROR, "Common column name \"%s\" appears more than once in left table", u_colname);
+ l_index = ndx;
+ }
+ ndx++;
}
+ if (l_index < 0)
+ elog(ERROR, "USING column \"%s\" not found in left table",
+ u_colname);
- /* otherwise, must be an expression from an ON clause... */
- else
- j->quals = (List *) lcons(j->quals, NIL);
-
- /* listCopy may not be needed here --- will j->quals list
- * be used again anywhere? The #ifdef'd code below may need
- * it, if it ever gets used...
- */
- pstate->p_join_quals = nconc(pstate->p_join_quals,
- listCopy(j->quals));
-
-#if 0
- if (qual == NULL)
- elog(ERROR, "JOIN/ON not supported in this context");
-
- printf("Table aliases are %s\n", nodeToString(*aliasList));
-#endif
-
-#if 0
- /* XXX this code is WRONG because j->quals is a List
- * not a simple expression. Perhaps *qual
- * ought also to be a List and we append to it,
- * similarly to the way p_join_quals is handled above?
- */
- if (*qual == NULL)
+ ndx = 0;
+ foreach(col, r_colnames)
{
- /* merge qualified join clauses... */
- if (j->quals != NULL)
+ char *r_colname = strVal(lfirst(col));
+
+ if (strcmp(r_colname, u_colname) == 0)
{
- if (*qual != NULL)
- {
- A_Expr *a = makeNode(A_Expr);
-
- a->oper = AND;
- a->opname = NULL;
- a->lexpr = (Node *) *qual;
- a->rexpr = (Node *) j->quals;
-
- *qual = (Node *) a;
- }
- else
- *qual = (Node *) j->quals;
+ if (r_index >= 0)
+ elog(ERROR, "Common column name \"%s\" appears more than once in right table", u_colname);
+ r_index = ndx;
}
+ ndx++;
}
- else
+ if (r_index < 0)
+ elog(ERROR, "USING column \"%s\" not found in right table",
+ u_colname);
+
+ l_colvar = nth(l_index, l_colvars);
+ l_usingvars = lappend(l_usingvars, l_colvar);
+ r_colvar = nth(r_index, r_colvars);
+ r_usingvars = lappend(r_usingvars, r_colvar);
+
+ res_colnames = lappend(res_colnames,
+ nth(l_index, l_colnames));
+ switch (j->jointype)
{
- elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)");
- *qual = lappend(*qual, j->quals);
+ case JOIN_INNER:
+ case JOIN_LEFT:
+ colvar = l_colvar;
+ break;
+ case JOIN_RIGHT:
+ colvar = r_colvar;
+ break;
+ default:
+ {
+ /* Need COALESCE(l_colvar, r_colvar) */
+ CaseExpr *c = makeNode(CaseExpr);
+ CaseWhen *w = makeNode(CaseWhen);
+ A_Expr *a = makeNode(A_Expr);
+
+ a->oper = NOTNULL;
+ a->lexpr = l_colvar;
+ w->expr = (Node *) a;
+ w->result = l_colvar;
+ c->args = lcons(w, NIL);
+ c->defresult = r_colvar;
+ colvar = transformExpr(pstate, (Node *) c,
+ EXPR_COLUMN_FIRST);
+ break;
+ }
}
-#endif
-
- /*
- * if we are transforming this node back into a FROM list,
- * then we will need to replace the node with two nodes.
- * Will need access to the previous list item to change
- * the link pointer to reference these new nodes. Try
- * accumulating and returning a new list. - thomas
- * 1999-01-08 Not doing this yet though!
- */
+ res_colvars = lappend(res_colvars, colvar);
+ }
+ j->quals = transformUsingClause(pstate, l_usingvars, r_usingvars);
+ }
+ else if (j->quals)
+ {
+ /* User-written ON-condition; transform it */
+ j->quals = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
+ if (exprType(j->quals) != BOOLOID)
+ {
+ elog(ERROR, "ON clause must return type bool, not type %s",
+ typeidTypeName(exprType(j->quals)));
}
- else if ((j->jointype == LEFT)
- || (j->jointype == RIGHT)
- || (j->jointype == FULL))
- elog(ERROR, "OUTER JOIN is not yet supported");
- else
- elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)",
- j->jointype);
-#else
- elog(ERROR, "JOIN expressions are not yet implemented");
-#endif
+ /* XXX should check that ON clause refers only to joined tbls */
}
else
- elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)"
- "\n\t%s", nodeToString(n));
+ {
+ /* CROSS JOIN: no quals */
+ }
+
+ /* Add remaining columns from each side to the output columns */
+ extractUniqueColumns(res_colnames,
+ l_colnames, l_colvars,
+ &l_colnames, &l_colvars);
+ extractUniqueColumns(res_colnames,
+ r_colnames, r_colvars,
+ &r_colnames, &r_colvars);
+ res_colnames = nconc(res_colnames, l_colnames);
+ res_colvars = nconc(res_colvars, l_colvars);
+ res_colnames = nconc(res_colnames, r_colnames);
+ res_colvars = nconc(res_colvars, r_colvars);
+
+ /*
+ * Process alias (AS clause), if any.
+ *
+ * The given table alias must be unique in the current nesting level,
+ * ie it cannot match any RTE refname or jointable alias. This is
+ * a bit painful to check because my own child joins are not yet in
+ * the pstate's jointree, so they have to be scanned separately.
+ */
+ if (j->alias)
+ {
+ /* Check against previously created RTEs and jointree entries */
+ if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL))
+ elog(ERROR, "Table name \"%s\" specified more than once",
+ j->alias->relname);
+ /* Check children */
+ if (scanJoinTreeForRefname(j->larg, j->alias->relname) ||
+ scanJoinTreeForRefname(j->rarg, j->alias->relname))
+ elog(ERROR, "Table name \"%s\" specified more than once",
+ j->alias->relname);
+ /*
+ * If a column alias list is specified, substitute the alias
+ * names into my output-column list
+ */
+ if (j->alias->attrs != NIL)
+ {
+ if (length(j->alias->attrs) != length(res_colnames))
+ elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)",
+ j->alias->relname, length(res_colnames));
+ res_colnames = j->alias->attrs;
+ }
+ }
+
+ j->colnames = res_colnames;
+ j->colvars = res_colvars;
+
+ return (Node *) j;
+ }
+ else
+ elog(ERROR, "transformFromClauseItem: unexpected node (internal error)"
+ "\n\t%s", nodeToString(n));
+ return NULL; /* can't get here, just keep compiler quiet */
+}
+
+
+/*
+ * transformWhereClause -
+ * transforms the qualification and make sure it is of type Boolean
+ */
+Node *
+transformWhereClause(ParseState *pstate, Node *clause)
+{
+ Node *qual;
+
+ if (clause == NULL)
+ return NULL;
+
+ qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST);
+
+ if (exprType(qual) != BOOLOID)
+ {
+ elog(ERROR, "WHERE clause must return type bool, not type %s",
+ typeidTypeName(exprType(qual)));
}
-} /* parseFromClause() */
+ return qual;
+}
/*
@@ -786,10 +687,10 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
* is a matching column. If so, fall through to let
* transformExpr() do the rest. NOTE: if name could refer
* ambiguously to more than one column name exposed by FROM,
- * colnameRangeTableEntry will elog(ERROR). That's just what
+ * colnameToVar will elog(ERROR). That's just what
* we want here.
*/
- if (colnameRangeTableEntry(pstate, name) != NULL)
+ if (colnameToVar(pstate, name) != NULL)
name = NULL;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7976f5e7795..a033ff4be22 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.82 2000/08/08 15:42:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -157,41 +157,51 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
{
case OP:
{
- Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
- Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr,
+ precedence);
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr,
+ precedence);
result = (Node *) make_op(a->opname, lexpr, rexpr);
}
break;
case ISNULL:
{
- Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr,
+ precedence);
result = ParseFuncOrColumn(pstate,
"nullvalue",
lcons(lexpr, NIL),
false, false,
- &pstate->p_last_resno,
precedence);
}
break;
case NOTNULL:
{
- Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr,
+ precedence);
result = ParseFuncOrColumn(pstate,
"nonnullvalue",
lcons(lexpr, NIL),
false, false,
- &pstate->p_last_resno,
precedence);
}
break;
case AND:
{
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr,
+ precedence);
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr,
+ precedence);
Expr *expr = makeNode(Expr);
- Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
- Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(lexpr) != BOOLOID)
elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
@@ -209,9 +219,13 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
case OR:
{
+ Node *lexpr = transformExpr(pstate,
+ a->lexpr,
+ precedence);
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr,
+ precedence);
Expr *expr = makeNode(Expr);
- Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
- Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(lexpr) != BOOLOID)
elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
@@ -227,8 +241,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
case NOT:
{
+ Node *rexpr = transformExpr(pstate,
+ a->rexpr,
+ precedence);
Expr *expr = makeNode(Expr);
- Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
if (exprType(rexpr) != BOOLOID)
elog(ERROR, "argument to NOT is type '%s', not '%s'",
@@ -254,13 +270,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
/* transform the list of arguments */
foreach(args, fn->args)
- lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
+ lfirst(args) = transformExpr(pstate,
+ (Node *) lfirst(args),
+ precedence);
result = ParseFuncOrColumn(pstate,
fn->funcname,
fn->args,
fn->agg_star,
fn->agg_distinct,
- &pstate->p_last_resno,
precedence);
break;
}
@@ -609,8 +626,7 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
{
Node *basenode;
- basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
- precedence);
+ basenode = ParseNestedFuncOrColumn(pstate, att, precedence);
return transformIndirection(pstate, basenode, att->indirection);
}
@@ -618,7 +634,6 @@ static Node *
transformIdent(ParseState *pstate, Ident *ident, int precedence)
{
Node *result = NULL;
- RangeTblEntry *rte;
/*
* try to find the ident as a relation ... but not if subscripts
@@ -634,14 +649,10 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
if (result == NULL || precedence == EXPR_COLUMN_FIRST)
{
/* try to find the ident as a column */
- if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
- {
- /* Convert it to a fully qualified Attr, and transform that */
- Attr *att = makeAttr(rte->eref->relname, ident->name);
-
- att->indirection = ident->indirection;
- return transformAttr(pstate, att, precedence);
- }
+ Node *var = colnameToVar(pstate, ident->name);
+
+ if (var != NULL)
+ result = transformIndirection(pstate, var, ident->indirection);
}
if (result == NULL)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index bad9401c609..1f19b1b949e 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.89 2000/08/24 03:29:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -64,19 +64,20 @@ static Oid agg_select_candidate(Oid typeid, CandidateList candidates);
** a tree with of Iter and Func nodes.
*/
Node *
-ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int precedence)
+ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
{
List *mutator_iter;
Node *retval = NULL;
if (attr->paramNo != NULL)
{
- Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST);
+ Param *param = (Param *) transformExpr(pstate,
+ (Node *) attr->paramNo,
+ EXPR_RELATION_FIRST);
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(param, NIL),
false, false,
- curr_resno,
precedence);
}
else
@@ -88,7 +89,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
lcons(ident, NIL),
false, false,
- curr_resno,
precedence);
}
@@ -98,7 +98,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
lcons(retval, NIL),
false, false,
- curr_resno,
precedence);
}
@@ -241,17 +240,15 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
Node *
ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
bool agg_star, bool agg_distinct,
- int *curr_resno, int precedence)
+ int precedence)
{
Oid rettype = InvalidOid;
Oid argrelid = InvalidOid;
Oid funcid = InvalidOid;
List *i = NIL;
Node *first_arg = NULL;
- char *relname = NULL;
- char *refname = NULL;
+ char *refname;
Relation rd;
- Oid relid;
int nargs = length(fargs);
Func *funcnode;
Oid oid_array[FUNC_MAX_ARGS];
@@ -283,81 +280,17 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
{
Ident *ident = (Ident *) first_arg;
- RangeTblEntry *rte;
- AttrNumber attnum;
/*
* first arg is a relation. This could be a projection.
*/
refname = ident->name;
- rte = refnameRangeTableEntry(pstate, refname);
- if (rte == NULL)
- {
- rte = addRangeTableEntry(pstate, refname,
- makeAttr(refname, NULL),
- FALSE, FALSE, TRUE);
- warnAutoRange(pstate, refname);
- }
-
- relname = rte->relname;
- relid = rte->relid;
- attnum = InvalidAttrNumber;
-
- /*
- * If the attr isn't a set, just make a var for it. If it is
- * a set, treat it like a function and drop through. Look
- * through the explicit column list first, since we now allow
- * column aliases. - thomas 2000-02-07
- */
- if (rte->eref->attrs != NULL)
- {
- List *c;
-
- /*
- * start counting attributes/columns from one. zero is
- * reserved for InvalidAttrNumber. - thomas 2000-01-27
- */
- int i = 1;
-
- foreach(c, rte->eref->attrs)
- {
- char *colname = strVal(lfirst(c));
-
- /* found a match? */
- if (strcmp(colname, funcname) == 0)
- {
- char *basename = get_attname(relid, i);
-
- if (basename != NULL)
- {
- funcname = basename;
- attnum = i;
- }
-
- /*
- * attnum was initialized to InvalidAttrNumber
- * earlier, so no need to reset it if the above
- * test fails. - thomas 2000-02-07
- */
- break;
- }
- i++;
- }
- if (attnum == InvalidAttrNumber)
- attnum = specialAttNum(funcname);
- }
- else
- attnum = get_attnum(relid, funcname);
+ retval = qualifiedNameToVar(pstate, refname, funcname, true);
+ if (retval)
+ return retval;
- if (attnum != InvalidAttrNumber)
- {
- return (Node *) make_var(pstate,
- relid,
- refname,
- funcname);
- }
- /* else drop through - attr is a set */
+ /* else drop through - attr is a set or function */
}
else if (ISCOMPLEX(exprType(first_arg)))
{
@@ -376,10 +309,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
toid = exprType(first_arg);
rd = heap_openr_nofail(typeidTypeName(toid));
if (RelationIsValid(rd))
- {
- relname = RelationGetRelationName(rd);
heap_close(rd, NoLock);
- }
else
elog(ERROR, "Type '%s' is not a relation type",
typeidTypeName(toid));
@@ -506,17 +436,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
rte = refnameRangeTableEntry(pstate, refname);
if (rte == NULL)
- {
- rte = addRangeTableEntry(pstate, refname,
- makeAttr(refname, NULL),
- FALSE, FALSE, TRUE);
- warnAutoRange(pstate, refname);
- }
-
- relname = rte->relname;
+ rte = addImplicitRTE(pstate, refname);
- vnum = refnameRangeTablePosn(pstate, rte->eref->relname,
- &sublevels_up);
+ vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
/*
* for func(relname), the param to the function is the tuple
@@ -525,7 +447,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
* but has varattno == 0 to signal that the whole tuple is the
* argument.
*/
- toid = typeTypeId(typenameType(relname));
+ toid = typeTypeId(typenameType(rte->relname));
+
/* replace it in the arg list */
lfirst(i) = makeVar(vnum, 0, toid, -1, sublevels_up);
}
@@ -666,16 +589,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
- /*
- * Special checks to disallow sequence functions with side-effects
- * in WHERE clauses. This is pretty much of a hack; why disallow these
- * when we have no way to check for side-effects of user-defined fns?
- */
- if (funcid == F_NEXTVAL && pstate->p_in_where_clause)
- elog(ERROR, "Sequence function nextval is not allowed in WHERE clauses");
- if (funcid == F_SETVAL && pstate->p_in_where_clause)
- elog(ERROR, "Sequence function setval is not allowed in WHERE clauses");
-
expr = makeNode(Expr);
expr->typeOid = rettype;
expr->opType = FUNC_EXPR;
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 5d363ea3e69..85a56067bd2 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.45 2000/08/24 03:29:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,8 +49,8 @@ make_parsestate(ParseState *parentParseState)
pstate = palloc(sizeof(ParseState));
MemSet(pstate, 0, sizeof(ParseState));
- pstate->p_last_resno = 1;
pstate->parentParseState = parentParseState;
+ pstate->p_last_resno = 1;
return pstate;
}
@@ -164,35 +164,44 @@ make_op(char *opname, Node *ltree, Node *rtree)
/*
* make_var
- * Build a Var node for an attribute identified by name
+ * Build a Var node for an attribute identified by RTE and attrno
*/
Var *
-make_var(ParseState *pstate, Oid relid, char *refname,
- char *attrname)
+make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
{
- HeapTuple tp;
- Form_pg_attribute att_tup;
int vnum,
- attid;
+ sublevels_up;
Oid vartypeid;
int32 type_mod;
- int sublevels_up;
-
- vnum = refnameRangeTablePosn(pstate, refname, &sublevels_up);
-
- tp = SearchSysCacheTuple(ATTNAME,
- ObjectIdGetDatum(relid),
- PointerGetDatum(attrname),
- 0, 0);
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "Relation %s does not have attribute %s",
- refname, attrname);
- att_tup = (Form_pg_attribute) GETSTRUCT(tp);
- attid = att_tup->attnum;
- vartypeid = att_tup->atttypid;
- type_mod = att_tup->atttypmod;
-
- return makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
+
+ vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+ if (rte->relid != InvalidOid)
+ {
+ /* Plain relation RTE --- get the attribute's type info */
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCacheTuple(ATTNUM,
+ ObjectIdGetDatum(rte->relid),
+ Int16GetDatum(attrno),
+ 0, 0);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation %s does not have attribute %d",
+ rte->relname, attrno);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ vartypeid = att_tup->atttypid;
+ type_mod = att_tup->atttypmod;
+ }
+ else
+ {
+ /* Subselect RTE --- get type info from subselect's tlist */
+ elog(ERROR, "make_var: subselect in FROM not implemented yet");
+ vartypeid = type_mod = 0;
+ }
+
+ return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
}
/*
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 802299c8966..491cbc5ef08 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.46 2000/08/08 15:42:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.47 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,14 +20,25 @@
#include "access/htup.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+ char *colname);
+static Node *scanJoinForColumn(JoinExpr *join, char *colname,
+ int sublevels_up);
+static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
+static void warnAutoRange(ParseState *pstate, char *refname);
+
+
/*
* Information defining the "system" attributes of every relation.
*/
@@ -65,40 +76,96 @@ static struct
#define SPECIALS ((int) (sizeof(special_attr)/sizeof(special_attr[0])))
-#ifdef NOT_USED
-/* refnameRangeTableEntries()
- * Given refname, return a list of range table entries
- * This is possible with JOIN syntax, where tables in a join
- * acquire the same reference name.
- * - thomas 2000-01-20
- * But at the moment we aren't carrying along a full list of
- * table/column aliases, so we don't have the full mechanism
- * to support outer joins in place yet.
- * - thomas 2000-03-04
+/*
+ * refnameRangeOrJoinEntry
+ * Given a refname, look to see if it matches any RTE or join table.
+ * If so, return a pointer to the RangeTblEntry or JoinExpr.
+ * Optionally get its nesting depth (0 = current). If sublevels_up
+ * is NULL, only consider items at the current nesting level.
*/
-
-static List *
-refnameRangeTableEntries(ParseState *pstate, char *refname)
+Node *
+refnameRangeOrJoinEntry(ParseState *pstate,
+ char *refname,
+ int *sublevels_up)
{
- List *rteList = NULL;
- List *temp;
+ if (sublevels_up)
+ *sublevels_up = 0;
while (pstate != NULL)
{
+ List *temp;
+ JoinExpr *join;
+
+ /*
+ * Check the rangetable for RTEs; if no match, recursively scan
+ * the jointree for join tables. We assume that no duplicate
+ * entries have been made in any one nesting level.
+ */
foreach(temp, pstate->p_rtable)
{
RangeTblEntry *rte = lfirst(temp);
if (strcmp(rte->eref->relname, refname) == 0)
- rteList = lappend(rteList, rte);
+ return (Node *) rte;
}
+
+ join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
+ if (join)
+ return (Node *) join;
+
pstate = pstate->parentParseState;
+ if (sublevels_up)
+ (*sublevels_up)++;
+ else
+ break;
}
- return rteList;
+ return NULL;
}
-#endif
-/* given refname, return a pointer to the range table entry */
+/* Recursively search a jointree for a joinexpr with given refname */
+JoinExpr *
+scanJoinTreeForRefname(Node *jtnode, char *refname)
+{
+ JoinExpr *result = NULL;
+
+ if (jtnode == NULL)
+ return NULL;
+ if (IsA(jtnode, List))
+ {
+ List *l;
+
+ foreach(l, (List *) jtnode)
+ {
+ result = scanJoinTreeForRefname(lfirst(l), refname);
+ if (result)
+ break;
+ }
+ }
+ else if (IsA(jtnode, RangeTblRef))
+ {
+ /* ignore ... */
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ if (j->alias && strcmp(j->alias->relname, refname) == 0)
+ return j;
+ result = scanJoinTreeForRefname(j->larg, refname);
+ if (! result)
+ result = scanJoinTreeForRefname(j->rarg, refname);
+ }
+ else
+ elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
+ nodeTag(jtnode));
+ return result;
+}
+
+/*
+ * given refname, return a pointer to the range table entry.
+ *
+ * NOTE that this routine will ONLY find RTEs, not join tables.
+ */
RangeTblEntry *
refnameRangeTableEntry(ParseState *pstate, char *refname)
{
@@ -118,9 +185,13 @@ refnameRangeTableEntry(ParseState *pstate, char *refname)
return NULL;
}
-/* given refname, return RT index (starting with 1) of the relation,
+/*
+ * given refname, return RT index (starting with 1) of the relation,
* and optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider rels at the current nesting level.
+ * A zero result means name not found.
+ *
+ * NOTE that this routine will ONLY find RTEs, not join tables.
*/
int
refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
@@ -152,114 +223,264 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
}
/*
- * returns range entry if found, else NULL
+ * given an RTE, return RT index (starting with 1) of the entry,
+ * and optionally get its nesting depth (0 = current). If sublevels_up
+ * is NULL, only consider rels at the current nesting level.
+ * Raises error if RTE not found.
*/
-RangeTblEntry *
-colnameRangeTableEntry(ParseState *pstate, char *colname)
+int
+RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
{
- List *et;
- List *rtable;
- RangeTblEntry *rte_result = NULL;
+ int index;
+ List *temp;
+
+ if (sublevels_up)
+ *sublevels_up = 0;
while (pstate != NULL)
{
- if (pstate->p_is_rule)
- rtable = lnext(lnext(pstate->p_rtable));
+ index = 1;
+ foreach(temp, pstate->p_rtable)
+ {
+ if (rte == (RangeTblEntry *) lfirst(temp))
+ return index;
+ index++;
+ }
+ pstate = pstate->parentParseState;
+ if (sublevels_up)
+ (*sublevels_up)++;
else
- rtable = pstate->p_rtable;
+ break;
+ }
+ elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
+ return 0; /* keep compiler quiet */
+}
+
+/*
+ * scanRTEForColumn
+ * Search the column names of a single RTE for the given name.
+ * If found, return an appropriate Var node, else return NULL.
+ * If the name proves ambiguous within this RTE, raise error.
+ */
+static Node *
+scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
+{
+ Node *result = NULL;
+ int attnum = 0;
+ List *c;
- foreach(et, rtable)
+ /*
+ * Scan the user column names (or aliases) for a match.
+ * Complain if multiple matches.
+ */
+ foreach(c, rte->eref->attrs)
+ {
+ attnum++;
+ if (strcmp(strVal(lfirst(c)), colname) == 0)
{
- RangeTblEntry *rte_candidate = NULL;
- RangeTblEntry *rte = lfirst(et);
+ if (result)
+ elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
+ result = (Node *) make_var(pstate, rte, attnum);
+ }
+ }
- /* only consider RTEs mentioned in FROM or UPDATE/DELETE */
- if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
- continue;
+ /*
+ * If we have a unique match, return it. Note that this allows a user
+ * alias to override a system column name (such as OID) without error.
+ */
+ if (result)
+ return result;
- if (rte->eref->attrs != NULL)
- {
- List *c;
-
- foreach(c, rte->ref->attrs)
- {
- if (strcmp(strVal(lfirst(c)), colname) == 0)
- {
- if (rte_candidate != NULL)
- elog(ERROR, "Column '%s' is ambiguous"
- " (internal error)", colname);
- rte_candidate = rte;
- }
- }
- }
+ /*
+ * If the RTE represents a table (not a sub-select), consider system
+ * column names.
+ */
+ if (rte->relid != InvalidOid)
+ {
+ attnum = specialAttNum(colname);
+ if (attnum != InvalidAttrNumber)
+ result = (Node *) make_var(pstate, rte, attnum);
+ }
+
+ return result;
+}
+/*
+ * scanJoinForColumn
+ * Search the column names of a single join table for the given name.
+ * If found, return an appropriate Var node or expression, else return NULL.
+ * If the name proves ambiguous within this jointable, raise error.
+ */
+static Node *
+scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
+{
+ Node *result = NULL;
+ int attnum = 0;
+ List *c;
+
+ foreach(c, join->colnames)
+ {
+ attnum++;
+ if (strcmp(strVal(lfirst(c)), colname) == 0)
+ {
+ if (result)
+ elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
+ result = copyObject(nth(attnum-1, join->colvars));
/*
- * Even if we have an attribute list in the RTE, look for the
- * column here anyway. This is the only way we will find
- * implicit columns like "oid". - thomas 2000-02-07
+ * If referencing an uplevel join item, we must adjust
+ * sublevels settings in the copied expression.
*/
- if ((rte_candidate == NULL)
- && (get_attnum(rte->relid, colname) != InvalidAttrNumber))
- rte_candidate = rte;
+ if (sublevels_up > 0)
+ IncrementVarSublevelsUp(result, sublevels_up, 0);
+ }
+ }
+ return result;
+}
+
+/*
+ * colnameToVar
+ * Search for an unqualified column name.
+ * If found, return the appropriate Var node (or expression).
+ * If not found, return NULL. If the name proves ambiguous, raise error.
+ */
+Node *
+colnameToVar(ParseState *pstate, char *colname)
+{
+ Node *result = NULL;
+ ParseState *orig_pstate = pstate;
+ int levels_up = 0;
- if (rte_candidate == NULL)
- continue;
+ while (pstate != NULL)
+ {
+ List *jt;
- if (rte_result != NULL)
+ /*
+ * We want to look only at top-level jointree items, and even for
+ * those, ignore RTEs that are marked as not inFromCl and not
+ * the query's target relation.
+ */
+ foreach(jt, pstate->p_jointree)
+ {
+ Node *jtnode = (Node *) lfirst(jt);
+ Node *newresult = NULL;
+
+ if (IsA(jtnode, RangeTblRef))
{
- if (!pstate->p_is_insert ||
+ int varno = ((RangeTblRef *) jtnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
+
+ if (! rte->inFromCl &&
rte != pstate->p_target_rangetblentry)
- elog(ERROR, "Column '%s' is ambiguous", colname);
+ continue;
+
+ /* use orig_pstate here to get the right sublevels_up */
+ newresult = scanRTEForColumn(orig_pstate, rte, colname);
+ }
+ else if (IsA(jtnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) jtnode;
+
+ newresult = scanJoinForColumn(j, colname, levels_up);
}
else
- rte_result = rte;
+ elog(ERROR, "colnameToVar: unexpected node type %d",
+ nodeTag(jtnode));
+
+ if (newresult)
+ {
+ if (result)
+ elog(ERROR, "Column reference \"%s\" is ambiguous",
+ colname);
+ result = newresult;
+ }
}
- if (rte_result != NULL)
+ if (result != NULL)
break; /* found */
pstate = pstate->parentParseState;
+ levels_up++;
}
- return rte_result;
+
+ return result;
}
/*
- * put new entry in pstate p_rtable structure, or return pointer
- * if pstate null
+ * qualifiedNameToVar
+ * Search for a qualified column name (refname + column name).
+ * If found, return the appropriate Var node (or expression).
+ * If not found, return NULL. If the name proves ambiguous, raise error.
+ */
+Node *
+qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
+ bool implicitRTEOK)
+{
+ Node *result;
+ Node *rteorjoin;
+ int sublevels_up;
+
+ rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+
+ if (rteorjoin == NULL)
+ {
+ if (! implicitRTEOK)
+ return NULL;
+ rteorjoin = (Node *) addImplicitRTE(pstate, refname);
+ sublevels_up = 0;
+ }
+
+ if (IsA(rteorjoin, RangeTblEntry))
+ result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
+ colname);
+ else if (IsA(rteorjoin, JoinExpr))
+ result = scanJoinForColumn((JoinExpr *) rteorjoin,
+ colname, sublevels_up);
+ else
+ {
+ elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
+ nodeTag(rteorjoin));
+ result = NULL; /* keep compiler quiet */
+ }
+
+ return result;
+}
+
+/*
+ * Add an entry to the pstate's range table (p_rtable), unless the
+ * specified refname is already present, in which case raise error.
+ *
+ * If pstate is NULL, we just build an RTE and return it without worrying
+ * about membership in an rtable list.
*/
RangeTblEntry *
addRangeTableEntry(ParseState *pstate,
char *relname,
- Attr *ref,
+ Attr *alias,
bool inh,
- bool inFromCl,
- bool inJoinSet)
+ bool inFromCl)
{
+ char *refname = alias ? alias->relname : relname;
Relation rel;
RangeTblEntry *rte;
Attr *eref;
int maxattrs;
- int sublevels_up;
+ int numaliases;
int varattno;
- /* Look for an existing rte, if available... */
+ /* Check for conflicting RTE or jointable alias (at level 0 only) */
if (pstate != NULL)
{
- int rt_index = refnameRangeTablePosn(pstate, ref->relname,
- &sublevels_up);
+ Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
- if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
- {
- if (!strcmp(ref->relname, "*OLD*") || !strcmp(ref->relname, "*NEW*"))
- return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
- elog(ERROR, "Table name '%s' specified more than once", ref->relname);
- }
+ if (rteorjoin)
+ elog(ERROR, "Table name \"%s\" specified more than once",
+ refname);
}
rte = makeNode(RangeTblEntry);
rte->relname = relname;
- rte->ref = ref;
+ rte->alias = alias;
/*
* Get the rel's OID. This access also ensures that we have an
@@ -271,30 +492,34 @@ addRangeTableEntry(ParseState *pstate,
rte->relid = RelationGetRelid(rel);
maxattrs = RelationGetNumberOfAttributes(rel);
- eref = copyObject(ref);
- if (maxattrs < length(eref->attrs))
- elog(ERROR, "Table '%s' has %d columns available but %d columns specified",
- relname, maxattrs, length(eref->attrs));
+ eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
+ numaliases = length(eref->attrs);
+
+ if (maxattrs < numaliases)
+ elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
+ refname, maxattrs, numaliases);
/* fill in any unspecified alias columns */
- for (varattno = length(eref->attrs); varattno < maxattrs; varattno++)
+ for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->attrs = lappend(eref->attrs, makeString(attrname));
}
- heap_close(rel, AccessShareLock);
rte->eref = eref;
- /*
- * Flags: - this RTE should be expanded to include descendant tables,
- * - this RTE is in the FROM clause, - this RTE should be included in
- * the planner's final join.
+ heap_close(rel, AccessShareLock);
+
+ /*----------
+ * Flags:
+ * - this RTE should be expanded to include descendant tables,
+ * - this RTE is in the FROM clause,
+ * - this RTE should not be checked for access rights.
+ *----------
*/
rte->inh = inh;
rte->inFromCl = inFromCl;
- rte->inJoinSet = inJoinSet;
rte->skipAcl = false; /* always starts out false */
/*
@@ -306,118 +531,184 @@ addRangeTableEntry(ParseState *pstate,
return rte;
}
-/* expandTable()
- * Populates an Attr with table name and column names
- * This is similar to expandAll(), but does not create an RTE
- * if it does not already exist.
- * - thomas 2000-01-19
+/*
+ * Add the given RTE as a top-level entry in the pstate's join tree,
+ * unless there already is an entry for it.
*/
-Attr *
-expandTable(ParseState *pstate, char *refname, bool getaliases)
+void
+addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
+{
+ int rtindex = RTERangeTablePosn(pstate, rte, NULL);
+ List *jt;
+ RangeTblRef *rtr;
+
+ foreach(jt, pstate->p_jointree)
+ {
+ Node *n = (Node *) lfirst(jt);
+
+ if (IsA(n, RangeTblRef))
+ {
+ if (rtindex == ((RangeTblRef *) n)->rtindex)
+ return; /* it's already being joined to */
+ }
+ }
+
+ /* Not present, so add it */
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = rtindex;
+ pstate->p_jointree = lappend(pstate->p_jointree, rtr);
+}
+
+/*
+ * Add a POSTQUEL-style implicit RTE.
+ *
+ * We assume caller has already checked that there is no such RTE now.
+ */
+RangeTblEntry *
+addImplicitRTE(ParseState *pstate, char *relname)
{
- Attr *attr;
RangeTblEntry *rte;
+
+ rte = addRangeTableEntry(pstate, relname, NULL, false, false);
+ addRTEtoJoinTree(pstate, rte);
+ warnAutoRange(pstate, relname);
+
+ return rte;
+}
+
+/* expandRTE()
+ *
+ * Given a rangetable entry, create lists of its column names (aliases if
+ * provided, else real names) and Vars for each column. Only user columns
+ * are considered, since this is primarily used to expand '*' and determine
+ * the contents of JOIN tables.
+ *
+ * If only one of the two kinds of output list is needed, pass NULL for the
+ * output pointer for the unwanted one.
+ */
+void
+expandRTE(ParseState *pstate, RangeTblEntry *rte,
+ List **colnames, List **colvars)
+{
Relation rel;
int varattno,
- maxattrs;
+ maxattrs,
+ rtindex,
+ sublevels_up;
- rte = refnameRangeTableEntry(pstate, refname);
+ if (colnames)
+ *colnames = NIL;
+ if (colvars)
+ *colvars = NIL;
- if (getaliases && (rte != NULL))
- return rte->eref;
+ /* Need the RT index of the entry for creating Vars */
+ rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte != NULL)
- rel = heap_open(rte->relid, AccessShareLock);
- else
- rel = heap_openr(refname, AccessShareLock);
-
- if (rel == NULL)
- elog(ERROR, "Relation '%s' not found", refname);
+ rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
- attr = makeAttr(refname, NULL);
-
for (varattno = 0; varattno < maxattrs; varattno++)
{
- char *attrname;
+ Form_pg_attribute attr = rel->rd_att->attrs[varattno];
#ifdef _DROP_COLUMN_HACK__
- if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
+ if (COLUMN_IS_DROPPED(attr))
continue;
#endif /* _DROP_COLUMN_HACK__ */
- attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
- attr->attrs = lappend(attr->attrs, makeString(attrname));
+
+ if (colnames)
+ {
+ char *label;
+
+ if (varattno < length(rte->eref->attrs))
+ label = strVal(nth(varattno, rte->eref->attrs));
+ else
+ label = NameStr(attr->attname);
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, attr->attnum,
+ attr->atttypid, attr->atttypmod,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
}
heap_close(rel, AccessShareLock);
-
- return attr;
}
/*
- * expandAll -
- * makes a list of attributes
+ * expandRelAttrs -
+ * makes a list of TargetEntry nodes for the attributes of the rel
*/
List *
-expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
- List *te_list = NIL;
- RangeTblEntry *rte;
- Relation rel;
- int varattno,
- maxattrs;
+ List *name_list,
+ *var_list;
- rte = refnameRangeTableEntry(pstate, ref->relname);
- if (rte == NULL)
- {
- rte = addRangeTableEntry(pstate, relname, ref,
- FALSE, FALSE, TRUE);
- warnAutoRange(pstate, ref->relname);
- }
+ expandRTE(pstate, rte, &name_list, &var_list);
- rel = heap_open(rte->relid, AccessShareLock);
+ return expandNamesVars(pstate, name_list, var_list);
+}
- maxattrs = RelationGetNumberOfAttributes(rel);
+/*
+ * expandJoinAttrs -
+ * makes a list of TargetEntry nodes for the attributes of the join
+ */
+List *
+expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
+{
+ List *vars;
- for (varattno = 0; varattno < maxattrs; varattno++)
- {
- char *attrname;
- char *label;
- Var *varnode;
- TargetEntry *te = makeNode(TargetEntry);
+ vars = copyObject(join->colvars);
+ /*
+ * If referencing an uplevel join item, we must adjust
+ * sublevels settings in the copied expression.
+ */
+ if (sublevels_up > 0)
+ IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
-#ifdef _DROP_COLUMN_HACK__
- if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
- continue;
-#endif /* _DROP_COLUMN_HACK__ */
- attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
+ return expandNamesVars(pstate,
+ copyObject(join->colnames),
+ vars);
+}
- /*
- * varattno is zero-based, so check that length() is always
- * greater
- */
- if (length(rte->eref->attrs) > varattno)
- label = pstrdup(strVal(nth(varattno, rte->eref->attrs)));
- else
- label = attrname;
- varnode = make_var(pstate, rte->relid, relname, attrname);
+/*
+ * expandNamesVars -
+ * Workhorse for "*" expansion: produce a list of targetentries
+ * given lists of column names (as String nodes) and var references.
+ */
+static List *
+expandNamesVars(ParseState *pstate, List *names, List *vars)
+{
+ List *te_list = NIL;
- /*
- * Even if the elements making up a set are complex, the set
- * itself is not.
- */
+ while (names)
+ {
+ char *label = strVal(lfirst(names));
+ Node *varnode = (Node *) lfirst(vars);
+ TargetEntry *te = makeNode(TargetEntry);
- te->resdom = makeResdom((AttrNumber) (*this_resno)++,
- varnode->vartype,
- varnode->vartypmod,
+ te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
+ exprType(varnode),
+ exprTypmod(varnode),
label,
false);
- te->expr = (Node *) varnode;
+ te->expr = varnode;
te_list = lappend(te_list, te);
+
+ names = lnext(names);
+ vars = lnext(vars);
}
- heap_close(rel, AccessShareLock);
+ Assert(vars == NIL); /* lists not same length? */
return te_list;
}
@@ -531,11 +822,17 @@ attnumTypeId(Relation rd, int attid)
return rd->rd_att->attrs[attid - 1]->atttypid;
}
-void
+/*
+ * Generate a warning about an implicit RTE, if appropriate.
+ *
+ * Our current theory on this is that we should allow "SELECT foo.*"
+ * but warn about a mixture of explicit and implicit RTEs.
+ */
+static void
warnAutoRange(ParseState *pstate, char *refname)
{
- List *temp;
bool foundInFromCl = false;
+ List *temp;
foreach(temp, pstate->p_rtable)
{
@@ -548,8 +845,8 @@ warnAutoRange(ParseState *pstate, char *refname)
}
}
if (foundInFromCl)
- elog(NOTICE, "Adding missing FROM-clause entry%s for table %s",
- pstate->parentParseState != NULL ? " in subquery" : "",
- refname);
+ elog(NOTICE, "Adding missing FROM-clause entry%s for table \"%s\"",
+ pstate->parentParseState != NULL ? " in subquery" : "",
+ refname);
}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 1564f976b04..b8e1570985f 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.61 2000/08/08 15:42:04 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
@@ -104,36 +105,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
* Target item is a single '*', expand all tables (eg.
* SELECT * FROM emp)
*/
- if (pstate->p_shape != NULL)
- {
- List *s,
- *a;
- int i;
-
- Assert(length(pstate->p_shape) == length(pstate->p_alias));
-
- s = pstate->p_shape;
- a = pstate->p_alias;
- for (i = 0; i < length(pstate->p_shape); i++)
- {
- TargetEntry *te;
- char *colname;
- Attr *shape = lfirst(s);
- Attr *alias = lfirst(a);
-
- Assert(IsA(shape, Attr) &&IsA(alias, Attr));
-
- colname = strVal(lfirst(alias->attrs));
- te = transformTargetEntry(pstate, (Node *) shape,
- NULL, colname, false);
- p_target = lappend(p_target, te);
- s = lnext(s);
- a = lnext(a);
- }
- }
- else
- p_target = nconc(p_target,
- ExpandAllTables(pstate));
+ p_target = nconc(p_target,
+ ExpandAllTables(pstate));
}
else if (att->attrs != NIL &&
strcmp(strVal(lfirst(att->attrs)), "*") == 0)
@@ -143,10 +116,30 @@ transformTargetList(ParseState *pstate, List *targetlist)
* Target item is relation.*, expand that table (eg.
* SELECT emp.*, dname FROM emp, dept)
*/
- p_target = nconc(p_target,
- expandAll(pstate, att->relname,
- makeAttr(att->relname, NULL),
- &pstate->p_last_resno));
+ Node *rteorjoin;
+ int sublevels_up;
+
+ rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
+ &sublevels_up);
+
+ if (rteorjoin == NULL)
+ {
+ rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
+ sublevels_up = 0;
+ }
+
+ if (IsA(rteorjoin, RangeTblEntry))
+ p_target = nconc(p_target,
+ expandRelAttrs(pstate,
+ (RangeTblEntry *) rteorjoin));
+ else if (IsA(rteorjoin, JoinExpr))
+ p_target = nconc(p_target,
+ expandJoinAttrs(pstate,
+ (JoinExpr *) rteorjoin,
+ sublevels_up));
+ else
+ elog(ERROR, "transformTargetList: unexpected node type %d",
+ nodeTag(rteorjoin));
}
else
{
@@ -219,23 +212,12 @@ updateTargetListEntry(ParseState *pstate,
*/
if (indirection)
{
-#ifndef DISABLE_JOIN_SYNTAX
- Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname);
-
-#else
- Attr *att = makeNode(Attr);
-
-#endif
+ Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
+ colname);
Node *arrayBase;
ArrayRef *aref;
-#ifdef DISABLE_JOIN_SYNTAX
- att->relname = pstrdup(RelationGetRelationName(rd));
- att->attrs = lcons(makeString(colname), NIL);
-#endif
- arrayBase = ParseNestedFuncOrColumn(pstate, att,
- &pstate->p_last_resno,
- EXPR_COLUMN_FIRST);
+ arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
aref = transformArraySubscripts(pstate, arrayBase,
indirection,
pstate->p_is_insert,
@@ -401,46 +383,54 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
}
/* ExpandAllTables()
- * Turns '*' (in the target list) into a list of attributes
- * (of all relations in the range table)
+ * Turns '*' (in the target list) into a list of targetlist entries.
+ *
+ * tlist entries are generated for each relation appearing in the FROM list,
+ * which by now has been expanded into a join tree.
*/
static List *
ExpandAllTables(ParseState *pstate)
{
List *target = NIL;
- List *rt,
- *rtable;
-
- rtable = pstate->p_rtable;
- if (pstate->p_is_rule)
- {
-
- /*
- * skip first two entries, "*new*" and "*current*"
- */
- rtable = lnext(lnext(rtable));
- }
+ List *jt;
/* SELECT *; */
- if (rtable == NIL)
+ if (pstate->p_jointree == NIL)
elog(ERROR, "Wildcard with no tables specified not allowed");
- foreach(rt, rtable)
+ foreach(jt, pstate->p_jointree)
{
- RangeTblEntry *rte = lfirst(rt);
+ Node *n = (Node *) lfirst(jt);
- /*
- * we only expand those listed in the from clause. (This will also
- * prevent us from using the wrong table in inserts: eg. tenk2 in
- * "insert into tenk2 select * from tenk1;")
- */
- if (!rte->inFromCl)
- continue;
+ if (IsA(n, RangeTblRef))
+ {
+ RangeTblEntry *rte;
- target = nconc(target,
- expandAll(pstate, rte->eref->relname, rte->eref,
- &pstate->p_last_resno));
+ rte = rt_fetch(((RangeTblRef *) n)->rtindex,
+ pstate->p_rtable);
+
+ /*
+ * Ignore added-on relations that were not listed in the FROM
+ * clause.
+ */
+ if (!rte->inFromCl)
+ continue;
+
+ target = nconc(target, expandRelAttrs(pstate, rte));
+ }
+ else if (IsA(n, JoinExpr))
+ {
+ /* A newfangled join expression */
+ JoinExpr *j = (JoinExpr *) n;
+
+ /* Currently, a join expr could only have come from FROM. */
+ target = nconc(target, expandJoinAttrs(pstate, j, 0));
+ }
+ else
+ elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
+ "\n\t%s", nodeToString(n));
}
+
return target;
}
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index a7652407b73..4a6c825498a 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -1,32 +1,40 @@
/*-------------------------------------------------------------------------
*
* parser.c
+ * Main entry point/driver for PostgreSQL parser
+ *
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- *
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.45 2000/04/12 17:15:27 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+
+#include "nodes/parsenodes.h"
+#include "nodes/pg_list.h"
#include "parser/analyze.h"
#include "parser/gramparse.h"
+#include "parser/parse.h"
#include "parser/parser.h"
#include "parser/parse_expr.h"
+
#if defined(FLEX_SCANNER)
extern void DeleteBuffer(void);
-
#endif /* FLEX_SCANNER */
char *parseString; /* the char* which holds the string to be
* parsed */
List *parsetree; /* result of parsing is left here */
+static int lookahead_token; /* one-token lookahead */
+static bool have_lookahead; /* lookahead_token set? */
+
#ifdef SETS_FIXED
static void fixupsets();
static void define_sets();
@@ -42,11 +50,11 @@ parser(char *str, Oid *typev, int nargs)
List *queryList;
int yyresult;
- init_io();
-
- parseString = pstrdup(str);
+ parseString = str;
parsetree = NIL; /* in case parser forgets to set it */
+ have_lookahead = false;
+ scanner_init();
parser_init(typev, nargs);
parse_expr_init();
@@ -83,6 +91,52 @@ parser(char *str, Oid *typev, int nargs)
return queryList;
}
+
+/*
+ * Intermediate filter between parser and base lexer (base_yylex in scan.l).
+ *
+ * The filter is needed because in some cases SQL92 requires more than one
+ * token lookahead. We reduce these cases to one-token lookahead by combining
+ * tokens here, in order to keep the grammar LR(1).
+ *
+ * Using a filter is simpler than trying to recognize multiword tokens
+ * directly in scan.l, because we'd have to allow for comments between the
+ * words ...
+ */
+int
+yylex(void)
+{
+ int cur_token;
+
+ /* Get next token --- we might already have it */
+ if (have_lookahead)
+ {
+ cur_token = lookahead_token;
+ have_lookahead = false;
+ }
+ else
+ cur_token = base_yylex();
+
+ /* Do we need to look ahead for a possible multiword token? */
+ switch (cur_token)
+ {
+ case UNION:
+ /* UNION JOIN must be reduced to a single UNIONJOIN token */
+ lookahead_token = base_yylex();
+ if (lookahead_token == JOIN)
+ cur_token = UNIONJOIN;
+ else
+ have_lookahead = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return cur_token;
+}
+
+
#ifdef SETS_FIXED
static void
fixupsets(Query *parse)
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index f5597d1593e..5700915ad94 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.76 2000/08/22 13:01:20 ishii Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.77 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -76,7 +76,7 @@ static char *literalbuf; /* expandable buffer */
static int literallen; /* actual current length */
static int literalalloc; /* current allocated buffer size */
-static int xcdepth = 0;
+static int xcdepth = 0; /* depth of nesting in slash-star comments */
#define startlit() (literalbuf[0] = '\0', literallen = 0)
static void addlit(char *ytext, int yleng);
@@ -510,22 +510,24 @@ other .
%%
-void yyerror(const char * message)
+void
+yyerror(const char *message)
{
elog(ERROR, "parser: %s at or near \"%s\"", message, yytext);
}
-int yywrap()
+int
+yywrap(void)
{
return(1);
}
/*
- init_io:
+ scanner_init:
called by postgres before any actual parsing is done
*/
void
-init_io()
+scanner_init(void)
{
/* it's important to set this to NULL
because input()/myinput() checks the non-nullness of parseCh