Commit bbd6eb5b authored by Tom Lane's avatar Tom Lane

Repair some issues with column aliases and RowExpr construction in the

presence of dropped columns.  Document the already-presumed fact that
eref aliases in relation RTEs are supposed to have entries for dropped
columns; cause the user alias structs to have such entries too, so that
there's always a one-to-one mapping to the underlying physical attnums.
Adjust expandRTE() and related code to handle the case where a column
that is part of a JOIN has been dropped.  Generalize expandRTE()'s API
so that it can be used in a couple of places that formerly rolled their
own implementation of the same logic.  Fix ruleutils.c to suppress
display of aliases for columns that were dropped since the rule was made.
parent 040450be
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.36 2004/05/26 04:41:06 neilc Exp $ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.37 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -970,10 +970,15 @@ find_expr_references_walker(Node *node, ...@@ -970,10 +970,15 @@ find_expr_references_walker(Node *node,
if (var->varno <= 0 || var->varno > list_length(rtable)) if (var->varno <= 0 || var->varno > list_length(rtable))
elog(ERROR, "invalid varno %d", var->varno); elog(ERROR, "invalid varno %d", var->varno);
rte = rt_fetch(var->varno, rtable); rte = rt_fetch(var->varno, rtable);
/*
* A whole-row Var references no specific columns, so adds no new
* dependency.
*/
if (var->varattno == InvalidAttrNumber)
return false;
if (rte->rtekind == RTE_RELATION) if (rte->rtekind == RTE_RELATION)
{ {
/* If it's a plain relation, reference this column */ /* If it's a plain relation, reference this column */
/* NB: this code works for whole-row Var with attno 0, too */
add_object_address(OCLASS_CLASS, rte->relid, var->varattno, add_object_address(OCLASS_CLASS, rte->relid, var->varattno,
&context->addrs); &context->addrs);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.118 2004/06/05 01:55:04 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.119 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -57,10 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes, ...@@ -57,10 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes,
bool *differentTypes); bool *differentTypes);
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
bool *differentTypes); bool *differentTypes);
static void subquery_push_qual(Query *subquery, static void subquery_push_qual(Query *subquery, List *rtable,
RangeTblEntry *rte, Index rti, Node *qual); Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery, static void recurse_push_qual(Node *setOp, Query *topquery,
RangeTblEntry *rte, Index rti, Node *qual); List *rtable, Index rti, Node *qual);
/* /*
...@@ -376,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, ...@@ -376,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
{ {
/* Push it down */ /* Push it down */
subquery_push_qual(subquery, rte, rti, clause); subquery_push_qual(subquery, root->rtable, rti, clause);
} }
else else
{ {
...@@ -780,12 +780,13 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, ...@@ -780,12 +780,13 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
* subquery_push_qual - push down a qual that we have determined is safe * subquery_push_qual - push down a qual that we have determined is safe
*/ */
static void static void
subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) subquery_push_qual(Query *subquery, List *rtable, Index rti, Node *qual)
{ {
if (subquery->setOperations != NULL) if (subquery->setOperations != NULL)
{ {
/* Recurse to push it separately to each component query */ /* Recurse to push it separately to each component query */
recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual); recurse_push_qual(subquery->setOperations, subquery,
rtable, rti, qual);
} }
else else
{ {
...@@ -799,7 +800,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) ...@@ -799,7 +800,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
* This step also ensures that when we are pushing into a setop tree, * This step also ensures that when we are pushing into a setop tree,
* each component query gets its own copy of the qual. * each component query gets its own copy of the qual.
*/ */
qual = ResolveNew(qual, rti, 0, rte, qual = ResolveNew(qual, rti, 0, rtable,
subquery->targetList, subquery->targetList,
CMD_SELECT, 0); CMD_SELECT, 0);
subquery->havingQual = make_and_qual(subquery->havingQual, subquery->havingQual = make_and_qual(subquery->havingQual,
...@@ -818,7 +819,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) ...@@ -818,7 +819,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
*/ */
static void static void
recurse_push_qual(Node *setOp, Query *topquery, recurse_push_qual(Node *setOp, Query *topquery,
RangeTblEntry *rte, Index rti, Node *qual) List *rtable, Index rti, Node *qual)
{ {
if (IsA(setOp, RangeTblRef)) if (IsA(setOp, RangeTblRef))
{ {
...@@ -827,14 +828,14 @@ recurse_push_qual(Node *setOp, Query *topquery, ...@@ -827,14 +828,14 @@ recurse_push_qual(Node *setOp, Query *topquery,
Query *subquery = subrte->subquery; Query *subquery = subrte->subquery;
Assert(subquery != NULL); Assert(subquery != NULL);
subquery_push_qual(subquery, rte, rti, qual); subquery_push_qual(subquery, rtable, rti, qual);
} }
else if (IsA(setOp, SetOperationStmt)) else if (IsA(setOp, SetOperationStmt))
{ {
SetOperationStmt *op = (SetOperationStmt *) setOp; SetOperationStmt *op = (SetOperationStmt *) setOp;
recurse_push_qual(op->larg, topquery, rte, rti, qual); recurse_push_qual(op->larg, topquery, rtable, rti, qual);
recurse_push_qual(op->rarg, topquery, rte, rti, qual); recurse_push_qual(op->rarg, topquery, rtable, rti, qual);
} }
else else
{ {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.20 2004/05/30 23:40:29 neilc Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.21 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,7 +46,7 @@ typedef struct reduce_outer_joins_state ...@@ -46,7 +46,7 @@ typedef struct reduce_outer_joins_state
static bool is_simple_subquery(Query *subquery); static bool is_simple_subquery(Query *subquery);
static bool has_nullable_targetlist(Query *subquery); static bool has_nullable_targetlist(Query *subquery);
static void resolvenew_in_jointree(Node *jtnode, int varno, static void resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist); List *rtable, List *subtlist);
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode); static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
static void reduce_outer_joins_pass2(Node *jtnode, static void reduce_outer_joins_pass2(Node *jtnode,
reduce_outer_joins_state *state, reduce_outer_joins_state *state,
...@@ -243,16 +243,19 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) ...@@ -243,16 +243,19 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
subtlist = subquery->targetList; subtlist = subquery->targetList;
parse->targetList = (List *) parse->targetList = (List *)
ResolveNew((Node *) parse->targetList, ResolveNew((Node *) parse->targetList,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, parse->rtable,
subtlist, CMD_SELECT, 0);
resolvenew_in_jointree((Node *) parse->jointree, varno, resolvenew_in_jointree((Node *) parse->jointree, varno,
rte, subtlist); parse->rtable, subtlist);
Assert(parse->setOperations == NULL); Assert(parse->setOperations == NULL);
parse->havingQual = parse->havingQual =
ResolveNew(parse->havingQual, ResolveNew(parse->havingQual,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, parse->rtable,
subtlist, CMD_SELECT, 0);
parse->in_info_list = (List *) parse->in_info_list = (List *)
ResolveNew((Node *) parse->in_info_list, ResolveNew((Node *) parse->in_info_list,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, parse->rtable,
subtlist, CMD_SELECT, 0);
foreach(rt, parse->rtable) foreach(rt, parse->rtable)
{ {
...@@ -261,7 +264,8 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) ...@@ -261,7 +264,8 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
if (otherrte->rtekind == RTE_JOIN) if (otherrte->rtekind == RTE_JOIN)
otherrte->joinaliasvars = (List *) otherrte->joinaliasvars = (List *)
ResolveNew((Node *) otherrte->joinaliasvars, ResolveNew((Node *) otherrte->joinaliasvars,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, parse->rtable,
subtlist, CMD_SELECT, 0);
} }
/* /*
...@@ -477,7 +481,7 @@ has_nullable_targetlist(Query *subquery) ...@@ -477,7 +481,7 @@ has_nullable_targetlist(Query *subquery)
*/ */
static void static void
resolvenew_in_jointree(Node *jtnode, int varno, resolvenew_in_jointree(Node *jtnode, int varno,
RangeTblEntry *rte, List *subtlist) List *rtable, List *subtlist)
{ {
if (jtnode == NULL) if (jtnode == NULL)
return; return;
...@@ -491,18 +495,20 @@ resolvenew_in_jointree(Node *jtnode, int varno, ...@@ -491,18 +495,20 @@ resolvenew_in_jointree(Node *jtnode, int varno,
ListCell *l; ListCell *l;
foreach(l, f->fromlist) foreach(l, f->fromlist)
resolvenew_in_jointree(lfirst(l), varno, rte, subtlist); resolvenew_in_jointree(lfirst(l), varno, rtable, subtlist);
f->quals = ResolveNew(f->quals, f->quals = ResolveNew(f->quals,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, rtable,
subtlist, CMD_SELECT, 0);
} }
else if (IsA(jtnode, JoinExpr)) else if (IsA(jtnode, JoinExpr))
{ {
JoinExpr *j = (JoinExpr *) jtnode; JoinExpr *j = (JoinExpr *) jtnode;
resolvenew_in_jointree(j->larg, varno, rte, subtlist); resolvenew_in_jointree(j->larg, varno, rtable, subtlist);
resolvenew_in_jointree(j->rarg, varno, rte, subtlist); resolvenew_in_jointree(j->rarg, varno, rtable, subtlist);
j->quals = ResolveNew(j->quals, j->quals = ResolveNew(j->quals,
varno, 0, rte, subtlist, CMD_SELECT, 0); varno, 0, rtable,
subtlist, CMD_SELECT, 0);
/* /*
* We don't bother to update the colvars list, since it won't be * We don't bother to update the colvars list, since it won't be
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.59 2004/06/01 04:47:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.60 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -515,11 +515,19 @@ flatten_join_alias_vars_mutator(Node *node, ...@@ -515,11 +515,19 @@ flatten_join_alias_vars_mutator(Node *node,
/* Must expand whole-row reference */ /* Must expand whole-row reference */
RowExpr *rowexpr; RowExpr *rowexpr;
List *fields = NIL; List *fields = NIL;
AttrNumber attnum;
ListCell *l; ListCell *l;
attnum = 0;
foreach(l, rte->joinaliasvars) foreach(l, rte->joinaliasvars)
{ {
newvar = (Node *) lfirst(l); newvar = (Node *) lfirst(l);
attnum++;
/* Ignore dropped columns */
if (get_rte_attribute_is_dropped(context->root->rtable,
var->varno,
attnum))
continue;
/* /*
* If we are expanding an alias carried down from an upper * If we are expanding an alias carried down from an upper
* query, must adjust its varlevelsup fields. * query, must adjust its varlevelsup fields.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.133 2004/06/16 01:26:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.134 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -655,8 +655,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -655,8 +655,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg)); elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg));
leftrti = 0; /* keep compiler quiet */ leftrti = 0; /* keep compiler quiet */
} }
rte = rt_fetch(leftrti, pstate->p_rtable); expandRTE(pstate->p_rtable, leftrti, 0, false,
expandRTE(pstate, rte, &l_colnames, &l_colvars); &l_colnames, &l_colvars);
if (IsA(j->rarg, RangeTblRef)) if (IsA(j->rarg, RangeTblRef))
rightrti = ((RangeTblRef *) j->rarg)->rtindex; rightrti = ((RangeTblRef *) j->rarg)->rtindex;
...@@ -667,8 +667,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) ...@@ -667,8 +667,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg)); elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg));
rightrti = 0; /* keep compiler quiet */ rightrti = 0; /* keep compiler quiet */
} }
rte = rt_fetch(rightrti, pstate->p_rtable); expandRTE(pstate->p_rtable, rightrti, 0, false,
expandRTE(pstate, rte, &r_colnames, &r_colvars); &r_colnames, &r_colvars);
/* /*
* Natural join does not explicitly specify columns; must generate * Natural join does not explicitly specify columns; must generate
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.120 2004/08/17 18:47:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.121 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -662,29 +662,12 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ...@@ -662,29 +662,12 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
else if (node && IsA(node, Var) && else if (node && IsA(node, Var) &&
((Var *) node)->varattno == InvalidAttrNumber) ((Var *) node)->varattno == InvalidAttrNumber)
{ {
RangeTblEntry *rte; int rtindex = ((Var *) node)->varno;
AttrNumber nfields; int sublevels_up = ((Var *) node)->varlevelsup;
AttrNumber nf; List *rtable;
rte = GetRTEByRangeTablePosn(pstate, rtable = GetLevelNRangeTable(pstate, sublevels_up);
((Var *) node)->varno, expandRTE(rtable, rtindex, sublevels_up, false, NULL, &args);
((Var *) node)->varlevelsup);
nfields = list_length(rte->eref->colnames);
for (nf = 1; nf <= nfields; nf++)
{
Oid vartype;
int32 vartypmod;
if (get_rte_attribute_is_dropped(rte, nf))
continue;
get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
args = lappend(args,
makeVar(((Var *) node)->varno,
nf,
vartype,
vartypmod,
((Var *) node)->varlevelsup));
}
} }
else else
ereport(ERROR, ereport(ERROR,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.97 2004/08/17 18:47:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.98 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,6 +42,10 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, ...@@ -42,6 +42,10 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1); RangeTblEntry *rte1, const char *aliasname1);
static bool isForUpdate(ParseState *pstate, char *refname); static bool isForUpdate(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars);
static int specialAttNum(const char *attname); static int specialAttNum(const char *attname);
static void warnAutoRange(ParseState *pstate, RangeVar *relation); static void warnAutoRange(ParseState *pstate, RangeVar *relation);
...@@ -438,6 +442,27 @@ GetRTEByRangeTablePosn(ParseState *pstate, ...@@ -438,6 +442,27 @@ GetRTEByRangeTablePosn(ParseState *pstate,
return rt_fetch(varno, pstate->p_rtable); return rt_fetch(varno, pstate->p_rtable);
} }
/*
* GetLevelNRangeTable
* Get the rangetable list for the N'th query level up from current.
*/
List *
GetLevelNRangeTable(ParseState *pstate, int sublevels_up)
{
int index = 0;
while (pstate != NULL)
{
if (index == sublevels_up)
return pstate->p_rtable;
index++;
pstate = pstate->parentParseState;
}
elog(ERROR, "rangetable not found (internal error)");
return NIL; /* keep compiler quiet */
}
/* /*
* scanRTEForColumn * scanRTEForColumn
* Search the column names of a single RTE for the given name. * Search the column names of a single RTE for the given name.
...@@ -464,27 +489,20 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) ...@@ -464,27 +489,20 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
* Scan the user column names (or aliases) for a match. Complain if * Scan the user column names (or aliases) for a match. Complain if
* multiple matches. * multiple matches.
* *
* Note: because eref->colnames may include names of dropped columns, we * Note: eref->colnames may include entries for dropped columns,
* need to check for non-droppedness before accepting a match. This * but those will be empty strings that cannot match any legal SQL
* takes an extra cache lookup, but we can skip the lookup most of the * identifier, so we don't bother to test for that case here.
* time by exploiting the knowledge that dropped columns are assigned
* dummy names starting with '.', which is an unusual choice for
* actual column names.
* *
* Should the user try to fool us by altering pg_attribute.attname for a * Should this somehow go wrong and we try to access a dropped column,
* dropped column, we'll still catch it by virtue of the checks in * we'll still catch it by virtue of the checks in
* get_rte_attribute_type(), which is called by make_var(). That * get_rte_attribute_type(), which is called by make_var(). That routine
* routine has to do a cache lookup anyway, so the check there is * has to do a cache lookup anyway, so the check there is cheap.
* cheap.
*/ */
foreach(c, rte->eref->colnames) foreach(c, rte->eref->colnames)
{ {
attnum++; attnum++;
if (strcmp(strVal(lfirst(c)), colname) == 0) if (strcmp(strVal(lfirst(c)), colname) == 0)
{ {
if (colname[0] == '.' && /* see note above */
get_rte_attribute_is_dropped(rte, attnum))
continue;
if (result) if (result)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_COLUMN), (errcode(ERRCODE_AMBIGUOUS_COLUMN),
...@@ -633,6 +651,81 @@ qualifiedNameToVar(ParseState *pstate, ...@@ -633,6 +651,81 @@ qualifiedNameToVar(ParseState *pstate,
return scanRTEForColumn(pstate, rte, colname); return scanRTEForColumn(pstate, rte, colname);
} }
/*
* buildRelationAliases
* Construct the eref column name list for a relation RTE.
* This code is also used for the case of a function RTE returning
* a named composite type.
*
* tupdesc: the physical column information
* alias: the user-supplied alias, or NULL if none
* eref: the eref Alias to store column names in
*
* eref->colnames is filled in. Also, alias->colnames is rebuilt to insert
* empty strings for any dropped columns, so that it will be one-to-one with
* physical column numbers.
*/
static void
buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
{
int maxattrs = tupdesc->natts;
ListCell *aliaslc;
int numaliases;
int varattno;
int numdropped = 0;
Assert(eref->colnames == NIL);
if (alias)
{
aliaslc = list_head(alias->colnames);
numaliases = list_length(alias->colnames);
/* We'll rebuild the alias colname list */
alias->colnames = NIL;
}
else
{
aliaslc = NULL;
numaliases = 0;
}
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = tupdesc->attrs[varattno];
Value *attrname;
if (attr->attisdropped)
{
/* Always insert an empty string for a dropped column */
attrname = makeString(pstrdup(""));
if (aliaslc)
alias->colnames = lappend(alias->colnames, attrname);
numdropped++;
}
else if (aliaslc)
{
/* Use the next user-supplied alias */
attrname = (Value *) lfirst(aliaslc);
aliaslc = lnext(aliaslc);
alias->colnames = lappend(alias->colnames, attrname);
}
else
{
attrname = makeString(pstrdup(NameStr(attr->attname)));
/* we're done with the alias if any */
}
eref->colnames = lappend(eref->colnames, attrname);
}
/* Too many user-supplied aliases? */
if (aliaslc)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("table \"%s\" has %d columns available but %d columns specified",
eref->aliasname, maxattrs - numdropped, numaliases)));
}
/* /*
* Add an entry for a relation to the pstate's range table (p_rtable). * Add an entry for a relation to the pstate's range table (p_rtable).
* *
...@@ -653,10 +746,6 @@ addRangeTableEntry(ParseState *pstate, ...@@ -653,10 +746,6 @@ addRangeTableEntry(ParseState *pstate,
char *refname = alias ? alias->aliasname : relation->relname; char *refname = alias ? alias->aliasname : relation->relname;
LOCKMODE lockmode; LOCKMODE lockmode;
Relation rel; Relation rel;
Alias *eref;
int maxattrs;
int numaliases;
int varattno;
rte->rtekind = RTE_RELATION; rte->rtekind = RTE_RELATION;
rte->alias = alias; rte->alias = alias;
...@@ -671,29 +760,12 @@ addRangeTableEntry(ParseState *pstate, ...@@ -671,29 +760,12 @@ addRangeTableEntry(ParseState *pstate,
rel = heap_openrv(relation, lockmode); rel = heap_openrv(relation, lockmode);
rte->relid = RelationGetRelid(rel); rte->relid = RelationGetRelid(rel);
eref = alias ? (Alias *) copyObject(alias) : makeAlias(refname, NIL);
numaliases = list_length(eref->colnames);
/* /*
* Since the rel is open anyway, let's check that the number of column * Build the list of effective column names using user-supplied aliases
* aliases is reasonable. - Thomas 2000-02-04 * and/or actual column names.
*/ */
maxattrs = RelationGetNumberOfAttributes(rel); rte->eref = makeAlias(refname, NIL);
if (maxattrs < numaliases) buildRelationAliases(rel->rd_att, alias, rte->eref);
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("table \"%s\" has %d columns available but %d columns specified",
RelationGetRelationName(rel), maxattrs, numaliases)));
/* fill in any unspecified alias columns using actual column names */
for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->colnames = lappend(eref->colnames, makeString(attrname));
}
rte->eref = eref;
/* /*
* Drop the rel refcount, but keep the access lock till end of * Drop the rel refcount, but keep the access lock till end of
...@@ -747,10 +819,6 @@ addRangeTableEntryForRelation(ParseState *pstate, ...@@ -747,10 +819,6 @@ addRangeTableEntryForRelation(ParseState *pstate,
char *refname = alias->aliasname; char *refname = alias->aliasname;
LOCKMODE lockmode; LOCKMODE lockmode;
Relation rel; Relation rel;
Alias *eref;
int maxattrs;
int numaliases;
int varattno;
rte->rtekind = RTE_RELATION; rte->rtekind = RTE_RELATION;
rte->alias = alias; rte->alias = alias;
...@@ -765,29 +833,12 @@ addRangeTableEntryForRelation(ParseState *pstate, ...@@ -765,29 +833,12 @@ addRangeTableEntryForRelation(ParseState *pstate,
rel = heap_open(relid, lockmode); rel = heap_open(relid, lockmode);
rte->relid = relid; rte->relid = relid;
eref = (Alias *) copyObject(alias);
numaliases = list_length(eref->colnames);
/* /*
* Since the rel is open anyway, let's check that the number of column * Build the list of effective column names using user-supplied aliases
* aliases is reasonable. - Thomas 2000-02-04 * and/or actual column names.
*/ */
maxattrs = RelationGetNumberOfAttributes(rel); rte->eref = makeAlias(refname, NIL);
if (maxattrs < numaliases) buildRelationAliases(rel->rd_att, alias, rte->eref);
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("table \"%s\" has %d columns available but %d columns specified",
RelationGetRelationName(rel), maxattrs, numaliases)));
/* fill in any unspecified alias columns using actual column names */
for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->colnames = lappend(eref->colnames, makeString(attrname));
}
rte->eref = eref;
/* /*
* Drop the rel refcount, but keep the access lock till end of * Drop the rel refcount, but keep the access lock till end of
...@@ -918,8 +969,6 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -918,8 +969,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
Alias *alias = rangefunc->alias; Alias *alias = rangefunc->alias;
List *coldeflist = rangefunc->coldeflist; List *coldeflist = rangefunc->coldeflist;
Alias *eref; Alias *eref;
int numaliases;
int varattno;
rte->rtekind = RTE_FUNCTION; rte->rtekind = RTE_FUNCTION;
rte->relid = InvalidOid; rte->relid = InvalidOid;
...@@ -928,11 +977,9 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -928,11 +977,9 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->coldeflist = coldeflist; rte->coldeflist = coldeflist;
rte->alias = alias; rte->alias = alias;
eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL); eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
rte->eref = eref; rte->eref = eref;
numaliases = list_length(eref->colnames);
/* /*
* Now determine if the function returns a simple or composite type, * Now determine if the function returns a simple or composite type,
* and check/add column aliases. * and check/add column aliases.
...@@ -969,7 +1016,6 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -969,7 +1016,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
*/ */
Oid funcrelid = typeidTypeRelid(funcrettype); Oid funcrelid = typeidTypeRelid(funcrettype);
Relation rel; Relation rel;
int maxattrs;
if (!OidIsValid(funcrelid)) /* shouldn't happen if typtype is if (!OidIsValid(funcrelid)) /* shouldn't happen if typtype is
* 'c' */ * 'c' */
...@@ -981,26 +1027,8 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -981,26 +1027,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
*/ */
rel = relation_open(funcrelid, AccessShareLock); rel = relation_open(funcrelid, AccessShareLock);
/* /* Build the column alias list */
* Since the rel is open anyway, let's check that the number of buildRelationAliases(rel->rd_att, alias, eref);
* column aliases is reasonable.
*/
maxattrs = RelationGetNumberOfAttributes(rel);
if (maxattrs < numaliases)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("table \"%s\" has %d columns available but %d columns specified",
RelationGetRelationName(rel),
maxattrs, numaliases)));
/* fill in alias columns using actual column names */
for (varattno = numaliases; varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->colnames = lappend(eref->colnames, makeString(attrname));
}
/* /*
* Drop the rel refcount, but keep the access lock till end of * Drop the rel refcount, but keep the access lock till end of
...@@ -1015,12 +1043,16 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -1015,12 +1043,16 @@ addRangeTableEntryForFunction(ParseState *pstate,
* Must be a base data type, i.e. scalar. Just add one alias * Must be a base data type, i.e. scalar. Just add one alias
* column named for the function. * column named for the function.
*/ */
if (numaliases > 1) if (alias && alias->colnames != NIL)
{
if (list_length(alias->colnames) != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE), (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("too many column aliases specified for function %s", errmsg("too many column aliases specified for function %s",
funcname))); funcname)));
if (numaliases == 0) eref->colnames = copyObject(alias->colnames);
}
else
eref->colnames = list_make1(makeString(eref->aliasname)); eref->colnames = list_make1(makeString(eref->aliasname));
} }
else if (functyptype == 'p' && funcrettype == RECORDOID) else if (functyptype == 'p' && funcrettype == RECORDOID)
...@@ -1028,7 +1060,6 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -1028,7 +1060,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
ListCell *col; ListCell *col;
/* Use the column definition list to form the alias list */ /* Use the column definition list to form the alias list */
eref->colnames = NIL;
foreach(col, coldeflist) foreach(col, coldeflist)
{ {
ColumnDef *n = lfirst(col); ColumnDef *n = lfirst(col);
...@@ -1202,77 +1233,42 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation) ...@@ -1202,77 +1233,42 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
return rte; return rte;
} }
/* expandRTE() /*
* expandRTE -- expand the columns of a rangetable entry
* *
* Given a rangetable entry, create lists of its column names (aliases if * This creates lists of an RTE's column names (aliases if provided, else
* provided, else real names) and Vars for each column. Only user columns * real names) and Vars for each column. Only user columns are considered.
* are considered, since this is primarily used to expand '*' and determine * If include_dropped is FALSE then dropped columns are omitted from the
* the contents of JOIN tables. * results. If include_dropped is TRUE then empty strings and NULL constants
* (not Vars!) are returned for dropped columns.
* *
* The target RTE is the rtindex'th entry of rtable. (The whole rangetable
* must be passed since we need it to determine dropped-ness for JOIN columns.)
* sublevels_up is the varlevelsup value to use in the created Vars.
*
* The output lists go into *colnames and *colvars.
* If only one of the two kinds of output list is needed, pass NULL for the * If only one of the two kinds of output list is needed, pass NULL for the
* output pointer for the unwanted one. * output pointer for the unwanted one.
*/ */
void void
expandRTE(ParseState *pstate, RangeTblEntry *rte, expandRTE(List *rtable, int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars) List **colnames, List **colvars)
{ {
int rtindex, RangeTblEntry *rte = rt_fetch(rtindex, rtable);
sublevels_up, int varattno;
varattno;
if (colnames) if (colnames)
*colnames = NIL; *colnames = NIL;
if (colvars) if (colvars)
*colvars = NIL; *colvars = NIL;
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
switch (rte->rtekind) switch (rte->rtekind)
{ {
case RTE_RELATION: case RTE_RELATION:
{
/* Ordinary relation RTE */ /* Ordinary relation RTE */
Relation rel; expandRelation(rte->relid, rte->eref, rtindex, sublevels_up,
int maxattrs; include_dropped, colnames, colvars);
int numaliases;
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = list_length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (attr->attisdropped)
continue;
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(list_nth(rte->eref->colnames, varattno));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
heap_close(rel, AccessShareLock);
}
break; break;
case RTE_SUBQUERY: case RTE_SUBQUERY:
{ {
...@@ -1318,60 +1314,22 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -1318,60 +1314,22 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Function RTE */ /* Function RTE */
Oid funcrettype = exprType(rte->funcexpr); Oid funcrettype = exprType(rte->funcexpr);
char functyptype = get_typtype(funcrettype); char functyptype = get_typtype(funcrettype);
List *coldeflist = rte->coldeflist;
if (functyptype == 'c') if (functyptype == 'c')
{ {
/* /*
* Composite data type, i.e. a table's row type Same * Composite data type, i.e. a table's row type
* as ordinary relation RTE *
* Same as ordinary relation RTE
*/ */
Oid funcrelid = typeidTypeRelid(funcrettype); Oid funcrelid = typeidTypeRelid(funcrettype);
Relation rel;
int maxattrs;
int numaliases;
if (!OidIsValid(funcrelid)) /* shouldn't happen */ if (!OidIsValid(funcrelid)) /* shouldn't happen */
elog(ERROR, "invalid typrelid for complex type %u", elog(ERROR, "invalid typrelid for complex type %u",
funcrettype); funcrettype);
rel = relation_open(funcrelid, AccessShareLock); expandRelation(funcrelid, rte->eref, rtindex, sublevels_up,
maxattrs = RelationGetNumberOfAttributes(rel); include_dropped, colnames, colvars);
numaliases = list_length(rte->eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (attr->attisdropped)
continue;
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(list_nth(rte->eref->colnames, varattno));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex,
attr->attnum,
attr->atttypid,
attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
relation_close(rel, AccessShareLock);
} }
else if (functyptype == 'b' || functyptype == 'd') else if (functyptype == 'b' || functyptype == 'd')
{ {
...@@ -1395,6 +1353,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -1395,6 +1353,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
} }
else if (functyptype == 'p' && funcrettype == RECORDOID) else if (functyptype == 'p' && funcrettype == RECORDOID)
{ {
List *coldeflist = rte->coldeflist;
ListCell *col; ListCell *col;
int attnum = 0; int attnum = 0;
...@@ -1447,11 +1406,41 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -1447,11 +1406,41 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{ {
varattno++; varattno++;
/*
* During ordinary parsing, there will never be any
* deleted columns in the join; but we have to check
* since this routine is also used by the rewriter,
* and joins found in stored rules might have join
* columns for since-deleted columns.
*/
if (get_rte_attribute_is_dropped(rtable, rtindex,
varattno))
{
if (include_dropped)
{
if (colnames)
*colnames = lappend(*colnames,
makeString(pstrdup("")));
if (colvars)
{
/*
* can't use atttypid here, but it doesn't
* really matter what type the Const claims to
* be.
*/
*colvars = lappend(*colvars,
makeNullConst(INT4OID));
}
}
continue;
}
if (colnames) if (colnames)
{ {
char *label = strVal(lfirst(colname)); char *label = strVal(lfirst(colname));
*colnames = lappend(*colnames, makeString(pstrdup(label))); *colnames = lappend(*colnames,
makeString(pstrdup(label)));
} }
if (colvars) if (colvars)
...@@ -1474,13 +1463,78 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, ...@@ -1474,13 +1463,78 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
} }
} }
/*
* expandRelation -- expandRTE subroutine
*/
static void
expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars)
{
Relation rel;
int varattno;
int maxattrs;
int numaliases;
rel = relation_open(relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
numaliases = list_length(eref->colnames);
for (varattno = 0; varattno < maxattrs; varattno++)
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
if (attr->attisdropped)
{
if (include_dropped)
{
if (colnames)
*colnames = lappend(*colnames, makeString(pstrdup("")));
if (colvars)
{
/*
* can't use atttypid here, but it doesn't really matter
* what type the Const claims to be.
*/
*colvars = lappend(*colvars, makeNullConst(INT4OID));
}
}
continue;
}
if (colnames)
{
char *label;
if (varattno < numaliases)
label = strVal(list_nth(eref->colnames, varattno));
else
label = NameStr(attr->attname);
*colnames = lappend(*colnames, makeString(pstrdup(label)));
}
if (colvars)
{
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
relation_close(rel, AccessShareLock);
}
/* /*
* expandRelAttrs - * expandRelAttrs -
* Workhorse for "*" expansion: produce a list of targetentries * Workhorse for "*" expansion: produce a list of targetentries
* for the attributes of the rte * for the attributes of the rte
*/ */
List * List *
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte) expandRelAttrs(ParseState *pstate, List *rtable, int rtindex, int sublevels_up)
{ {
List *names, List *names,
*vars; *vars;
...@@ -1488,9 +1542,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte) ...@@ -1488,9 +1542,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
*var; *var;
List *te_list = NIL; List *te_list = NIL;
expandRTE(pstate, rte, &names, &vars); expandRTE(rtable, rtindex, sublevels_up, false, &names, &vars);
forboth (name, names, var, vars) forboth(name, names, var, vars)
{ {
char *label = strVal(lfirst(name)); char *label = strVal(lfirst(name));
Node *varnode = (Node *) lfirst(var); Node *varnode = (Node *) lfirst(var);
...@@ -1698,15 +1752,16 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, ...@@ -1698,15 +1752,16 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
* Check whether attempted attribute ref is to a dropped column * Check whether attempted attribute ref is to a dropped column
*/ */
bool bool
get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) get_rte_attribute_is_dropped(List *rtable, int rtindex, AttrNumber attnum)
{ {
RangeTblEntry *rte = rt_fetch(rtindex, rtable);
bool result; bool result;
switch (rte->rtekind) switch (rte->rtekind)
{ {
case RTE_RELATION: case RTE_RELATION:
{ {
/* Plain relation RTE --- get the attribute's type info */ /* Plain relation RTE --- get the attribute's catalog entry */
HeapTuple tp; HeapTuple tp;
Form_pg_attribute att_tup; Form_pg_attribute att_tup;
...@@ -1723,9 +1778,37 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) ...@@ -1723,9 +1778,37 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
} }
break; break;
case RTE_SUBQUERY: case RTE_SUBQUERY:
/* Subselect RTEs never have dropped columns */
result = false;
break;
case RTE_JOIN: case RTE_JOIN:
/* Subselect and join RTEs never have dropped columns */ {
/*
* A join RTE would not have dropped columns when constructed,
* but one in a stored rule might contain columns that were
* dropped from the underlying tables, if said columns are
* nowhere explicitly referenced in the rule. So we have to
* recursively look at the referenced column.
*/
Var *aliasvar;
if (attnum <= 0 ||
attnum > list_length(rte->joinaliasvars))
elog(ERROR, "invalid varattno %d", attnum);
aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
/*
* If the list item isn't a simple Var, then it must
* represent a merged column, ie a USING column, and so it
* couldn't possibly be dropped (since it's referenced in
* the join clause).
*/
if (!IsA(aliasvar, Var))
result = false; result = false;
else
result = get_rte_attribute_is_dropped(rtable,
aliasvar->varno,
aliasvar->varattno);
}
break; break;
case RTE_FUNCTION: case RTE_FUNCTION:
{ {
...@@ -1736,8 +1819,9 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) ...@@ -1736,8 +1819,9 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
if (OidIsValid(funcrelid)) if (OidIsValid(funcrelid))
{ {
/* /*
* Composite data type, i.e. a table's row type Same * Composite data type, i.e. a table's row type
* as ordinary relation RTE *
* Same as ordinary relation RTE
*/ */
HeapTuple tp; HeapTuple tp;
Form_pg_attribute att_tup; Form_pg_attribute att_tup;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.122 2004/06/19 18:19:55 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.123 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -699,6 +699,8 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref) ...@@ -699,6 +699,8 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
char *relname; char *relname;
RangeTblEntry *rte; RangeTblEntry *rte;
int sublevels_up; int sublevels_up;
int rtindex;
List *rtable;
switch (numnames) switch (numnames)
{ {
...@@ -743,7 +745,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref) ...@@ -743,7 +745,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
rte = addImplicitRTE(pstate, makeRangeVar(schemaname, rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
relname)); relname));
return expandRelAttrs(pstate, rte); rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
rtable = GetLevelNRangeTable(pstate, sublevels_up);
return expandRelAttrs(pstate, rtable, rtindex, sublevels_up);
} }
} }
...@@ -765,29 +770,31 @@ ExpandAllTables(ParseState *pstate) ...@@ -765,29 +770,31 @@ ExpandAllTables(ParseState *pstate)
foreach(ns, pstate->p_namespace) foreach(ns, pstate->p_namespace)
{ {
Node *n = (Node *) lfirst(ns); Node *n = (Node *) lfirst(ns);
int rtindex;
RangeTblEntry *rte; RangeTblEntry *rte;
if (IsA(n, RangeTblRef)) if (IsA(n, RangeTblRef))
rte = rt_fetch(((RangeTblRef *) n)->rtindex, rtindex = ((RangeTblRef *) n)->rtindex;
pstate->p_rtable);
else if (IsA(n, JoinExpr)) else if (IsA(n, JoinExpr))
rte = rt_fetch(((JoinExpr *) n)->rtindex, rtindex = ((JoinExpr *) n)->rtindex;
pstate->p_rtable);
else else
{ {
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n)); elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
rte = NULL; /* keep compiler quiet */ rtindex = 0; /* keep compiler quiet */
} }
/* /*
* Ignore added-on relations that were not listed in the FROM * Ignore added-on relations that were not listed in the FROM
* clause. * clause.
*/ */
rte = rt_fetch(rtindex, pstate->p_rtable);
if (!rte->inFromCl) if (!rte->inFromCl)
continue; continue;
found_table = true; found_table = true;
target = list_concat(target, expandRelAttrs(pstate, rte)); target = list_concat(target,
expandRelAttrs(pstate, pstate->p_rtable,
rtindex, 0));
} }
/* Check for SELECT *; */ /* Check for SELECT *; */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.141 2004/08/07 17:40:49 tgl Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.142 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -214,8 +214,7 @@ rewriteRuleAction(Query *parsetree, ...@@ -214,8 +214,7 @@ rewriteRuleAction(Query *parsetree,
sub_action = (Query *) ResolveNew((Node *) sub_action, sub_action = (Query *) ResolveNew((Node *) sub_action,
new_varno, new_varno,
0, 0,
rt_fetch(new_varno, sub_action->rtable,
sub_action->rtable),
parsetree->targetList, parsetree->targetList,
event, event,
current_varno); current_varno);
...@@ -1040,7 +1039,7 @@ CopyAndAddInvertedQual(Query *parsetree, ...@@ -1040,7 +1039,7 @@ CopyAndAddInvertedQual(Query *parsetree,
new_qual = ResolveNew(new_qual, new_qual = ResolveNew(new_qual,
PRS2_NEW_VARNO, PRS2_NEW_VARNO,
0, 0,
rt_fetch(rt_index, parsetree->rtable), parsetree->rtable,
parsetree->targetList, parsetree->targetList,
event, event,
rt_index); rt_index);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.85 2004/08/17 18:47:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.86 2004/08/19 20:57:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_clause.h" #include "parser/parse_relation.h"
#include "rewrite/rewriteManip.h" #include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -853,9 +853,10 @@ AddInvertedQual(Query *parsetree, Node *qual) ...@@ -853,9 +853,10 @@ AddInvertedQual(Query *parsetree, Node *qual)
* If not, we either change the unmatched Var's varno to update_varno * If not, we either change the unmatched Var's varno to update_varno
* (when event == CMD_UPDATE) or replace it with a constant NULL. * (when event == CMD_UPDATE) or replace it with a constant NULL.
* *
* The caller must also provide target_rte, the RTE describing the target * The caller must also provide target_rtable, the rangetable containing
* relation. This is needed to handle whole-row Vars referencing the target. * the target relation (which must be described by the target_varno'th
* We expand such Vars into RowExpr constructs. * RTE in that list). This is needed to handle whole-row Vars referencing
* the target. We expand such Vars into RowExpr constructs.
* *
* Note: the business with inserted_sublink is needed to update hasSubLinks * Note: the business with inserted_sublink is needed to update hasSubLinks
* in subqueries when the replacement adds a subquery inside a subquery. * in subqueries when the replacement adds a subquery inside a subquery.
...@@ -868,7 +869,7 @@ typedef struct ...@@ -868,7 +869,7 @@ typedef struct
{ {
int target_varno; int target_varno;
int sublevels_up; int sublevels_up;
RangeTblEntry *target_rte; List *target_rtable;
List *targetlist; List *targetlist;
int event; int event;
int update_varno; int update_varno;
...@@ -931,40 +932,21 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -931,40 +932,21 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
if (var->varattno == InvalidAttrNumber) if (var->varattno == InvalidAttrNumber)
{ {
/* Must expand whole-tuple reference into RowExpr */ /* Must expand whole-tuple reference into RowExpr */
RangeTblEntry *rte = context->target_rte;
RowExpr *rowexpr; RowExpr *rowexpr;
List *fields = NIL; List *fields;
AttrNumber nfields = list_length(rte->eref->colnames);
AttrNumber nf;
for (nf = 1; nf <= nfields; nf++)
{
if (get_rte_attribute_is_dropped(rte, nf))
{
/* /*
* can't determine att type here, but it doesn't * If generating an expansion for a var of a named rowtype
* really matter what type the Const claims to be. * (ie, this is a plain relation RTE), then we must include
*/ * dummy items for dropped columns. If the var is RECORD
fields = lappend(fields, * (ie, this is a JOIN), then omit dropped columns.
makeNullConst(INT4OID)); */
} expandRTE(context->target_rtable, this_varno, this_varlevelsup,
else (var->vartype != RECORDOID),
{ NULL, &fields);
Oid vartype; /* Adjust the generated per-field Vars... */
int32 vartypmod; fields = (List *) ResolveNew_mutator((Node *) fields,
Var *newvar; context);
get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
newvar = makeVar(this_varno,
nf,
vartype,
vartypmod,
this_varlevelsup);
fields = lappend(fields,
resolve_one_var(newvar, context));
}
}
rowexpr = makeNode(RowExpr); rowexpr = makeNode(RowExpr);
rowexpr->args = fields; rowexpr->args = fields;
rowexpr->row_typeid = var->vartype; rowexpr->row_typeid = var->vartype;
...@@ -1003,14 +985,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -1003,14 +985,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
Node * Node *
ResolveNew(Node *node, int target_varno, int sublevels_up, ResolveNew(Node *node, int target_varno, int sublevels_up,
RangeTblEntry *target_rte, List *target_rtable,
List *targetlist, int event, int update_varno) List *targetlist, int event, int update_varno)
{ {
ResolveNew_context context; ResolveNew_context context;
context.target_varno = target_varno; context.target_varno = target_varno;
context.sublevels_up = sublevels_up; context.sublevels_up = sublevels_up;
context.target_rte = target_rte; context.target_rtable = target_rtable;
context.targetlist = targetlist; context.targetlist = targetlist;
context.event = event; context.event = event;
context.update_varno = update_varno; context.update_varno = update_varno;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.177 2004/08/17 18:47:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.178 2004/08/19 20:57:41 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -203,6 +203,8 @@ static void get_sublink_expr(SubLink *sublink, deparse_context *context); ...@@ -203,6 +203,8 @@ static void get_sublink_expr(SubLink *sublink, deparse_context *context);
static void get_from_clause(Query *query, deparse_context *context); static void get_from_clause(Query *query, deparse_context *context);
static void get_from_clause_item(Node *jtnode, Query *query, static void get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context); deparse_context *context);
static void get_from_clause_alias(Alias *alias, int varno,
Query *query, deparse_context *context);
static void get_from_clause_coldeflist(List *coldeflist, static void get_from_clause_coldeflist(List *coldeflist,
deparse_context *context); deparse_context *context);
static void get_opclass_name(Oid opclass, Oid actual_datatype, static void get_opclass_name(Oid opclass, Oid actual_datatype,
...@@ -3962,20 +3964,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -3962,20 +3964,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
appendStringInfo(buf, " %s", appendStringInfo(buf, " %s",
quote_identifier(rte->alias->aliasname)); quote_identifier(rte->alias->aliasname));
gavealias = true; gavealias = true;
if (rte->alias->colnames != NIL && coldeflist == NIL) if (coldeflist == NIL)
{ get_from_clause_alias(rte->alias, varno, query, context);
ListCell *col;
appendStringInfoChar(buf, '(');
foreach(col, rte->alias->colnames)
{
if (col != list_head(rte->alias->colnames))
appendStringInfo(buf, ", ");
appendStringInfoString(buf,
quote_identifier(strVal(lfirst(col))));
}
appendStringInfoChar(buf, ')');
}
} }
else if (rte->rtekind == RTE_RELATION && else if (rte->rtekind == RTE_RELATION &&
strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0) strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0)
...@@ -4128,25 +4118,49 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) ...@@ -4128,25 +4118,49 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{ {
appendStringInfo(buf, " %s", appendStringInfo(buf, " %s",
quote_identifier(j->alias->aliasname)); quote_identifier(j->alias->aliasname));
if (j->alias->colnames != NIL) get_from_clause_alias(j->alias, j->rtindex, query, context);
{ }
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
}
/*
* get_from_clause_alias - reproduce column alias list
*
* This is tricky because we must ignore dropped columns.
*/
static void
get_from_clause_alias(Alias *alias, int varno,
Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *col; ListCell *col;
AttrNumber attnum;
bool first = true;
appendStringInfoChar(buf, '('); if (alias == NULL || alias->colnames == NIL)
foreach(col, j->alias->colnames) return; /* definitely nothing to do */
attnum = 0;
foreach(col, alias->colnames)
{
attnum++;
if (get_rte_attribute_is_dropped(query->rtable, varno, attnum))
continue;
if (first)
{ {
if (col != list_head(j->alias->colnames)) appendStringInfoChar(buf, '(');
first = false;
}
else
appendStringInfo(buf, ", "); appendStringInfo(buf, ", ");
appendStringInfoString(buf, appendStringInfoString(buf,
quote_identifier(strVal(lfirst(col)))); quote_identifier(strVal(lfirst(col))));
} }
if (!first)
appendStringInfoChar(buf, ')'); appendStringInfoChar(buf, ')');
}
}
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
} }
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.265 2004/08/04 21:34:24 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.266 2004/08/19 20:57:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -447,6 +447,22 @@ typedef struct DefElem ...@@ -447,6 +447,22 @@ typedef struct DefElem
* eref->aliasname is required to be present, and should generally be used * eref->aliasname is required to be present, and should generally be used
* to identify the RTE for error messages etc. * to identify the RTE for error messages etc.
* *
* In RELATION RTEs, the colnames in both alias and eref are indexed by
* physical attribute number; this means there must be colname entries for
* dropped columns. When building an RTE we insert empty strings ("") for
* dropped columns. Note however that a stored rule may have nonempty
* colnames for columns dropped since the rule was created (and for that
* matter the colnames might be out of date due to column renamings).
* The same comments apply to FUNCTION RTEs when the function's return type
* is a named composite type.
*
* In JOIN RTEs, the colnames in both alias and eref are one-to-one with
* joinaliasvars entries. A JOIN RTE will omit columns of its inputs when
* those columns are known to be dropped at parse time. Again, however,
* a stored rule might contain entries for columns dropped since the rule
* was created. (This is only possible for columns not actually referenced
* in the rule.)
*
* inh is TRUE for relation references that should be expanded to include * inh is TRUE for relation references that should be expanded to include
* inheritance children, if the rel has any. This *must* be FALSE for * inheritance children, if the rel has any. This *must* be FALSE for
* RTEs other than RTE_RELATION entries. * RTEs other than RTE_RELATION entries.
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.101 2004/08/17 18:47:09 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.102 2004/08/19 20:57:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -98,9 +98,9 @@ typedef struct Resdom ...@@ -98,9 +98,9 @@ typedef struct Resdom
* specifies an alias for a range variable; the alias might also * specifies an alias for a range variable; the alias might also
* specify renaming of columns within the table. * specify renaming of columns within the table.
* *
* Note: colnames is a list of Value nodes (always strings). In an RTE's * Note: colnames is a list of Value nodes (always strings). In Alias structs
* eref Alias, the colnames list includes dropped columns, so that the * associated with RTEs, there may be entries corresponding to dropped
* colname list position matches the physical attribute number. * columns; these are normally empty strings (""). See parsenodes.h for info.
*/ */
typedef struct Alias typedef struct Alias
{ {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.44 2004/04/18 18:12:58 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.45 2004/08/19 20:57:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,6 +30,7 @@ extern int RTERangeTablePosn(ParseState *pstate, ...@@ -30,6 +30,7 @@ extern int RTERangeTablePosn(ParseState *pstate,
extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate, extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
int varno, int varno,
int sublevels_up); int sublevels_up);
extern List *GetLevelNRangeTable(ParseState *pstate, int sublevels_up);
extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname); char *colname);
extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly); extern Node *colNameToVar(ParseState *pstate, char *colname, bool localonly);
...@@ -66,9 +67,11 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate, ...@@ -66,9 +67,11 @@ extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList, bool addToNameSpace); bool addToJoinList, bool addToNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation); extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation);
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte, extern void expandRTE(List *rtable, int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars); List **colnames, List **colvars);
extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte); extern List *expandRelAttrs(ParseState *pstate, List *rtable,
int rtindex, int sublevels_up);
extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK); extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK);
extern Name attnumAttName(Relation rd, int attid); extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.25 2004/08/17 18:47:09 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.26 2004/08/19 20:57:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -59,7 +59,7 @@ extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, ...@@ -59,7 +59,7 @@ extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
* Check whether an attribute of an RTE has been dropped (note that * Check whether an attribute of an RTE has been dropped (note that
* get_rte_attribute_type will fail on such an attr) * get_rte_attribute_type will fail on such an attr)
*/ */
extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte, extern bool get_rte_attribute_is_dropped(List *rtable, int rtindex,
AttrNumber attnum); AttrNumber attnum);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.35 2004/05/10 22:44:49 tgl Exp $ * $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.36 2004/08/19 20:57:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,7 +38,7 @@ extern bool checkExprHasAggs(Node *node); ...@@ -38,7 +38,7 @@ extern bool checkExprHasAggs(Node *node);
extern bool checkExprHasSubLink(Node *node); extern bool checkExprHasSubLink(Node *node);
extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up, extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
RangeTblEntry *target_rte, List *target_rtable,
List *targetlist, int event, int update_varno); List *targetlist, int event, int update_varno);
#endif /* REWRITEMANIP_H */ #endif /* REWRITEMANIP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment