mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Fix planner and rewriter to follow SQL semantics for tables that are
mentioned in FROM but not elsewhere in the query: such tables should be joined over anyway. Aside from being more standards-compliant, this allows removal of some very ugly hacks for COUNT(*) processing. Also, allow HAVING clause without aggregate functions, since SQL does. Clean up CREATE RULE statement-list syntax the same way Bruce just fixed the main stmtmulti production. CAUTION: addition of a field to RangeTblEntry nodes breaks stored rules; you will have to initdb if you have any rules.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: analyze.c,v 1.120 1999/10/03 23:55:30 tgl Exp $
|
||||
* $Id: analyze.c,v 1.121 1999/10/07 04:23:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -298,18 +298,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs || qry->groupClause)
|
||||
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
|
||||
/*
|
||||
* If there is a havingQual but there are no aggregates, then there is
|
||||
* something wrong with the query because HAVING must contain
|
||||
* aggregates in its expressions! Otherwise the query could have been
|
||||
* formulated using the WHERE clause.
|
||||
*/
|
||||
if (qry->havingQual && ! qry->hasAggs)
|
||||
elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
|
||||
|
||||
/*
|
||||
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
||||
* unionClause may be false
|
||||
@@ -961,9 +952,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
nothing_qry->commandType = CMD_NOTHING;
|
||||
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
|
||||
FALSE, FALSE);
|
||||
FALSE, FALSE, FALSE);
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
|
||||
FALSE, FALSE);
|
||||
FALSE, FALSE, FALSE);
|
||||
|
||||
nothing_qry->rtable = pstate->p_rtable;
|
||||
|
||||
@@ -983,9 +974,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
* equal to 2.
|
||||
*/
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*",
|
||||
FALSE, FALSE);
|
||||
FALSE, FALSE, FALSE);
|
||||
addRangeTableEntry(pstate, stmt->object->relname, "*NEW*",
|
||||
FALSE, FALSE);
|
||||
FALSE, FALSE, FALSE);
|
||||
|
||||
pstate->p_last_resno = 1;
|
||||
pstate->p_is_rule = true; /* for expand all */
|
||||
@@ -1048,18 +1039,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs || qry->groupClause)
|
||||
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
|
||||
/*
|
||||
* If there is a havingQual but there are no aggregates, then there is
|
||||
* something wrong with the query because HAVING must contain
|
||||
* aggregates in its expressions! Otherwise the query could have been
|
||||
* formulated using the WHERE clause.
|
||||
*/
|
||||
if (qry->havingQual && ! qry->hasAggs)
|
||||
elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
|
||||
|
||||
/*
|
||||
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
||||
* unionClause may be false
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.107 1999/10/05 18:14:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.108 1999/10/07 04:23:12 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -72,7 +72,6 @@ static char *xlateSqlType(char *);
|
||||
static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
|
||||
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
|
||||
static void mapTargetColumns(List *source, List *target);
|
||||
static char *fmtId(char *rawid);
|
||||
static void param_type_init(Oid *typev, int nargs);
|
||||
static Node *doNegate(Node *n);
|
||||
|
||||
@@ -130,7 +129,7 @@ Oid param_type(int t); /* used in parse_expr.c */
|
||||
UpdateStmt, InsertStmt, select_clause, SelectStmt, NotifyStmt, DeleteStmt,
|
||||
ClusterStmt, ExplainStmt, VariableSetStmt, VariableShowStmt, VariableResetStmt,
|
||||
CreateUserStmt, AlterUserStmt, DropUserStmt, RuleActionStmt,
|
||||
ConstraintsSetStmt,
|
||||
RuleActionStmtOrEmpty, ConstraintsSetStmt,
|
||||
|
||||
%type <str> opt_database1, opt_database2, location, encoding
|
||||
|
||||
@@ -165,7 +164,7 @@ Oid param_type(int t); /* used in parse_expr.c */
|
||||
result, relation_name_list, OptTableElementList,
|
||||
OptInherit, definition,
|
||||
opt_with, func_args, func_args_list, func_as,
|
||||
oper_argtypes, RuleActionList, RuleActionBlock, RuleActionMulti,
|
||||
oper_argtypes, RuleActionList, RuleActionMulti,
|
||||
opt_column_list, columnList, opt_va_list, va_list,
|
||||
sort_clause, sortby_list, index_params, index_list, name_list,
|
||||
from_clause, from_expr, table_list, opt_array_bounds,
|
||||
@@ -374,17 +373,18 @@ stmtblock: stmtmulti
|
||||
{ parsetree = $1; }
|
||||
;
|
||||
|
||||
/* the thrashing around here is to discard "empty" statements... */
|
||||
stmtmulti: stmtmulti ';' stmt
|
||||
{ if ($3 != (Node *)NIL)
|
||||
{ if ($3 != (Node *)NULL)
|
||||
$$ = lappend($1, $3);
|
||||
else
|
||||
$$ = $1;
|
||||
}
|
||||
| stmt
|
||||
{ if ($1 != (Node *)NIL)
|
||||
{ if ($1 != (Node *)NULL)
|
||||
$$ = lcons($1,NIL);
|
||||
else
|
||||
$$ = (Node *)NIL;
|
||||
$$ = NIL;
|
||||
}
|
||||
;
|
||||
|
||||
@@ -433,7 +433,7 @@ stmt : AddAttrStmt
|
||||
| VariableResetStmt
|
||||
| ConstraintsSetStmt
|
||||
| /*EMPTY*/
|
||||
{ $$ = (Node *)NIL; }
|
||||
{ $$ = (Node *)NULL; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -930,7 +930,7 @@ ColConstraint:
|
||||
CONSTRAINT name ColConstraintElem
|
||||
{
|
||||
Constraint *n = (Constraint *)$3;
|
||||
if (n != NULL) n->name = fmtId($2);
|
||||
if (n != NULL) n->name = $2;
|
||||
$$ = $3;
|
||||
}
|
||||
| ColConstraintElem
|
||||
@@ -1024,7 +1024,7 @@ ColConstraintElem: CHECK '(' a_expr ')'
|
||||
TableConstraint: CONSTRAINT name ConstraintElem
|
||||
{
|
||||
Constraint *n = (Constraint *)$3;
|
||||
if (n != NULL) n->name = fmtId($2);
|
||||
if (n != NULL) n->name = $2;
|
||||
$$ = $3;
|
||||
}
|
||||
| ConstraintElem
|
||||
@@ -2034,20 +2034,23 @@ RuleStmt: CREATE RULE name AS
|
||||
RuleActionList: NOTHING { $$ = NIL; }
|
||||
| SelectStmt { $$ = lcons($1, NIL); }
|
||||
| RuleActionStmt { $$ = lcons($1, NIL); }
|
||||
| '[' RuleActionBlock ']' { $$ = $2; }
|
||||
| '(' RuleActionBlock ')' { $$ = $2; }
|
||||
| '[' RuleActionMulti ']' { $$ = $2; }
|
||||
| '(' RuleActionMulti ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
RuleActionBlock: RuleActionMulti { $$ = $1; }
|
||||
| RuleActionStmt { $$ = lcons($1, NIL); }
|
||||
;
|
||||
|
||||
RuleActionMulti: RuleActionMulti RuleActionStmt
|
||||
{ $$ = lappend($1, $2); }
|
||||
| RuleActionMulti RuleActionStmt ';'
|
||||
{ $$ = lappend($1, $2); }
|
||||
| RuleActionStmt ';'
|
||||
{ $$ = lcons($1, NIL); }
|
||||
/* the thrashing around here is to discard "empty" statements... */
|
||||
RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty
|
||||
{ if ($3 != (Node *)NULL)
|
||||
$$ = lappend($1, $3);
|
||||
else
|
||||
$$ = $1;
|
||||
}
|
||||
| RuleActionStmtOrEmpty
|
||||
{ if ($1 != (Node *)NULL)
|
||||
$$ = lcons($1,NIL);
|
||||
else
|
||||
$$ = NIL;
|
||||
}
|
||||
;
|
||||
|
||||
RuleActionStmt: InsertStmt
|
||||
@@ -2056,6 +2059,11 @@ RuleActionStmt: InsertStmt
|
||||
| NotifyStmt
|
||||
;
|
||||
|
||||
RuleActionStmtOrEmpty: RuleActionStmt
|
||||
| /*EMPTY*/
|
||||
{ $$ = (Node *)NULL; }
|
||||
;
|
||||
|
||||
event_object: relation_name '.' attr_name
|
||||
{
|
||||
$$ = makeNode(Attr);
|
||||
@@ -3676,12 +3684,25 @@ a_expr: attr
|
||||
{ $$ = makeA_Expr(OP, $2, $1, NULL); }
|
||||
| func_name '(' '*' ')'
|
||||
{
|
||||
/* cheap hack for aggregate (eg. count) */
|
||||
/*
|
||||
* For now, we transform AGGREGATE(*) into AGGREGATE(1).
|
||||
*
|
||||
* This does the right thing for COUNT(*) (in fact,
|
||||
* any certainly-non-null expression would do for COUNT),
|
||||
* and there are no other aggregates in SQL92 that accept
|
||||
* '*' as parameter.
|
||||
*
|
||||
* XXX really, the '*' ought to be transformed to some
|
||||
* special construct that wouldn't be acceptable as the
|
||||
* input of a non-aggregate function, in case the given
|
||||
* func_name matches a plain function. This would also
|
||||
* support a possible extension to let user-defined
|
||||
* aggregates do something special with '*' as input.
|
||||
*/
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
A_Const *star = makeNode(A_Const);
|
||||
|
||||
star->val.type = T_String;
|
||||
star->val.val.str = "";
|
||||
star->val.type = T_Integer;
|
||||
star->val.val.ival = 1;
|
||||
n->funcname = $1;
|
||||
n->args = lcons(star, NIL);
|
||||
$$ = (Node *)n;
|
||||
@@ -5265,30 +5286,6 @@ void parser_init(Oid *typev, int nargs)
|
||||
}
|
||||
|
||||
|
||||
/* fmtId()
|
||||
* Check input string for non-lowercase/non-numeric characters.
|
||||
* Returns either input string or input surrounded by double quotes.
|
||||
*/
|
||||
static char *
|
||||
fmtId(char *rawid)
|
||||
{
|
||||
static char *cp;
|
||||
|
||||
for (cp = rawid; *cp != '\0'; cp++)
|
||||
if (! (islower(*cp) || isdigit(*cp) || (*cp == '_'))) break;
|
||||
|
||||
if (*cp != '\0') {
|
||||
cp = palloc(strlen(rawid)+3);
|
||||
strcpy(cp,"\"");
|
||||
strcat(cp,rawid);
|
||||
strcat(cp,"\"");
|
||||
} else {
|
||||
cp = rawid;
|
||||
};
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
/*
|
||||
* param_type_init()
|
||||
*
|
||||
@@ -5313,6 +5310,11 @@ Oid param_type(int t)
|
||||
* The optimizer doesn't like '-' 4 for index use. It only checks for
|
||||
* Var '=' Const. It wants an integer of -4, so we try to merge the
|
||||
* minus into the constant.
|
||||
*
|
||||
* This code is no longer essential as of 10/1999, since the optimizer
|
||||
* now has a constant-subexpression simplifier. However, we can save
|
||||
* a few cycles throughout the parse and rewrite stages if we collapse
|
||||
* the minus into the constant sooner rather than later...
|
||||
*/
|
||||
static Node *doNegate(Node *n)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.28 1999/08/21 03:48:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.29 1999/10/07 04:23:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -129,8 +129,8 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
List *groupClauses = NIL;
|
||||
List *tl;
|
||||
|
||||
/* This should only be called if we found aggregates or grouping */
|
||||
Assert(pstate->p_hasAggs || qry->groupClause);
|
||||
/* This should only be called if we found aggregates, GROUP, or HAVING */
|
||||
Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual);
|
||||
|
||||
/*
|
||||
* Aggregates must never appear in WHERE clauses. (Note this check
|
||||
@@ -160,6 +160,15 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
groupClauses = lcons(expr, groupClauses);
|
||||
}
|
||||
|
||||
/*
|
||||
* The expression specified in the HAVING clause can only contain
|
||||
* aggregates, group columns and functions thereof. As with WHERE,
|
||||
* we want to point the finger at HAVING before the target list.
|
||||
*/
|
||||
if (!exprIsAggOrGroupCol(qry->havingQual, groupClauses))
|
||||
elog(ERROR,
|
||||
"Illegal use of aggregates or non-group column in HAVING clause");
|
||||
|
||||
/*
|
||||
* The target list can only contain aggregates, group columns and
|
||||
* functions thereof.
|
||||
@@ -173,14 +182,6 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
"Illegal use of aggregates or non-group column in target list");
|
||||
}
|
||||
|
||||
/*
|
||||
* The expression specified in the HAVING clause has the same
|
||||
* restriction as those in the target list.
|
||||
*/
|
||||
if (!exprIsAggOrGroupCol(qry->havingQual, groupClauses))
|
||||
elog(ERROR,
|
||||
"Illegal use of aggregates or non-group column in HAVING clause");
|
||||
|
||||
/* Release the list storage (but not the pointed-to expressions!) */
|
||||
freeList(groupClauses);
|
||||
}
|
||||
@@ -190,12 +191,12 @@ Aggref *
|
||||
ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
List *target, int precedence)
|
||||
{
|
||||
Oid fintype;
|
||||
Oid vartype;
|
||||
Oid xfn1;
|
||||
Form_pg_aggregate aggform;
|
||||
Aggref *aggref;
|
||||
HeapTuple theAggTuple;
|
||||
Form_pg_aggregate aggform;
|
||||
Oid fintype;
|
||||
Oid xfn1;
|
||||
Oid vartype;
|
||||
Aggref *aggref;
|
||||
bool usenulls = false;
|
||||
|
||||
theAggTuple = SearchSysCacheTuple(AGGNAME,
|
||||
@@ -206,66 +207,19 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
elog(ERROR, "Aggregate %s does not exist", aggname);
|
||||
|
||||
/*
|
||||
* We do a major hack for count(*) here.
|
||||
* There used to be a really ugly hack for count(*) here.
|
||||
*
|
||||
* Count(*) poses several problems. First, we need a field that is
|
||||
* guaranteed to be in the range table, and unique. Using a constant
|
||||
* causes the optimizer to properly remove the aggragate from any
|
||||
* elements of the query. Using just 'oid', which can not be null, in
|
||||
* the parser fails on:
|
||||
* It's gone. Now, the grammar transforms count(*) into count(1),
|
||||
* which does the right thing. (It didn't use to do the right thing,
|
||||
* because the optimizer had the wrong ideas about semantics of queries
|
||||
* without explicit variables. Fixed as of Oct 1999 --- tgl.)
|
||||
*
|
||||
* select count(*) from tab1, tab2 -- oid is not unique select
|
||||
* count(*) from viewtable -- views don't have real oids
|
||||
*
|
||||
* So, for an aggregate with parameter '*', we use the first valid range
|
||||
* table entry, and pick the first column from the table. We set a
|
||||
* flag to count nulls, because we could have nulls in that column.
|
||||
*
|
||||
* It's an ugly job, but someone has to do it. bjm 1998/1/18
|
||||
* Since "1" never evaluates as null, we currently have no need of
|
||||
* the "usenulls" flag, but it should be kept around; in fact, we should
|
||||
* extend the pg_aggregate table to let usenulls be specified as an
|
||||
* attribute of user-defined aggregates.
|
||||
*/
|
||||
|
||||
if (nodeTag(lfirst(target)) == T_Const)
|
||||
{
|
||||
Const *con = (Const *) lfirst(target);
|
||||
|
||||
if (con->consttype == UNKNOWNOID && VARSIZE(con->constvalue) == VARHDRSZ)
|
||||
{
|
||||
Attr *attr = makeNode(Attr);
|
||||
List *rtable,
|
||||
*rlist;
|
||||
RangeTblEntry *first_valid_rte;
|
||||
|
||||
Assert(lnext(target) == NULL);
|
||||
|
||||
if (pstate->p_is_rule)
|
||||
rtable = lnext(lnext(pstate->p_rtable));
|
||||
else
|
||||
rtable = pstate->p_rtable;
|
||||
|
||||
first_valid_rte = NULL;
|
||||
foreach(rlist, rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(rlist);
|
||||
|
||||
/* only entries on outer(non-function?) scope */
|
||||
if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
|
||||
continue;
|
||||
|
||||
first_valid_rte = rte;
|
||||
break;
|
||||
}
|
||||
if (first_valid_rte == NULL)
|
||||
elog(ERROR, "Can't find column to do aggregate(*) on.");
|
||||
|
||||
attr->relname = first_valid_rte->refname;
|
||||
attr->attrs = lcons(makeString(
|
||||
get_attname(first_valid_rte->relid, 1)), NIL);
|
||||
|
||||
lfirst(target) = transformExpr(pstate, (Node *) attr, precedence);
|
||||
usenulls = true;
|
||||
}
|
||||
}
|
||||
|
||||
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
|
||||
fintype = aggform->aggfinaltype;
|
||||
xfn1 = aggform->aggtransfn1;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.45 1999/09/18 19:07:12 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.46 1999/10/07 04:23:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -60,6 +60,15 @@ makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
|
||||
* setTargetTable
|
||||
* Add the target relation of INSERT or UPDATE to the range table,
|
||||
* and make the special links to it in the ParseState.
|
||||
*
|
||||
* Note that the target is not marked as either inFromCl or inJoinSet.
|
||||
* For INSERT, we don't want the target to be joined to; it's a
|
||||
* destination of tuples, not a source. For UPDATE/DELETE, we do
|
||||
* need to scan or join the target. This will happen without the
|
||||
* inJoinSet flag because the planner's preprocess_targetlist()
|
||||
* adds the destination's CTID attribute to the targetlist, and
|
||||
* therefore the destination will be a referenced table even if
|
||||
* there is no other use of any of its attributes. Tricky, eh?
|
||||
*/
|
||||
void
|
||||
setTargetTable(ParseState *pstate, char *relname)
|
||||
@@ -69,7 +78,8 @@ setTargetTable(ParseState *pstate, char *relname)
|
||||
|
||||
if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
|
||||
|| (sublevels_up != 0))
|
||||
rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE);
|
||||
rte = addRangeTableEntry(pstate, relname, relname,
|
||||
FALSE, FALSE, FALSE);
|
||||
else
|
||||
rte = refnameRangeTableEntry(pstate, relname);
|
||||
|
||||
@@ -230,7 +240,8 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||
* we expand * to foo.x.
|
||||
*/
|
||||
|
||||
rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE);
|
||||
rte = addRangeTableEntry(pstate, relname, refname,
|
||||
baserel->inh, TRUE, TRUE);
|
||||
|
||||
return refname;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.59 1999/09/29 18:16:04 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.60 1999/10/07 04:23:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -274,7 +274,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, refname, refname,FALSE, FALSE);
|
||||
rte = addRangeTableEntry(pstate, refname, refname,
|
||||
FALSE, FALSE, TRUE);
|
||||
#ifdef WARN_FROM
|
||||
elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
@@ -437,7 +438,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, refname, refname,FALSE, FALSE);
|
||||
rte = addRangeTableEntry(pstate, refname, refname,
|
||||
FALSE, FALSE, TRUE);
|
||||
#ifdef WARN_FROM
|
||||
elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.31 1999/09/29 18:16:04 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.32 1999/10/07 04:23:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -144,7 +144,7 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(et);
|
||||
|
||||
/* only entries on outer(non-function?) scope */
|
||||
/* only consider RTEs mentioned in FROM or UPDATE/DELETE */
|
||||
if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
|
||||
continue;
|
||||
|
||||
@@ -178,45 +178,51 @@ addRangeTableEntry(ParseState *pstate,
|
||||
char *relname,
|
||||
char *refname,
|
||||
bool inh,
|
||||
bool inFromCl)
|
||||
bool inFromCl,
|
||||
bool inJoinSet)
|
||||
{
|
||||
Relation relation;
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
RangeTblEntry *rte;
|
||||
int sublevels_up;
|
||||
|
||||
if (pstate != NULL)
|
||||
{
|
||||
if (refnameRangeTablePosn(pstate, refname, &sublevels_up) != 0 &&
|
||||
(!inFromCl || sublevels_up == 0))
|
||||
int rt_index = refnameRangeTablePosn(pstate, refname,
|
||||
&sublevels_up);
|
||||
|
||||
if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
|
||||
{
|
||||
if (!strcmp(refname, "*CURRENT*") || !strcmp(refname, "*NEW*"))
|
||||
{
|
||||
int rt_index = refnameRangeTablePosn(pstate, refname, &sublevels_up);
|
||||
|
||||
return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
|
||||
}
|
||||
elog(ERROR, "Table name '%s' specified more than once", refname);
|
||||
}
|
||||
}
|
||||
|
||||
rte = makeNode(RangeTblEntry);
|
||||
|
||||
rte->relname = pstrdup(relname);
|
||||
rte->refname = pstrdup(refname);
|
||||
|
||||
/* Get the rel's OID. This access also ensures that we have an
|
||||
* up-to-date relcache entry for the rel. We don't need to keep
|
||||
* it open, however.
|
||||
*/
|
||||
relation = heap_openr(relname, AccessShareLock);
|
||||
rte->relid = RelationGetRelid(relation);
|
||||
heap_close(relation, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Flags - zero or more from inheritance,union,version or recursive
|
||||
* (transitive closure) [we don't support them all -- ay 9/94 ]
|
||||
* 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.
|
||||
*/
|
||||
rte->inh = inh;
|
||||
|
||||
/* RelOID */
|
||||
rte->inFromCl = inFromCl;
|
||||
rte->inJoinSet = inJoinSet;
|
||||
rte->skipAcl = false; /* always starts out false */
|
||||
|
||||
/*
|
||||
* close the relation we're done with it for now.
|
||||
* Add completed RTE to range table list.
|
||||
*/
|
||||
if (pstate != NULL)
|
||||
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
||||
@@ -240,7 +246,8 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
|
||||
rte = addRangeTableEntry(pstate, relname, refname,
|
||||
FALSE, FALSE, TRUE);
|
||||
#ifdef WARN_FROM
|
||||
elog(NOTICE,"Adding missing FROM-clause entry%s for table %s",
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
|
||||
Reference in New Issue
Block a user