1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

First cut at full support for OUTER JOINs. There are still a few loose

ends to clean up (see my message of same date to pghackers), but mostly
it works.  INITDB REQUIRED!
This commit is contained in:
Tom Lane
2000-09-12 21:07:18 +00:00
parent b5c0ab278b
commit ed5003c584
93 changed files with 6386 additions and 4262 deletions

View File

@ -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

View File

@ -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;
/*
* 'instead nothing' rules with a qualification need a query a
* 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
* 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);
foreach(actions, stmt->actions)
{
ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
Query *sub_qry;
bool has_old,
has_new;
pstate->p_last_resno = 1;
pstate->p_is_rule = true; /* for expand all */
pstate->p_hasAggs = false;
/*
* 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);
action = (Query *) lfirst(actions);
if (action->commandType != CMD_NOTHING)
lfirst(actions) = transformStmt(pstate, lfirst(actions));
actions = lnext(actions);
/* 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);
}
}
/* take care of the where clause */
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
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);
}

View File

@ -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,64 +3738,150 @@ 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); }
from_list: from_list ',' table_ref { $$ = lappend($1, $3); }
| table_ref { $$ = lcons($1, NIL); }
;
/***********
* This results in one shift/reduce conflict, presumably due to the trailing "(+)"
* - Thomas 1999-09-20
/*
* 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
{
$$ = (Node *) $1;
}
| relation_expr alias_clause
{
$1->name = $2;
$$ = (Node *) $1;
}
| '(' select_clause ')'
{
RangeSubselect *n = makeNode(RangeSubselect);
n->subquery = $2;
n->name = NULL;
$$ = (Node *) n;
}
| '(' select_clause ')' alias_clause
{
RangeSubselect *n = makeNode(RangeSubselect);
n->subquery = $2;
n->name = $4;
$$ = (Node *) n;
}
| joined_table
{
$$ = (Node *) $1;
}
| '(' joined_table ')' alias_clause
{
$2->alias = $4;
$$ = (Node *) $2;
}
;
/*
* 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.
*
#ifdef ENABLE_ORACLE_JOIN_SYNTAX
oracle_list: oracle_expr { $$ = lcons($1, NIL); }
;
* 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.
* A NATURAL JOIN implicitly matches column names between
* tables and the shape is determined by which columns are
* in common. We'll collect columns during the later transformations.
*/
oracle_expr: ColId ',' ColId oracle_outer
joined_table: '(' joined_table ')'
{
elog(ERROR,"Oracle OUTER JOIN not yet supported");
$$ = NULL;
$$ = $2;
}
| oracle_outer ColId ',' ColId
| table_ref CROSS JOIN table_ref
{
elog(ERROR,"Oracle OUTER JOIN not yet supported");
$$ = NULL;
/* CROSS JOIN is same as unqualified inner join */
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_INNER;
n->isNatural = FALSE;
n->larg = $1;
n->rarg = $4;
n->using = NIL;
n->quals = NULL;
$$ = n;
}
;
oracle_outer: '(' '+' ')' { $$ = TRUE; }
;
#endif
***********/
from_expr: '(' join_clause_with_union ')' alias_clause
| table_ref UNIONJOIN table_ref
{
JoinExpr *j = $2;
j->alias = $4;
$$ = j;
/* UNION JOIN is made into 1 token to avoid shift/reduce
* conflict against regular UNION keyword.
*/
JoinExpr *n = makeNode(JoinExpr);
n->jointype = JOIN_UNION;
n->isNatural = FALSE;
n->larg = $1;
n->rarg = $3;
n->using = NIL;
n->quals = NULL;
$$ = n;
}
| join_clause
{ $$ = $1; }
;
table_expr: relation_expr alias_clause
| table_ref join_type JOIN table_ref join_qual
{
$$ = 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
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $2;
n->isNatural = FALSE;
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;
}
| table_ref JOIN table_ref join_qual
{
/* 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_ref NATURAL join_type JOIN table_ref
{
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;
}
| table_ref NATURAL JOIN table_ref
{
/* letting join_type reduce to empty doesn't work */
JoinExpr *n = makeNode(JoinExpr);
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;
}
;
@ -3824,102 +3907,17 @@ alias_clause: AS ColId '(' name_list ')'
$$ = makeNode(Attr);
$$->relname = $1;
}
| /*EMPTY*/
{
$$ = NULL; /* no qualifiers */
}
;
/* 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
{
$2->larg = (Node *)$1;
$$ = $2;
}
| table_expr join_expr
{
$2->larg = (Node *)$1;
$$ = $2;
}
;
/* This is everything but the left side of a join.
* 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.
* A NATURAL JOIN implicitly matches column names between
* 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
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $1;
n->rarg = (Node *)$3;
n->quals = (List *)$4;
$$ = n;
}
| NATURAL join_type JOIN table_expr
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = $2;
n->isNatural = TRUE;
n->rarg = (Node *)$4;
n->quals = NULL; /* figure out which columns later... */
$$ = n;
}
| CROSS JOIN table_expr
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = INNER_P;
n->isNatural = FALSE;
n->rarg = (Node *)$3;
n->quals = NULL;
$$ = n;
}
;
join_clause_with_union: join_clause_with_union join_expr_with_union
{
$2->larg = (Node *)$1;
$$ = $2;
}
| table_expr join_expr_with_union
{
$2->larg = (Node *)$1;
$$ = $2;
}
;
join_expr_with_union: join_expr
{ $$ = $1; }
| UNION JOIN table_expr
{
JoinExpr *n = makeNode(JoinExpr);
n->jointype = UNION;
n->rarg = (Node *)$3;
n->quals = NULL;
$$ = n;
elog(ERROR,"UNION JOIN not yet implemented");
}
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_type: FULL join_outer { $$ = FULL; }
| LEFT join_outer { $$ = LEFT; }
| RIGHT join_outer { $$ = RIGHT; }
| OUTER_P { $$ = LEFT; }
| INNER_P { $$ = INNER_P; }
| /*EMPTY*/ { $$ = INNER_P; }
;
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];
}

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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);
Node *var = colnameToVar(pstate, ident->name);
att->indirection = ident->indirection;
return transformAttr(pstate, att, precedence);
}
if (var != NULL)
result = transformIndirection(pstate, var, ident->indirection);
}
if (result == NULL)

