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:
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user