aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-08-19 20:57:41 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-08-19 20:57:41 +0000
commitbbd6eb5b958ef38f786089fd4a03d650d4b7220e (patch)
tree3dbc25578ccd347b586018f6b8a6d8d6baa84ac7 /src/backend/parser/parse_relation.c
parent040450beef4f3fdafaa9e20dbb0ee3e00c5856ba (diff)
downloadpostgresql-bbd6eb5b958ef38f786089fd4a03d650d4b7220e.tar.gz
postgresql-bbd6eb5b958ef38f786089fd4a03d650d4b7220e.zip
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.
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c494
1 files changed, 289 insertions, 205 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 3f32f8c80f5..1a9aa2cd73f 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* 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,
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1);
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 void warnAutoRange(ParseState *pstate, RangeVar *relation);
@@ -439,6 +443,27 @@ GetRTEByRangeTablePosn(ParseState *pstate,
}
/*
+ * 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
* Search the column names of a single RTE for the given name.
* If found, return an appropriate Var node, else return NULL.
@@ -464,27 +489,20 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
* Scan the user column names (or aliases) for a match. Complain if
* multiple matches.
*
- * Note: because eref->colnames may include names of dropped columns, we
- * need to check for non-droppedness before accepting a match. This
- * takes an extra cache lookup, but we can skip the lookup most of the
- * time by exploiting the knowledge that dropped columns are assigned
- * dummy names starting with '.', which is an unusual choice for
- * actual column names.
+ * Note: eref->colnames may include entries for dropped columns,
+ * but those will be empty strings that cannot match any legal SQL
+ * identifier, so we don't bother to test for that case here.
*
- * Should the user try to fool us by altering pg_attribute.attname for a
- * dropped column, we'll still catch it by virtue of the checks in
- * get_rte_attribute_type(), which is called by make_var(). That
- * routine has to do a cache lookup anyway, so the check there is
- * cheap.
+ * Should this somehow go wrong and we try to access a dropped column,
+ * we'll still catch it by virtue of the checks in
+ * get_rte_attribute_type(), which is called by make_var(). That routine
+ * has to do a cache lookup anyway, so the check there is cheap.
*/
foreach(c, rte->eref->colnames)
{
attnum++;
if (strcmp(strVal(lfirst(c)), colname) == 0)
{
- if (colname[0] == '.' && /* see note above */
- get_rte_attribute_is_dropped(rte, attnum))
- continue;
if (result)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_COLUMN),
@@ -634,6 +652,81 @@ qualifiedNameToVar(ParseState *pstate,
}
/*
+ * 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).
*
* If pstate is NULL, we just build an RTE and return it without adding it
@@ -653,10 +746,6 @@ addRangeTableEntry(ParseState *pstate,
char *refname = alias ? alias->aliasname : relation->relname;
LOCKMODE lockmode;
Relation rel;
- Alias *eref;
- int maxattrs;
- int numaliases;
- int varattno;
rte->rtekind = RTE_RELATION;
rte->alias = alias;
@@ -671,29 +760,12 @@ addRangeTableEntry(ParseState *pstate,
rel = heap_openrv(relation, lockmode);
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
- * aliases is reasonable. - Thomas 2000-02-04
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names.
*/
- 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 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;
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(rel->rd_att, alias, rte->eref);
/*
* Drop the rel refcount, but keep the access lock till end of
@@ -747,10 +819,6 @@ addRangeTableEntryForRelation(ParseState *pstate,
char *refname = alias->aliasname;
LOCKMODE lockmode;
Relation rel;
- Alias *eref;
- int maxattrs;
- int numaliases;
- int varattno;
rte->rtekind = RTE_RELATION;
rte->alias = alias;
@@ -765,29 +833,12 @@ addRangeTableEntryForRelation(ParseState *pstate,
rel = heap_open(relid, lockmode);
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
- * aliases is reasonable. - Thomas 2000-02-04
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names.
*/
- 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 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;
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(rel->rd_att, alias, rte->eref);
/*
* Drop the rel refcount, but keep the access lock till end of
@@ -918,8 +969,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
Alias *alias = rangefunc->alias;
List *coldeflist = rangefunc->coldeflist;
Alias *eref;
- int numaliases;
- int varattno;
rte->rtekind = RTE_FUNCTION;
rte->relid = InvalidOid;
@@ -928,11 +977,9 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->coldeflist = coldeflist;
rte->alias = alias;
- eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL);
+ eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
rte->eref = eref;
- numaliases = list_length(eref->colnames);
-
/*
* Now determine if the function returns a simple or composite type,
* and check/add column aliases.
@@ -969,7 +1016,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
*/
Oid funcrelid = typeidTypeRelid(funcrettype);
Relation rel;
- int maxattrs;
if (!OidIsValid(funcrelid)) /* shouldn't happen if typtype is
* 'c' */
@@ -981,26 +1027,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
*/
rel = relation_open(funcrelid, AccessShareLock);
- /*
- * Since the rel is open anyway, let's check that the number of
- * 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));
- }
+ /* Build the column alias list */
+ buildRelationAliases(rel->rd_att, alias, eref);
/*
* Drop the rel refcount, but keep the access lock till end of
@@ -1015,12 +1043,16 @@ addRangeTableEntryForFunction(ParseState *pstate,
* Must be a base data type, i.e. scalar. Just add one alias
* column named for the function.
*/
- if (numaliases > 1)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
- errmsg("too many column aliases specified for function %s",
- funcname)));
- if (numaliases == 0)
+ if (alias && alias->colnames != NIL)
+ {
+ if (list_length(alias->colnames) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("too many column aliases specified for function %s",
+ funcname)));
+ eref->colnames = copyObject(alias->colnames);
+ }
+ else
eref->colnames = list_make1(makeString(eref->aliasname));
}
else if (functyptype == 'p' && funcrettype == RECORDOID)
@@ -1028,7 +1060,6 @@ addRangeTableEntryForFunction(ParseState *pstate,
ListCell *col;
/* Use the column definition list to form the alias list */
- eref->colnames = NIL;
foreach(col, coldeflist)
{
ColumnDef *n = lfirst(col);
@@ -1202,77 +1233,42 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation)
return rte;
}
-/* expandRTE()
+/*
+ * expandRTE -- expand the columns of a rangetable entry
*
- * 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.
+ * This creates lists of an RTE's column names (aliases if provided, else
+ * real names) and Vars for each column. Only user columns are considered.
+ * If include_dropped is FALSE then dropped columns are omitted from the
+ * 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
* output pointer for the unwanted one.
*/
void
-expandRTE(ParseState *pstate, RangeTblEntry *rte,
+expandRTE(List *rtable, int rtindex, int sublevels_up,
+ bool include_dropped,
List **colnames, List **colvars)
{
- int rtindex,
- sublevels_up,
- varattno;
+ RangeTblEntry *rte = rt_fetch(rtindex, rtable);
+ int varattno;
if (colnames)
*colnames = NIL;
if (colvars)
*colvars = NIL;
- /* Need the RT index of the entry for creating Vars */
- rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
-
switch (rte->rtekind)
{
case RTE_RELATION:
- {
- /* Ordinary relation RTE */
- Relation rel;
- int maxattrs;
- 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);
- }
+ /* Ordinary relation RTE */
+ expandRelation(rte->relid, rte->eref, rtindex, sublevels_up,
+ include_dropped, colnames, colvars);
break;
case RTE_SUBQUERY:
{
@@ -1318,60 +1314,22 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Function RTE */
Oid funcrettype = exprType(rte->funcexpr);
char functyptype = get_typtype(funcrettype);
- List *coldeflist = rte->coldeflist;
if (functyptype == 'c')
{
/*
- * Composite data type, i.e. a table's row type Same
- * as ordinary relation RTE
+ * Composite data type, i.e. a table's row type
+ *
+ * Same as ordinary relation RTE
*/
Oid funcrelid = typeidTypeRelid(funcrettype);
- Relation rel;
- int maxattrs;
- int numaliases;
if (!OidIsValid(funcrelid)) /* shouldn't happen */
elog(ERROR, "invalid typrelid for complex type %u",
funcrettype);
- rel = relation_open(funcrelid, 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);
- }
- }
-
- relation_close(rel, AccessShareLock);
+ expandRelation(funcrelid, rte->eref, rtindex, sublevels_up,
+ include_dropped, colnames, colvars);
}
else if (functyptype == 'b' || functyptype == 'd')
{
@@ -1395,6 +1353,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
}
else if (functyptype == 'p' && funcrettype == RECORDOID)
{
+ List *coldeflist = rte->coldeflist;
ListCell *col;
int attnum = 0;
@@ -1447,11 +1406,41 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
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)
{
char *label = strVal(lfirst(colname));
- *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ *colnames = lappend(*colnames,
+ makeString(pstrdup(label)));
}
if (colvars)
@@ -1475,12 +1464,77 @@ 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 -
* Workhorse for "*" expansion: produce a list of targetentries
* for the attributes of the rte
*/
List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
+expandRelAttrs(ParseState *pstate, List *rtable, int rtindex, int sublevels_up)
{
List *names,
*vars;
@@ -1488,9 +1542,9 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
*var;
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));
Node *varnode = (Node *) lfirst(var);
@@ -1698,15 +1752,16 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
* Check whether attempted attribute ref is to a dropped column
*/
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;
switch (rte->rtekind)
{
case RTE_RELATION:
{
- /* Plain relation RTE --- get the attribute's type info */
+ /* Plain relation RTE --- get the attribute's catalog entry */
HeapTuple tp;
Form_pg_attribute att_tup;
@@ -1723,10 +1778,38 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
}
break;
case RTE_SUBQUERY:
- case RTE_JOIN:
- /* Subselect and join RTEs never have dropped columns */
+ /* Subselect RTEs never have dropped columns */
result = false;
break;
+ case RTE_JOIN:
+ {
+ /*
+ * 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;
+ else
+ result = get_rte_attribute_is_dropped(rtable,
+ aliasvar->varno,
+ aliasvar->varattno);
+ }
+ break;
case RTE_FUNCTION:
{
/* Function RTE */
@@ -1736,8 +1819,9 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
if (OidIsValid(funcrelid))
{
/*
- * Composite data type, i.e. a table's row type Same
- * as ordinary relation RTE
+ * Composite data type, i.e. a table's row type
+ *
+ * Same as ordinary relation RTE
*/
HeapTuple tp;
Form_pg_attribute att_tup;