View File

@ -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);
}
retval = qualifiedNameToVar(pstate, refname, funcname, true);
if (retval)
return retval;
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);
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);
}
rte = addImplicitRTE(pstate, refname);
relname = rte->relname;
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;

View File

@ -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);
vnum = RTERangeTablePosn(pstate, rte, &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;
if (rte->relid != InvalidOid)
{
/* Plain relation RTE --- get the attribute's type info */
HeapTuple tp;
Form_pg_attribute att_tup;
return makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
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);
}
/*

View File

@ -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;
}
pstate = pstate->parentParseState;
}
return rteList;
}
#endif
/* given refname, return a pointer to the range table entry */
join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
if (join)
return (Node *) join;
pstate = pstate->parentParseState;
if (sublevels_up)
(*sublevels_up)++;
else
break;
}
return NULL;
}
/* 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));
else
rtable = pstate->p_rtable;
foreach(et, rtable)
index = 1;
foreach(temp, pstate->p_rtable)
{
RangeTblEntry *rte_candidate = NULL;
RangeTblEntry *rte = lfirst(et);
/* only consider RTEs mentioned in FROM or UPDATE/DELETE */
if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
continue;
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;
}
}
}
/*
* 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 ((rte_candidate == NULL)
&& (get_attnum(rte->relid, colname) != InvalidAttrNumber))
rte_candidate = rte;
if (rte_candidate == NULL)
continue;
if (rte_result != NULL)
{
if (!pstate->p_is_insert ||
rte != pstate->p_target_rangetblentry)
elog(ERROR, "Column '%s' is ambiguous", colname);
}
else
rte_result = rte;
if (rte == (RangeTblEntry *) lfirst(temp))
return index;
index++;
}
if (rte_result != NULL)
break; /* found */
pstate = pstate->parentParseState;
if (sublevels_up)
(*sublevels_up)++;
else
break;
}
return rte_result;
elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
return 0; /* keep compiler quiet */
}
/*
* put new entry in pstate p_rtable structure, or return pointer
* if pstate null
* 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;
/*
* 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)
{
if (result)
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
result = (Node *) make_var(pstate, rte, attnum);
}
}
/*
* 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 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));
/*
* If referencing an uplevel join item, we must adjust
* sublevels settings in the copied expression.
*/
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;
while (pstate != NULL)
{
List *jt;
/*
* 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))
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
if (! rte->inFromCl &&
rte != pstate->p_target_rangetblentry)
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
elog(ERROR, "colnameToVar: unexpected node type %d",
nodeTag(jtnode));
if (newresult)
{
if (result)
elog(ERROR, "Column reference \"%s\" is ambiguous",
colname);
result = newresult;
}
}
if (result != NULL)
break; /* found */
pstate = pstate->parentParseState;
levels_up++;
}
return result;
}
/*
* 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,75 +531,78 @@ 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)
{
Attr *attr;
RangeTblEntry *rte;
Relation rel;
int varattno,
maxattrs;
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
List *jt;
RangeTblRef *rtr;
rte = refnameRangeTableEntry(pstate, refname);
if (getaliases && (rte != NULL))
return rte->eref;
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);
maxattrs = RelationGetNumberOfAttributes(rel);
attr = makeAttr(refname, NULL);
for (varattno = 0; varattno < maxattrs; varattno++)
foreach(jt, pstate->p_jointree)
{
char *attrname;
Node *n = (Node *) lfirst(jt);
#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));
attr->attrs = lappend(attr->attrs, makeString(attrname));
if (IsA(n, RangeTblRef))
{
if (rtindex == ((RangeTblRef *) n)->rtindex)
return; /* it's already being joined to */
}
}
heap_close(rel, AccessShareLock);
return attr;
/* Not present, so add it */
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
pstate->p_jointree = lappend(pstate->p_jointree, rtr);
}
/*
* expandAll -
* makes a list of attributes
* Add a POSTQUEL-style implicit RTE.
*
* We assume caller has already checked that there is no such RTE now.
*/
List *
expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
RangeTblEntry *
addImplicitRTE(ParseState *pstate, char *relname)
{
List *te_list = NIL;
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, ref->relname);
if (rte == NULL)
{
rte = addRangeTableEntry(pstate, relname, ref,
FALSE, FALSE, TRUE);
warnAutoRange(pstate, ref->relname);
}
if (colnames)
*colnames = NIL;
if (colvars)
*colvars = NIL;
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
rel = heap_open(rte->relid, AccessShareLock);
@ -382,42 +610,105 @@ expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
for (varattno = 0; varattno < maxattrs; varattno++)
{
char *attrname;
char *label;
Var *varnode;
TargetEntry *te = makeNode(TargetEntry);
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));
/*
* 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);
if (colnames)
{
char *label;
/*
* Even if the elements making up a set are complex, the set
* itself is not.
*/
if (varattno < length(rte->eref->attrs))
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
te->resdom = makeResdom((AttrNumber) (*this_resno)++,
varnode->vartype,
varnode->vartypmod,
label,
false);
te->expr = (Node *) varnode;
te_list = lappend(te_list, te);
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
/*
* expandRelAttrs -
* makes a list of TargetEntry nodes for the attributes of the rel
*/
List *
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
List *name_list,
*var_list;
expandRTE(pstate, rte, &name_list, &var_list);
return expandNamesVars(pstate, name_list, var_list);
}
/*
* expandJoinAttrs -
* makes a list of TargetEntry nodes for the attributes of the join
*/
List *
expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
{
List *vars;
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);
return expandNamesVars(pstate,
copyObject(join->colnames),
vars);
}
/*
* 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;
while (names)
{
char *label = strVal(lfirst(names));
Node *varnode = (Node *) lfirst(vars);
TargetEntry *te = makeNode(TargetEntry);
te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
exprType(varnode),
exprTypmod(varnode),
label,
false);
te->expr = varnode;
te_list = lappend(te_list, te);
names = lnext(names);
vars = lnext(vars);
}
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);
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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