summaryrefslogtreecommitdiffstats
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/parser/parse_relation.c
parentInitial commit. (diff)
downloadpostgresql-14-upstream.tar.xz
postgresql-14-upstream.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c3711
1 files changed, 3711 insertions, 0 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
new file mode 100644
index 0000000..713ec0e
--- /dev/null
+++ b/src/backend/parser/parse_relation.c
@@ -0,0 +1,3711 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_relation.c
+ * parser support routines dealing with relations
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/parser/parse_relation.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+
+#include "access/htup_details.h"
+#include "access/relation.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "catalog/heap.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "funcapi.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_enr.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_type.h"
+#include "parser/parsetree.h"
+#include "storage/lmgr.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/varlena.h"
+
+
+/*
+ * Support for fuzzily matching columns.
+ *
+ * This is for building diagnostic messages, where non-exact matching
+ * attributes are suggested to the user. The struct's fields may be facets of
+ * a particular RTE, or of an entire range table, depending on context.
+ */
+typedef struct
+{
+ int distance; /* Weighted distance (lowest so far) */
+ RangeTblEntry *rfirst; /* RTE of first */
+ AttrNumber first; /* Closest attribute so far */
+ RangeTblEntry *rsecond; /* RTE of second */
+ AttrNumber second; /* Second closest attribute so far */
+} FuzzyAttrMatchState;
+
+#define MAX_FUZZY_DISTANCE 3
+
+
+static ParseNamespaceItem *scanNameSpaceForRefname(ParseState *pstate,
+ const char *refname,
+ int location);
+static ParseNamespaceItem *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
+ int location);
+static void check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
+ int location);
+static int scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+ Alias *eref,
+ const char *colname, int location,
+ int fuzzy_rte_penalty,
+ FuzzyAttrMatchState *fuzzystate);
+static void markRTEForSelectPriv(ParseState *pstate,
+ int rtindex, AttrNumber col);
+static void expandRelation(Oid relid, Alias *eref,
+ int rtindex, int sublevels_up,
+ int location, bool include_dropped,
+ List **colnames, List **colvars);
+static void expandTupleDesc(TupleDesc tupdesc, Alias *eref,
+ int count, int offset,
+ int rtindex, int sublevels_up,
+ int location, bool include_dropped,
+ List **colnames, List **colvars);
+static int specialAttNum(const char *attname);
+static bool isQueryUsingTempRelation_walker(Node *node, void *context);
+
+
+/*
+ * refnameNamespaceItem
+ * Given a possibly-qualified refname, look to see if it matches any visible
+ * namespace item. If so, return a pointer to the nsitem; else return NULL.
+ *
+ * Optionally get nsitem's nesting depth (0 = current) into *sublevels_up.
+ * If sublevels_up is NULL, only consider items at the current nesting
+ * level.
+ *
+ * An unqualified refname (schemaname == NULL) can match any item with matching
+ * alias, or matching unqualified relname in the case of alias-less relation
+ * items. It is possible that such a refname matches multiple items in the
+ * nearest nesting level that has a match; if so, we report an error via
+ * ereport().
+ *
+ * A qualified refname (schemaname != NULL) can only match a relation item
+ * that (a) has no alias and (b) is for the same relation identified by
+ * schemaname.refname. In this case we convert schemaname.refname to a
+ * relation OID and search by relid, rather than by alias name. This is
+ * peculiar, but it's what SQL says to do.
+ */
+ParseNamespaceItem *
+refnameNamespaceItem(ParseState *pstate,
+ const char *schemaname,
+ const char *refname,
+ int location,
+ int *sublevels_up)
+{
+ Oid relId = InvalidOid;
+
+ if (sublevels_up)
+ *sublevels_up = 0;
+
+ if (schemaname != NULL)
+ {
+ Oid namespaceId;
+
+ /*
+ * We can use LookupNamespaceNoError() here because we are only
+ * interested in finding existing RTEs. Checking USAGE permission on
+ * the schema is unnecessary since it would have already been checked
+ * when the RTE was made. Furthermore, we want to report "RTE not
+ * found", not "no permissions for schema", if the name happens to
+ * match a schema name the user hasn't got access to.
+ */
+ namespaceId = LookupNamespaceNoError(schemaname);
+ if (!OidIsValid(namespaceId))
+ return NULL;
+ relId = get_relname_relid(refname, namespaceId);
+ if (!OidIsValid(relId))
+ return NULL;
+ }
+
+ while (pstate != NULL)
+ {
+ ParseNamespaceItem *result;
+
+ if (OidIsValid(relId))
+ result = scanNameSpaceForRelid(pstate, relId, location);
+ else
+ result = scanNameSpaceForRefname(pstate, refname, location);
+
+ if (result)
+ return result;
+
+ if (sublevels_up)
+ (*sublevels_up)++;
+ else
+ break;
+
+ pstate = pstate->parentParseState;
+ }
+ return NULL;
+}
+
+/*
+ * Search the query's table namespace for an item matching the
+ * given unqualified refname. Return the nsitem if a unique match, or NULL
+ * if no match. Raise error if multiple matches.
+ *
+ * Note: it might seem that we shouldn't have to worry about the possibility
+ * of multiple matches; after all, the SQL standard disallows duplicate table
+ * aliases within a given SELECT level. Historically, however, Postgres has
+ * been laxer than that. For example, we allow
+ * SELECT ... FROM tab1 x CROSS JOIN (tab2 x CROSS JOIN tab3 y) z
+ * on the grounds that the aliased join (z) hides the aliases within it,
+ * therefore there is no conflict between the two RTEs named "x". However,
+ * if tab3 is a LATERAL subquery, then from within the subquery both "x"es
+ * are visible. Rather than rejecting queries that used to work, we allow
+ * this situation, and complain only if there's actually an ambiguous
+ * reference to "x".
+ */
+static ParseNamespaceItem *
+scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
+{
+ ParseNamespaceItem *result = NULL;
+ ListCell *l;
+
+ foreach(l, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
+
+ /* Ignore columns-only items */
+ if (!nsitem->p_rel_visible)
+ continue;
+ /* If not inside LATERAL, ignore lateral-only items */
+ if (nsitem->p_lateral_only && !pstate->p_lateral_active)
+ continue;
+
+ if (strcmp(nsitem->p_names->aliasname, refname) == 0)
+ {
+ if (result)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_ALIAS),
+ errmsg("table reference \"%s\" is ambiguous",
+ refname),
+ parser_errposition(pstate, location)));
+ check_lateral_ref_ok(pstate, nsitem, location);
+ result = nsitem;
+ }
+ }
+ return result;
+}
+
+/*
+ * Search the query's table namespace for a relation item matching the
+ * given relation OID. Return the nsitem if a unique match, or NULL
+ * if no match. Raise error if multiple matches.
+ *
+ * See the comments for refnameNamespaceItem to understand why this
+ * acts the way it does.
+ */
+static ParseNamespaceItem *
+scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
+{
+ ParseNamespaceItem *result = NULL;
+ ListCell *l;
+
+ foreach(l, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
+ RangeTblEntry *rte = nsitem->p_rte;
+
+ /* Ignore columns-only items */
+ if (!nsitem->p_rel_visible)
+ continue;
+ /* If not inside LATERAL, ignore lateral-only items */
+ if (nsitem->p_lateral_only && !pstate->p_lateral_active)
+ continue;
+
+ /* yes, the test for alias == NULL should be there... */
+ if (rte->rtekind == RTE_RELATION &&
+ rte->relid == relid &&
+ rte->alias == NULL)
+ {
+ if (result)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_ALIAS),
+ errmsg("table reference %u is ambiguous",
+ relid),
+ parser_errposition(pstate, location)));
+ check_lateral_ref_ok(pstate, nsitem, location);
+ result = nsitem;
+ }
+ }
+ return result;
+}
+
+/*
+ * Search the query's CTE namespace for a CTE matching the given unqualified
+ * refname. Return the CTE (and its levelsup count) if a match, or NULL
+ * if no match. We need not worry about multiple matches, since parse_cte.c
+ * rejects WITH lists containing duplicate CTE names.
+ */
+CommonTableExpr *
+scanNameSpaceForCTE(ParseState *pstate, const char *refname,
+ Index *ctelevelsup)
+{
+ Index levelsup;
+
+ for (levelsup = 0;
+ pstate != NULL;
+ pstate = pstate->parentParseState, levelsup++)
+ {
+ ListCell *lc;
+
+ foreach(lc, pstate->p_ctenamespace)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+ if (strcmp(cte->ctename, refname) == 0)
+ {
+ *ctelevelsup = levelsup;
+ return cte;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Search for a possible "future CTE", that is one that is not yet in scope
+ * according to the WITH scoping rules. This has nothing to do with valid
+ * SQL semantics, but it's important for error reporting purposes.
+ */
+static bool
+isFutureCTE(ParseState *pstate, const char *refname)
+{
+ for (; pstate != NULL; pstate = pstate->parentParseState)
+ {
+ ListCell *lc;
+
+ foreach(lc, pstate->p_future_ctes)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+ if (strcmp(cte->ctename, refname) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Search the query's ephemeral named relation namespace for a relation
+ * matching the given unqualified refname.
+ */
+bool
+scanNameSpaceForENR(ParseState *pstate, const char *refname)
+{
+ return name_matches_visible_ENR(pstate, refname);
+}
+
+/*
+ * searchRangeTableForRel
+ * See if any RangeTblEntry could possibly match the RangeVar.
+ * If so, return a pointer to the RangeTblEntry; else return NULL.
+ *
+ * This is different from refnameNamespaceItem in that it considers every
+ * entry in the ParseState's rangetable(s), not only those that are currently
+ * visible in the p_namespace list(s). This behavior is invalid per the SQL
+ * spec, and it may give ambiguous results (there might be multiple equally
+ * valid matches, but only one will be returned). This must be used ONLY
+ * as a heuristic in giving suitable error messages. See errorMissingRTE.
+ *
+ * Notice that we consider both matches on actual relation (or CTE) name
+ * and matches on alias.
+ */
+static RangeTblEntry *
+searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
+{
+ const char *refname = relation->relname;
+ Oid relId = InvalidOid;
+ CommonTableExpr *cte = NULL;
+ bool isenr = false;
+ Index ctelevelsup = 0;
+ Index levelsup;
+
+ /*
+ * If it's an unqualified name, check for possible CTE matches. A CTE
+ * hides any real relation matches. If no CTE, look for a matching
+ * relation.
+ *
+ * NB: It's not critical that RangeVarGetRelid return the correct answer
+ * here in the face of concurrent DDL. If it doesn't, the worst case
+ * scenario is a less-clear error message. Also, the tables involved in
+ * the query are already locked, which reduces the number of cases in
+ * which surprising behavior can occur. So we do the name lookup
+ * unlocked.
+ */
+ if (!relation->schemaname)
+ {
+ cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup);
+ if (!cte)
+ isenr = scanNameSpaceForENR(pstate, refname);
+ }
+
+ if (!cte && !isenr)
+ relId = RangeVarGetRelid(relation, NoLock, true);
+
+ /* Now look for RTEs matching either the relation/CTE/ENR or the alias */
+ for (levelsup = 0;
+ pstate != NULL;
+ pstate = pstate->parentParseState, levelsup++)
+ {
+ ListCell *l;
+
+ foreach(l, pstate->p_rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+
+ if (rte->rtekind == RTE_RELATION &&
+ OidIsValid(relId) &&
+ rte->relid == relId)
+ return rte;
+ if (rte->rtekind == RTE_CTE &&
+ cte != NULL &&
+ rte->ctelevelsup + levelsup == ctelevelsup &&
+ strcmp(rte->ctename, refname) == 0)
+ return rte;
+ if (rte->rtekind == RTE_NAMEDTUPLESTORE &&
+ isenr &&
+ strcmp(rte->enrname, refname) == 0)
+ return rte;
+ if (strcmp(rte->eref->aliasname, refname) == 0)
+ return rte;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Check for relation-name conflicts between two namespace lists.
+ * Raise an error if any is found.
+ *
+ * Note: we assume that each given argument does not contain conflicts
+ * itself; we just want to know if the two can be merged together.
+ *
+ * Per SQL, two alias-less plain relation RTEs do not conflict even if
+ * they have the same eref->aliasname (ie, same relation name), if they
+ * are for different relation OIDs (implying they are in different schemas).
+ *
+ * We ignore the lateral-only flags in the namespace items: the lists must
+ * not conflict, even when all items are considered visible. However,
+ * columns-only items should be ignored.
+ */
+void
+checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
+ List *namespace2)
+{
+ ListCell *l1;
+
+ foreach(l1, namespace1)
+ {
+ ParseNamespaceItem *nsitem1 = (ParseNamespaceItem *) lfirst(l1);
+ RangeTblEntry *rte1 = nsitem1->p_rte;
+ const char *aliasname1 = nsitem1->p_names->aliasname;
+ ListCell *l2;
+
+ if (!nsitem1->p_rel_visible)
+ continue;
+
+ foreach(l2, namespace2)
+ {
+ ParseNamespaceItem *nsitem2 = (ParseNamespaceItem *) lfirst(l2);
+ RangeTblEntry *rte2 = nsitem2->p_rte;
+ const char *aliasname2 = nsitem2->p_names->aliasname;
+
+ if (!nsitem2->p_rel_visible)
+ continue;
+ if (strcmp(aliasname2, aliasname1) != 0)
+ continue; /* definitely no conflict */
+ if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL &&
+ rte2->rtekind == RTE_RELATION && rte2->alias == NULL &&
+ rte1->relid != rte2->relid)
+ continue; /* no conflict per SQL rule */
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_ALIAS),
+ errmsg("table name \"%s\" specified more than once",
+ aliasname1)));
+ }
+ }
+}
+
+/*
+ * Complain if a namespace item is currently disallowed as a LATERAL reference.
+ * This enforces both SQL:2008's rather odd idea of what to do with a LATERAL
+ * reference to the wrong side of an outer join, and our own prohibition on
+ * referencing the target table of an UPDATE or DELETE as a lateral reference
+ * in a FROM/USING clause.
+ *
+ * Note: the pstate should be the same query level the nsitem was found in.
+ *
+ * Convenience subroutine to avoid multiple copies of a rather ugly ereport.
+ */
+static void
+check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
+ int location)
+{
+ if (nsitem->p_lateral_only && !nsitem->p_lateral_ok)
+ {
+ /* SQL:2008 demands this be an error, not an invisible item */
+ RangeTblEntry *rte = nsitem->p_rte;
+ char *refname = nsitem->p_names->aliasname;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("invalid reference to FROM-clause entry for table \"%s\"",
+ refname),
+ (pstate->p_target_nsitem != NULL &&
+ rte == pstate->p_target_nsitem->p_rte) ?
+ errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
+ refname) :
+ errdetail("The combining JOIN type must be INNER or LEFT for a LATERAL reference."),
+ parser_errposition(pstate, location)));
+ }
+}
+
+/*
+ * Given an RT index and nesting depth, find the corresponding
+ * ParseNamespaceItem (there must be one).
+ */
+ParseNamespaceItem *
+GetNSItemByRangeTablePosn(ParseState *pstate,
+ int varno,
+ int sublevels_up)
+{
+ ListCell *lc;
+
+ while (sublevels_up-- > 0)
+ {
+ pstate = pstate->parentParseState;
+ Assert(pstate != NULL);
+ }
+ foreach(lc, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
+
+ if (nsitem->p_rtindex == varno)
+ return nsitem;
+ }
+ elog(ERROR, "nsitem not found (internal error)");
+ return NULL; /* keep compiler quiet */
+}
+
+/*
+ * Given an RT index and nesting depth, find the corresponding RTE.
+ * (Note that the RTE need not be in the query's namespace.)
+ */
+RangeTblEntry *
+GetRTEByRangeTablePosn(ParseState *pstate,
+ int varno,
+ int sublevels_up)
+{
+ while (sublevels_up-- > 0)
+ {
+ pstate = pstate->parentParseState;
+ Assert(pstate != NULL);
+ }
+ Assert(varno > 0 && varno <= list_length(pstate->p_rtable));
+ return rt_fetch(varno, pstate->p_rtable);
+}
+
+/*
+ * Fetch the CTE for a CTE-reference RTE.
+ *
+ * rtelevelsup is the number of query levels above the given pstate that the
+ * RTE came from.
+ */
+CommonTableExpr *
+GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
+{
+ Index levelsup;
+ ListCell *lc;
+
+ Assert(rte->rtekind == RTE_CTE);
+ levelsup = rte->ctelevelsup + rtelevelsup;
+ while (levelsup-- > 0)
+ {
+ pstate = pstate->parentParseState;
+ if (!pstate) /* shouldn't happen */
+ elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
+ }
+ foreach(lc, pstate->p_ctenamespace)
+ {
+ CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+
+ if (strcmp(cte->ctename, rte->ctename) == 0)
+ return cte;
+ }
+ /* shouldn't happen */
+ elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
+ return NULL; /* keep compiler quiet */
+}
+
+/*
+ * updateFuzzyAttrMatchState
+ * Using Levenshtein distance, consider if column is best fuzzy match.
+ */
+static void
+updateFuzzyAttrMatchState(int fuzzy_rte_penalty,
+ FuzzyAttrMatchState *fuzzystate, RangeTblEntry *rte,
+ const char *actual, const char *match, int attnum)
+{
+ int columndistance;
+ int matchlen;
+
+ /* Bail before computing the Levenshtein distance if there's no hope. */
+ if (fuzzy_rte_penalty > fuzzystate->distance)
+ return;
+
+ /*
+ * Outright reject dropped columns, which can appear here with apparent
+ * empty actual names, per remarks within scanRTEForColumn().
+ */
+ if (actual[0] == '\0')
+ return;
+
+ /* Use Levenshtein to compute match distance. */
+ matchlen = strlen(match);
+ columndistance =
+ varstr_levenshtein_less_equal(actual, strlen(actual), match, matchlen,
+ 1, 1, 1,
+ fuzzystate->distance + 1
+ - fuzzy_rte_penalty,
+ true);
+
+ /*
+ * If more than half the characters are different, don't treat it as a
+ * match, to avoid making ridiculous suggestions.
+ */
+ if (columndistance > matchlen / 2)
+ return;
+
+ /*
+ * From this point on, we can ignore the distinction between the RTE-name
+ * distance and the column-name distance.
+ */
+ columndistance += fuzzy_rte_penalty;
+
+ /*
+ * If the new distance is less than or equal to that of the best match
+ * found so far, update fuzzystate.
+ */
+ if (columndistance < fuzzystate->distance)
+ {
+ /* Store new lowest observed distance for RTE */
+ fuzzystate->distance = columndistance;
+ fuzzystate->rfirst = rte;
+ fuzzystate->first = attnum;
+ fuzzystate->rsecond = NULL;
+ fuzzystate->second = InvalidAttrNumber;
+ }
+ else if (columndistance == fuzzystate->distance)
+ {
+ /*
+ * This match distance may equal a prior match within this same range
+ * table. When that happens, the prior match may also be given, but
+ * only if there is no more than two equally distant matches from the
+ * RTE (in turn, our caller will only accept two equally distant
+ * matches overall).
+ */
+ if (AttributeNumberIsValid(fuzzystate->second))
+ {
+ /* Too many RTE-level matches */
+ fuzzystate->rfirst = NULL;
+ fuzzystate->first = InvalidAttrNumber;
+ fuzzystate->rsecond = NULL;
+ fuzzystate->second = InvalidAttrNumber;
+ /* Clearly, distance is too low a bar (for *any* RTE) */
+ fuzzystate->distance = columndistance - 1;
+ }
+ else if (AttributeNumberIsValid(fuzzystate->first))
+ {
+ /* Record as provisional second match for RTE */
+ fuzzystate->rsecond = rte;
+ fuzzystate->second = attnum;
+ }
+ else if (fuzzystate->distance <= MAX_FUZZY_DISTANCE)
+ {
+ /*
+ * Record as provisional first match (this can occasionally occur
+ * because previous lowest distance was "too low a bar", rather
+ * than being associated with a real match)
+ */
+ fuzzystate->rfirst = rte;
+ fuzzystate->first = attnum;
+ }
+ }
+}
+
+/*
+ * scanNSItemForColumn
+ * Search the column names of a single namespace item for the given name.
+ * If found, return an appropriate Var node, else return NULL.
+ * If the name proves ambiguous within this nsitem, raise error.
+ *
+ * Side effect: if we find a match, mark the corresponding RTE as requiring
+ * read access for the column.
+ */
+Node *
+scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
+ int sublevels_up, const char *colname, int location)
+{
+ RangeTblEntry *rte = nsitem->p_rte;
+ int attnum;
+ Var *var;
+
+ /*
+ * Scan the nsitem's column names (or aliases) for a match. Complain if
+ * multiple matches.
+ */
+ attnum = scanRTEForColumn(pstate, rte, nsitem->p_names,
+ colname, location,
+ 0, NULL);
+
+ if (attnum == InvalidAttrNumber)
+ return NULL; /* Return NULL if no match */
+
+ /* In constraint check, no system column is allowed except tableOid */
+ if (pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT &&
+ attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("system column \"%s\" reference in check constraint is invalid",
+ colname),
+ parser_errposition(pstate, location)));
+
+ /* In generated column, no system column is allowed except tableOid */
+ if (pstate->p_expr_kind == EXPR_KIND_GENERATED_COLUMN &&
+ attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("cannot use system column \"%s\" in column generation expression",
+ colname),
+ parser_errposition(pstate, location)));
+
+ /* Found a valid match, so build a Var */
+ if (attnum > InvalidAttrNumber)
+ {
+ /* Get attribute data from the ParseNamespaceColumn array */
+ ParseNamespaceColumn *nscol = &nsitem->p_nscolumns[attnum - 1];
+
+ /* Complain if dropped column. See notes in scanRTEForColumn. */
+ if (nscol->p_varno == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ colname,
+ nsitem->p_names->aliasname)));
+
+ var = makeVar(nscol->p_varno,
+ nscol->p_varattno,
+ nscol->p_vartype,
+ nscol->p_vartypmod,
+ nscol->p_varcollid,
+ sublevels_up);
+ /* makeVar doesn't offer parameters for these, so set them by hand: */
+ var->varnosyn = nscol->p_varnosyn;
+ var->varattnosyn = nscol->p_varattnosyn;
+ }
+ else
+ {
+ /* System column, so use predetermined type data */
+ const FormData_pg_attribute *sysatt;
+
+ sysatt = SystemAttributeDefinition(attnum);
+ var = makeVar(nsitem->p_rtindex,
+ attnum,
+ sysatt->atttypid,
+ sysatt->atttypmod,
+ sysatt->attcollation,
+ sublevels_up);
+ }
+ var->location = location;
+
+ /* Require read access to the column */
+ markVarForSelectPriv(pstate, var);
+
+ return (Node *) var;
+}
+
+/*
+ * scanRTEForColumn
+ * Search the column names of a single RTE for the given name.
+ * If found, return the attnum (possibly negative, for a system column);
+ * else return InvalidAttrNumber.
+ * If the name proves ambiguous within this RTE, raise error.
+ *
+ * Actually, we only search the names listed in "eref". This can be either
+ * rte->eref, in which case we are indeed searching all the column names,
+ * or for a join it can be rte->join_using_alias, in which case we are only
+ * considering the common column names (which are the first N columns of the
+ * join, so everything works).
+ *
+ * pstate and location are passed only for error-reporting purposes.
+ *
+ * Side effect: if fuzzystate is non-NULL, check non-system columns
+ * for an approximate match and update fuzzystate accordingly.
+ *
+ * Note: this is factored out of scanNSItemForColumn because error message
+ * creation may want to check RTEs that are not in the namespace. To support
+ * that usage, minimize the number of validity checks performed here. It's
+ * okay to complain about ambiguous-name cases, though, since if we are
+ * working to complain about an invalid name, we've already eliminated that.
+ */
+static int
+scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+ Alias *eref,
+ const char *colname, int location,
+ int fuzzy_rte_penalty,
+ FuzzyAttrMatchState *fuzzystate)
+{
+ int result = InvalidAttrNumber;
+ int attnum = 0;
+ ListCell *c;
+
+ /*
+ * Scan the user column names (or aliases) for a match. Complain if
+ * multiple matches.
+ *
+ * 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 this somehow go wrong and we try to access a dropped column,
+ * we'll still catch it by virtue of the check in scanNSItemForColumn().
+ * Callers interested in finding match with shortest distance need to
+ * defend against this directly, though.
+ */
+ foreach(c, eref->colnames)
+ {
+ const char *attcolname = strVal(lfirst(c));
+
+ attnum++;
+ if (strcmp(attcolname, colname) == 0)
+ {
+ if (result)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ colname),
+ parser_errposition(pstate, location)));
+ result = attnum;
+ }
+
+ /* Update fuzzy match state, if provided. */
+ if (fuzzystate != NULL)
+ updateFuzzyAttrMatchState(fuzzy_rte_penalty, fuzzystate,
+ rte, attcolname, colname, attnum);
+ }
+
+ /*
+ * If we have a unique match, return it. Note that this allows a user
+ * alias to override a system column name (such as OID) without error.
+ */
+ if (result)
+ return result;
+
+ /*
+ * If the RTE represents a real relation, consider system column names.
+ * Composites are only used for pseudo-relations like ON CONFLICT's
+ * excluded.
+ */
+ if (rte->rtekind == RTE_RELATION &&
+ rte->relkind != RELKIND_COMPOSITE_TYPE)
+ {
+ /* quick check to see if name could be a system column */
+ attnum = specialAttNum(colname);
+ if (attnum != InvalidAttrNumber)
+ {
+ /* now check to see if column actually is defined */
+ if (SearchSysCacheExists2(ATTNUM,
+ ObjectIdGetDatum(rte->relid),
+ Int16GetDatum(attnum)))
+ result = attnum;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * colNameToVar
+ * Search for an unqualified column name.
+ * If found, return the appropriate Var node (or expression).
+ * If not found, return NULL. If the name proves ambiguous, raise error.
+ * If localonly is true, only names in the innermost query are considered.
+ */
+Node *
+colNameToVar(ParseState *pstate, const char *colname, bool localonly,
+ int location)
+{
+ Node *result = NULL;
+ int sublevels_up = 0;
+ ParseState *orig_pstate = pstate;
+
+ while (pstate != NULL)
+ {
+ ListCell *l;
+
+ foreach(l, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
+ Node *newresult;
+
+ /* Ignore table-only items */
+ if (!nsitem->p_cols_visible)
+ continue;
+ /* If not inside LATERAL, ignore lateral-only items */
+ if (nsitem->p_lateral_only && !pstate->p_lateral_active)
+ continue;
+
+ /* use orig_pstate here for consistency with other callers */
+ newresult = scanNSItemForColumn(orig_pstate, nsitem, sublevels_up,
+ colname, location);
+
+ if (newresult)
+ {
+ if (result)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_COLUMN),
+ errmsg("column reference \"%s\" is ambiguous",
+ colname),
+ parser_errposition(pstate, location)));
+ check_lateral_ref_ok(pstate, nsitem, location);
+ result = newresult;
+ }
+ }
+
+ if (result != NULL || localonly)
+ break; /* found, or don't want to look at parent */
+
+ pstate = pstate->parentParseState;
+ sublevels_up++;
+ }
+
+ return result;
+}
+
+/*
+ * searchRangeTableForCol
+ * See if any RangeTblEntry could possibly provide the given column name (or
+ * find the best match available). Returns state with relevant details.
+ *
+ * This is different from colNameToVar in that it considers every entry in
+ * the ParseState's rangetable(s), not only those that are currently visible
+ * in the p_namespace list(s). This behavior is invalid per the SQL spec,
+ * and it may give ambiguous results (there might be multiple equally valid
+ * matches, but only one will be returned). This must be used ONLY as a
+ * heuristic in giving suitable error messages. See errorMissingColumn.
+ *
+ * This function is also different in that it will consider approximate
+ * matches -- if the user entered an alias/column pair that is only slightly
+ * different from a valid pair, we may be able to infer what they meant to
+ * type and provide a reasonable hint.
+ *
+ * The FuzzyAttrMatchState will have 'rfirst' pointing to the best RTE
+ * containing the most promising match for the alias and column name. If
+ * the alias and column names match exactly, 'first' will be InvalidAttrNumber;
+ * otherwise, it will be the attribute number for the match. In the latter
+ * case, 'rsecond' may point to a second, equally close approximate match,
+ * and 'second' will contain the attribute number for the second match.
+ */
+static FuzzyAttrMatchState *
+searchRangeTableForCol(ParseState *pstate, const char *alias, const char *colname,
+ int location)
+{
+ ParseState *orig_pstate = pstate;
+ FuzzyAttrMatchState *fuzzystate = palloc(sizeof(FuzzyAttrMatchState));
+
+ fuzzystate->distance = MAX_FUZZY_DISTANCE + 1;
+ fuzzystate->rfirst = NULL;
+ fuzzystate->rsecond = NULL;
+ fuzzystate->first = InvalidAttrNumber;
+ fuzzystate->second = InvalidAttrNumber;
+
+ while (pstate != NULL)
+ {
+ ListCell *l;
+
+ foreach(l, pstate->p_rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+ int fuzzy_rte_penalty = 0;
+
+ /*
+ * Typically, it is not useful to look for matches within join
+ * RTEs; they effectively duplicate other RTEs for our purposes,
+ * and if a match is chosen from a join RTE, an unhelpful alias is
+ * displayed in the final diagnostic message.
+ */
+ if (rte->rtekind == RTE_JOIN)
+ continue;
+
+ /*
+ * If the user didn't specify an alias, then matches against one
+ * RTE are as good as another. But if the user did specify an
+ * alias, then we want at least a fuzzy - and preferably an exact
+ * - match for the range table entry.
+ */
+ if (alias != NULL)
+ fuzzy_rte_penalty =
+ varstr_levenshtein_less_equal(alias, strlen(alias),
+ rte->eref->aliasname,
+ strlen(rte->eref->aliasname),
+ 1, 1, 1,
+ MAX_FUZZY_DISTANCE + 1,
+ true);
+
+ /*
+ * Scan for a matching column; if we find an exact match, we're
+ * done. Otherwise, update fuzzystate.
+ */
+ if (scanRTEForColumn(orig_pstate, rte, rte->eref, colname, location,
+ fuzzy_rte_penalty, fuzzystate)
+ && fuzzy_rte_penalty == 0)
+ {
+ fuzzystate->rfirst = rte;
+ fuzzystate->first = InvalidAttrNumber;
+ fuzzystate->rsecond = NULL;
+ fuzzystate->second = InvalidAttrNumber;
+ return fuzzystate;
+ }
+ }
+
+ pstate = pstate->parentParseState;
+ }
+
+ return fuzzystate;
+}
+
+/*
+ * markRTEForSelectPriv
+ * Mark the specified column of the RTE with index rtindex
+ * as requiring SELECT privilege
+ *
+ * col == InvalidAttrNumber means a "whole row" reference
+ */
+static void
+markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col)
+{
+ RangeTblEntry *rte = rt_fetch(rtindex, pstate->p_rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ /* Make sure the rel as a whole is marked for SELECT access */
+ rte->requiredPerms |= ACL_SELECT;
+ /* Must offset the attnum to fit in a bitmapset */
+ rte->selectedCols = bms_add_member(rte->selectedCols,
+ col - FirstLowInvalidHeapAttributeNumber);
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ if (col == InvalidAttrNumber)
+ {
+ /*
+ * A whole-row reference to a join has to be treated as whole-row
+ * references to the two inputs.
+ */
+ JoinExpr *j;
+
+ if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs))
+ j = list_nth_node(JoinExpr, pstate->p_joinexprs, rtindex - 1);
+ else
+ j = NULL;
+ if (j == NULL)
+ elog(ERROR, "could not find JoinExpr for whole-row reference");
+
+ /* Note: we can't see FromExpr here */
+ if (IsA(j->larg, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) j->larg)->rtindex;
+
+ markRTEForSelectPriv(pstate, varno, InvalidAttrNumber);
+ }
+ else if (IsA(j->larg, JoinExpr))
+ {
+ int varno = ((JoinExpr *) j->larg)->rtindex;
+
+ markRTEForSelectPriv(pstate, varno, InvalidAttrNumber);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(j->larg));
+ if (IsA(j->rarg, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) j->rarg)->rtindex;
+
+ markRTEForSelectPriv(pstate, varno, InvalidAttrNumber);
+ }
+ else if (IsA(j->rarg, JoinExpr))
+ {
+ int varno = ((JoinExpr *) j->rarg)->rtindex;
+
+ markRTEForSelectPriv(pstate, varno, InvalidAttrNumber);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(j->rarg));
+ }
+ else
+ {
+ /*
+ * Join alias Vars for ordinary columns must refer to merged JOIN
+ * USING columns. We don't need to do anything here, because the
+ * join input columns will also be referenced in the join's qual
+ * clause, and will get marked for select privilege there.
+ */
+ }
+ }
+ /* other RTE types don't require privilege marking */
+}
+
+/*
+ * markVarForSelectPriv
+ * Mark the RTE referenced by the Var as requiring SELECT privilege
+ * for the Var's column (the Var could be a whole-row Var, too)
+ */
+void
+markVarForSelectPriv(ParseState *pstate, Var *var)
+{
+ Index lv;
+
+ Assert(IsA(var, Var));
+ /* Find the appropriate pstate if it's an uplevel Var */
+ for (lv = 0; lv < var->varlevelsup; lv++)
+ pstate = pstate->parentParseState;
+ markRTEForSelectPriv(pstate, var->varno, var->varattno);
+}
+
+/*
+ * buildRelationAliases
+ * Construct the eref column name list for a relation RTE.
+ * This code is also used for function RTEs.
+ *
+ * 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.
+ *
+ * It is an error for there to be more aliases present than required.
+ */
+static void
+buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
+{
+ int maxattrs = tupdesc->natts;
+ List *aliaslist;
+ ListCell *aliaslc;
+ int numaliases;
+ int varattno;
+ int numdropped = 0;
+
+ Assert(eref->colnames == NIL);
+
+ if (alias)
+ {
+ aliaslist = alias->colnames;
+ aliaslc = list_head(aliaslist);
+ numaliases = list_length(aliaslist);
+ /* We'll rebuild the alias colname list */
+ alias->colnames = NIL;
+ }
+ else
+ {
+ aliaslist = NIL;
+ aliaslc = NULL;
+ numaliases = 0;
+ }
+
+ for (varattno = 0; varattno < maxattrs; varattno++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, 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(aliaslist, 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)));
+}
+
+/*
+ * chooseScalarFunctionAlias
+ * Select the column alias for a function in a function RTE,
+ * when the function returns a scalar type (not composite or RECORD).
+ *
+ * funcexpr: transformed expression tree for the function call
+ * funcname: function name (as determined by FigureColname)
+ * alias: the user-supplied alias for the RTE, or NULL if none
+ * nfuncs: the number of functions appearing in the function RTE
+ *
+ * Note that the name we choose might be overridden later, if the user-given
+ * alias includes column alias names. That's of no concern here.
+ */
+static char *
+chooseScalarFunctionAlias(Node *funcexpr, char *funcname,
+ Alias *alias, int nfuncs)
+{
+ char *pname;
+
+ /*
+ * If the expression is a simple function call, and the function has a
+ * single OUT parameter that is named, use the parameter's name.
+ */
+ if (funcexpr && IsA(funcexpr, FuncExpr))
+ {
+ pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid);
+ if (pname)
+ return pname;
+ }
+
+ /*
+ * If there's just one function in the RTE, and the user gave an RTE alias
+ * name, use that name. (This makes FROM func() AS foo use "foo" as the
+ * column name as well as the table alias.)
+ */
+ if (nfuncs == 1 && alias)
+ return alias->aliasname;
+
+ /*
+ * Otherwise use the function name.
+ */
+ return funcname;
+}
+
+/*
+ * buildNSItemFromTupleDesc
+ * Build a ParseNamespaceItem, given a tupdesc describing the columns.
+ *
+ * rte: the new RangeTblEntry for the rel
+ * rtindex: its index in the rangetable list
+ * tupdesc: the physical column information
+ */
+static ParseNamespaceItem *
+buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc)
+{
+ ParseNamespaceItem *nsitem;
+ ParseNamespaceColumn *nscolumns;
+ int maxattrs = tupdesc->natts;
+ int varattno;
+
+ /* colnames must have the same number of entries as the nsitem */
+ Assert(maxattrs == list_length(rte->eref->colnames));
+
+ /* extract per-column data from the tupdesc */
+ nscolumns = (ParseNamespaceColumn *)
+ palloc0(maxattrs * sizeof(ParseNamespaceColumn));
+
+ for (varattno = 0; varattno < maxattrs; varattno++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno);
+
+ /* For a dropped column, just leave the entry as zeroes */
+ if (attr->attisdropped)
+ continue;
+
+ nscolumns[varattno].p_varno = rtindex;
+ nscolumns[varattno].p_varattno = varattno + 1;
+ nscolumns[varattno].p_vartype = attr->atttypid;
+ nscolumns[varattno].p_vartypmod = attr->atttypmod;
+ nscolumns[varattno].p_varcollid = attr->attcollation;
+ nscolumns[varattno].p_varnosyn = rtindex;
+ nscolumns[varattno].p_varattnosyn = varattno + 1;
+ }
+
+ /* ... and build the nsitem */
+ nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+ nsitem->p_names = rte->eref;
+ nsitem->p_rte = rte;
+ nsitem->p_rtindex = rtindex;
+ nsitem->p_nscolumns = nscolumns;
+ /* set default visibility flags; might get changed later */
+ nsitem->p_rel_visible = true;
+ nsitem->p_cols_visible = true;
+ nsitem->p_lateral_only = false;
+ nsitem->p_lateral_ok = true;
+
+ return nsitem;
+}
+
+/*
+ * buildNSItemFromLists
+ * Build a ParseNamespaceItem, given column type information in lists.
+ *
+ * rte: the new RangeTblEntry for the rel
+ * rtindex: its index in the rangetable list
+ * coltypes: per-column datatype OIDs
+ * coltypmods: per-column type modifiers
+ * colcollation: per-column collation OIDs
+ */
+static ParseNamespaceItem *
+buildNSItemFromLists(RangeTblEntry *rte, Index rtindex,
+ List *coltypes, List *coltypmods, List *colcollations)
+{
+ ParseNamespaceItem *nsitem;
+ ParseNamespaceColumn *nscolumns;
+ int maxattrs = list_length(coltypes);
+ int varattno;
+ ListCell *lct;
+ ListCell *lcm;
+ ListCell *lcc;
+
+ /* colnames must have the same number of entries as the nsitem */
+ Assert(maxattrs == list_length(rte->eref->colnames));
+
+ Assert(maxattrs == list_length(coltypmods));
+ Assert(maxattrs == list_length(colcollations));
+
+ /* extract per-column data from the lists */
+ nscolumns = (ParseNamespaceColumn *)
+ palloc0(maxattrs * sizeof(ParseNamespaceColumn));
+
+ varattno = 0;
+ forthree(lct, coltypes,
+ lcm, coltypmods,
+ lcc, colcollations)
+ {
+ nscolumns[varattno].p_varno = rtindex;
+ nscolumns[varattno].p_varattno = varattno + 1;
+ nscolumns[varattno].p_vartype = lfirst_oid(lct);
+ nscolumns[varattno].p_vartypmod = lfirst_int(lcm);
+ nscolumns[varattno].p_varcollid = lfirst_oid(lcc);
+ nscolumns[varattno].p_varnosyn = rtindex;
+ nscolumns[varattno].p_varattnosyn = varattno + 1;
+ varattno++;
+ }
+
+ /* ... and build the nsitem */
+ nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+ nsitem->p_names = rte->eref;
+ nsitem->p_rte = rte;
+ nsitem->p_rtindex = rtindex;
+ nsitem->p_nscolumns = nscolumns;
+ /* set default visibility flags; might get changed later */
+ nsitem->p_rel_visible = true;
+ nsitem->p_cols_visible = true;
+ nsitem->p_lateral_only = false;
+ nsitem->p_lateral_ok = true;
+
+ return nsitem;
+}
+
+/*
+ * Open a table during parse analysis
+ *
+ * This is essentially just the same as table_openrv(), except that it caters
+ * to some parser-specific error reporting needs, notably that it arranges
+ * to include the RangeVar's parse location in any resulting error.
+ *
+ * Note: properly, lockmode should be declared LOCKMODE not int, but that
+ * would require importing storage/lock.h into parse_relation.h. Since
+ * LOCKMODE is typedef'd as int anyway, that seems like overkill.
+ */
+Relation
+parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
+{
+ Relation rel;
+ ParseCallbackState pcbstate;
+
+ setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
+ rel = table_openrv_extended(relation, lockmode, true);
+ if (rel == NULL)
+ {
+ if (relation->schemaname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s.%s\" does not exist",
+ relation->schemaname, relation->relname)));
+ else
+ {
+ /*
+ * An unqualified name might have been meant as a reference to
+ * some not-yet-in-scope CTE. The bare "does not exist" message
+ * has proven remarkably unhelpful for figuring out such problems,
+ * so we take pains to offer a specific hint.
+ */
+ if (isFutureCTE(pstate, relation->relname))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname),
+ errdetail("There is a WITH item named \"%s\", but it cannot be referenced from this part of the query.",
+ relation->relname),
+ errhint("Use WITH RECURSIVE, or re-order the WITH items to remove forward references.")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist",
+ relation->relname)));
+ }
+ }
+ cancel_parser_errposition_callback(&pcbstate);
+ return rel;
+}
+
+/*
+ * Add an entry for a relation to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * We do not link the ParseNamespaceItem into the pstate here; it's the
+ * caller's job to do that in the appropriate way.
+ *
+ * Note: formerly this checked for refname conflicts, but that's wrong.
+ * Caller is responsible for checking for conflicts in the appropriate scope.
+ */
+ParseNamespaceItem *
+addRangeTableEntry(ParseState *pstate,
+ RangeVar *relation,
+ Alias *alias,
+ bool inh,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias ? alias->aliasname : relation->relname;
+ LOCKMODE lockmode;
+ Relation rel;
+ ParseNamespaceItem *nsitem;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_RELATION;
+ rte->alias = alias;
+
+ /*
+ * Identify the type of lock we'll need on this relation. It's not the
+ * query's target table (that case is handled elsewhere), so we need
+ * either RowShareLock if it's locked by FOR UPDATE/SHARE, or plain
+ * AccessShareLock otherwise.
+ */
+ lockmode = isLockedRefname(pstate, refname) ? RowShareLock : AccessShareLock;
+
+ /*
+ * Get the rel's OID. This access also ensures that we have an up-to-date
+ * relcache entry for the rel. Since this is typically the first access
+ * to a rel in a statement, we must open the rel with the proper lockmode.
+ */
+ rel = parserOpenTable(pstate, relation, lockmode);
+ rte->relid = RelationGetRelid(rel);
+ rte->relkind = rel->rd_rel->relkind;
+ rte->rellockmode = lockmode;
+
+ /*
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names.
+ */
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(rel->rd_att, alias, rte->eref);
+
+ /*
+ * Set flags and access permissions.
+ *
+ * The initial default on access checks is always check-for-READ-access,
+ * which is the right thing for all except target tables.
+ */
+ rte->lateral = false;
+ rte->inh = inh;
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = ACL_SELECT;
+ rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+ rel->rd_att);
+
+ /*
+ * Drop the rel refcount, but keep the access lock till end of transaction
+ * so that the table can't be deleted or have its schema modified
+ * underneath us.
+ */
+ table_close(rel, NoLock);
+
+ return nsitem;
+}
+
+/*
+ * Add an entry for a relation to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is just like addRangeTableEntry() except that it makes an RTE
+ * given an already-open relation instead of a RangeVar reference.
+ *
+ * lockmode is the lock type required for query execution; it must be one
+ * of AccessShareLock, RowShareLock, or RowExclusiveLock depending on the
+ * RTE's role within the query. The caller must hold that lock mode
+ * or a stronger one.
+ *
+ * Note: properly, lockmode should be declared LOCKMODE not int, but that
+ * would require importing storage/lock.h into parse_relation.h. Since
+ * LOCKMODE is typedef'd as int anyway, that seems like overkill.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForRelation(ParseState *pstate,
+ Relation rel,
+ int lockmode,
+ Alias *alias,
+ bool inh,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias ? alias->aliasname : RelationGetRelationName(rel);
+
+ Assert(pstate != NULL);
+
+ Assert(lockmode == AccessShareLock ||
+ lockmode == RowShareLock ||
+ lockmode == RowExclusiveLock);
+ Assert(CheckRelationLockedByMe(rel, lockmode, true));
+
+ rte->rtekind = RTE_RELATION;
+ rte->alias = alias;
+ rte->relid = RelationGetRelid(rel);
+ rte->relkind = rel->rd_rel->relkind;
+ rte->rellockmode = lockmode;
+
+ /*
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names.
+ */
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(rel->rd_att, alias, rte->eref);
+
+ /*
+ * Set flags and access permissions.
+ *
+ * The initial default on access checks is always check-for-READ-access,
+ * which is the right thing for all except target tables.
+ */
+ rte->lateral = false;
+ rte->inh = inh;
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = ACL_SELECT;
+ rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+ rel->rd_att);
+}
+
+/*
+ * Add an entry for a subquery to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a subquery RTE.
+ * Note that an alias clause *must* be supplied.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForSubquery(ParseState *pstate,
+ Query *subquery,
+ Alias *alias,
+ bool lateral,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias->aliasname;
+ Alias *eref;
+ int numaliases;
+ List *coltypes,
+ *coltypmods,
+ *colcollations;
+ int varattno;
+ ListCell *tlistitem;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_SUBQUERY;
+ rte->subquery = subquery;
+ rte->alias = alias;
+
+ eref = copyObject(alias);
+ numaliases = list_length(eref->colnames);
+
+ /* fill in any unspecified alias columns, and extract column type info */
+ coltypes = coltypmods = colcollations = NIL;
+ varattno = 0;
+ foreach(tlistitem, subquery->targetList)
+ {
+ TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+ if (te->resjunk)
+ continue;
+ varattno++;
+ Assert(varattno == te->resno);
+ if (varattno > numaliases)
+ {
+ char *attrname;
+
+ attrname = pstrdup(te->resname);
+ eref->colnames = lappend(eref->colnames, makeString(attrname));
+ }
+ coltypes = lappend_oid(coltypes,
+ exprType((Node *) te->expr));
+ coltypmods = lappend_int(coltypmods,
+ exprTypmod((Node *) te->expr));
+ colcollations = lappend_oid(colcollations,
+ exprCollation((Node *) te->expr));
+ }
+ if (varattno < numaliases)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("table \"%s\" has %d columns available but %d columns specified",
+ refname, varattno, numaliases)));
+
+ rte->eref = eref;
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Subqueries are never checked for access rights.
+ */
+ rte->lateral = lateral;
+ rte->inh = false; /* never true for subqueries */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ coltypes, coltypmods, colcollations);
+}
+
+/*
+ * Add an entry for a function (or functions) to the pstate's range table
+ * (p_rtable). Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a function RTE.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForFunction(ParseState *pstate,
+ List *funcnames,
+ List *funcexprs,
+ List *coldeflists,
+ RangeFunction *rangefunc,
+ bool lateral,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Alias *alias = rangefunc->alias;
+ Alias *eref;
+ char *aliasname;
+ int nfuncs = list_length(funcexprs);
+ TupleDesc *functupdescs;
+ TupleDesc tupdesc;
+ ListCell *lc1,
+ *lc2,
+ *lc3;
+ int i;
+ int j;
+ int funcno;
+ int natts,
+ totalatts;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_FUNCTION;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->functions = NIL; /* we'll fill this list below */
+ rte->funcordinality = rangefunc->ordinality;
+ rte->alias = alias;
+
+ /*
+ * Choose the RTE alias name. We default to using the first function's
+ * name even when there's more than one; which is maybe arguable but beats
+ * using something constant like "table".
+ */
+ if (alias)
+ aliasname = alias->aliasname;
+ else
+ aliasname = linitial(funcnames);
+
+ eref = makeAlias(aliasname, NIL);
+ rte->eref = eref;
+
+ /* Process each function ... */
+ functupdescs = (TupleDesc *) palloc(nfuncs * sizeof(TupleDesc));
+
+ totalatts = 0;
+ funcno = 0;
+ forthree(lc1, funcexprs, lc2, funcnames, lc3, coldeflists)
+ {
+ Node *funcexpr = (Node *) lfirst(lc1);
+ char *funcname = (char *) lfirst(lc2);
+ List *coldeflist = (List *) lfirst(lc3);
+ RangeTblFunction *rtfunc = makeNode(RangeTblFunction);
+ TypeFuncClass functypclass;
+ Oid funcrettype;
+
+ /* Initialize RangeTblFunction node */
+ rtfunc->funcexpr = funcexpr;
+ rtfunc->funccolnames = NIL;
+ rtfunc->funccoltypes = NIL;
+ rtfunc->funccoltypmods = NIL;
+ rtfunc->funccolcollations = NIL;
+ rtfunc->funcparams = NULL; /* not set until planning */
+
+ /*
+ * Now determine if the function returns a simple or composite type.
+ */
+ functypclass = get_expr_result_type(funcexpr,
+ &funcrettype,
+ &tupdesc);
+
+ /*
+ * A coldeflist is required if the function returns RECORD and hasn't
+ * got a predetermined record type, and is prohibited otherwise. This
+ * can be a bit confusing, so we expend some effort on delivering a
+ * relevant error message.
+ */
+ if (coldeflist != NIL)
+ {
+ switch (functypclass)
+ {
+ case TYPEFUNC_RECORD:
+ /* ok */
+ break;
+ case TYPEFUNC_COMPOSITE:
+ case TYPEFUNC_COMPOSITE_DOMAIN:
+
+ /*
+ * If the function's raw result type is RECORD, we must
+ * have resolved it using its OUT parameters. Otherwise,
+ * it must have a named composite type.
+ */
+ if (exprType(funcexpr) == RECORDOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("a column definition list is redundant for a function with OUT parameters"),
+ parser_errposition(pstate,
+ exprLocation((Node *) coldeflist))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("a column definition list is redundant for a function returning a named composite type"),
+ parser_errposition(pstate,
+ exprLocation((Node *) coldeflist))));
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("a column definition list is only allowed for functions returning \"record\""),
+ parser_errposition(pstate,
+ exprLocation((Node *) coldeflist))));
+ break;
+ }
+ }
+ else
+ {
+ if (functypclass == TYPEFUNC_RECORD)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("a column definition list is required for functions returning \"record\""),
+ parser_errposition(pstate, exprLocation(funcexpr))));
+ }
+
+ if (functypclass == TYPEFUNC_COMPOSITE ||
+ functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
+ {
+ /* Composite data type, e.g. a table's row type */
+ Assert(tupdesc);
+ }
+ else if (functypclass == TYPEFUNC_SCALAR)
+ {
+ /* Base data type, i.e. scalar */
+ tupdesc = CreateTemplateTupleDesc(1);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ chooseScalarFunctionAlias(funcexpr, funcname,
+ alias, nfuncs),
+ funcrettype,
+ exprTypmod(funcexpr),
+ 0);
+ TupleDescInitEntryCollation(tupdesc,
+ (AttrNumber) 1,
+ exprCollation(funcexpr));
+ }
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ ListCell *col;
+
+ /*
+ * Use the column definition list to construct a tupdesc and fill
+ * in the RangeTblFunction's lists. Limit number of columns to
+ * MaxHeapAttributeNumber, because CheckAttributeNamesTypes will.
+ */
+ if (list_length(coldeflist) > MaxHeapAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("column definition lists can have at most %d entries",
+ MaxHeapAttributeNumber),
+ parser_errposition(pstate,
+ exprLocation((Node *) coldeflist))));
+ tupdesc = CreateTemplateTupleDesc(list_length(coldeflist));
+ i = 1;
+ foreach(col, coldeflist)
+ {
+ ColumnDef *n = (ColumnDef *) lfirst(col);
+ char *attrname;
+ Oid attrtype;
+ int32 attrtypmod;
+ Oid attrcollation;
+
+ attrname = n->colname;
+ if (n->typeName->setof)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("column \"%s\" cannot be declared SETOF",
+ attrname),
+ parser_errposition(pstate, n->location)));
+ typenameTypeIdAndMod(pstate, n->typeName,
+ &attrtype, &attrtypmod);
+ attrcollation = GetColumnDefCollation(pstate, n, attrtype);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) i,
+ attrname,
+ attrtype,
+ attrtypmod,
+ 0);
+ TupleDescInitEntryCollation(tupdesc,
+ (AttrNumber) i,
+ attrcollation);
+ rtfunc->funccolnames = lappend(rtfunc->funccolnames,
+ makeString(pstrdup(attrname)));
+ rtfunc->funccoltypes = lappend_oid(rtfunc->funccoltypes,
+ attrtype);
+ rtfunc->funccoltypmods = lappend_int(rtfunc->funccoltypmods,
+ attrtypmod);
+ rtfunc->funccolcollations = lappend_oid(rtfunc->funccolcollations,
+ attrcollation);
+
+ i++;
+ }
+
+ /*
+ * Ensure that the coldeflist defines a legal set of names (no
+ * duplicates, but we needn't worry about system column names) and
+ * datatypes. Although we mostly can't allow pseudo-types, it
+ * seems safe to allow RECORD and RECORD[], since values within
+ * those type classes are self-identifying at runtime, and the
+ * coldeflist doesn't represent anything that will be visible to
+ * other sessions.
+ */
+ CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE,
+ CHKATYPE_ANYRECORD);
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("function \"%s\" in FROM has unsupported return type %s",
+ funcname, format_type_be(funcrettype)),
+ parser_errposition(pstate, exprLocation(funcexpr))));
+
+ /* Finish off the RangeTblFunction and add it to the RTE's list */
+ rtfunc->funccolcount = tupdesc->natts;
+ rte->functions = lappend(rte->functions, rtfunc);
+
+ /* Save the tupdesc for use below */
+ functupdescs[funcno] = tupdesc;
+ totalatts += tupdesc->natts;
+ funcno++;
+ }
+
+ /*
+ * If there's more than one function, or we want an ordinality column, we
+ * have to produce a merged tupdesc.
+ */
+ if (nfuncs > 1 || rangefunc->ordinality)
+ {
+ if (rangefunc->ordinality)
+ totalatts++;
+
+ /* Disallow more columns than will fit in a tuple */
+ if (totalatts > MaxTupleAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("functions in FROM can return at most %d columns",
+ MaxTupleAttributeNumber),
+ parser_errposition(pstate,
+ exprLocation((Node *) funcexprs))));
+
+ /* Merge the tuple descs of each function into a composite one */
+ tupdesc = CreateTemplateTupleDesc(totalatts);
+ natts = 0;
+ for (i = 0; i < nfuncs; i++)
+ {
+ for (j = 1; j <= functupdescs[i]->natts; j++)
+ TupleDescCopyEntry(tupdesc, ++natts, functupdescs[i], j);
+ }
+
+ /* Add the ordinality column if needed */
+ if (rangefunc->ordinality)
+ {
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) ++natts,
+ "ordinality",
+ INT8OID,
+ -1,
+ 0);
+ /* no need to set collation */
+ }
+
+ Assert(natts == totalatts);
+ }
+ else
+ {
+ /* We can just use the single function's tupdesc as-is */
+ tupdesc = functupdescs[0];
+ }
+
+ /* Use the tupdesc while assigning column aliases for the RTE */
+ buildRelationAliases(tupdesc, alias, eref);
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Functions are never checked for access rights (at least, not by the RTE
+ * permissions mechanism).
+ */
+ rte->lateral = lateral;
+ rte->inh = false; /* never true for functions */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+ tupdesc);
+}
+
+/*
+ * Add an entry for a table function to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a tablefunc RTE.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForTableFunc(ParseState *pstate,
+ TableFunc *tf,
+ Alias *alias,
+ bool lateral,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias ? alias->aliasname : pstrdup("xmltable");
+ Alias *eref;
+ int numaliases;
+
+ Assert(pstate != NULL);
+
+ /* Disallow more columns than will fit in a tuple */
+ if (list_length(tf->colnames) > MaxTupleAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("functions in FROM can return at most %d columns",
+ MaxTupleAttributeNumber),
+ parser_errposition(pstate,
+ exprLocation((Node *) tf))));
+ Assert(list_length(tf->coltypes) == list_length(tf->colnames));
+ Assert(list_length(tf->coltypmods) == list_length(tf->colnames));
+ Assert(list_length(tf->colcollations) == list_length(tf->colnames));
+
+ rte->rtekind = RTE_TABLEFUNC;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->tablefunc = tf;
+ rte->coltypes = tf->coltypes;
+ rte->coltypmods = tf->coltypmods;
+ rte->colcollations = tf->colcollations;
+ rte->alias = alias;
+
+ eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
+ numaliases = list_length(eref->colnames);
+
+ /* fill in any unspecified alias columns */
+ if (numaliases < list_length(tf->colnames))
+ eref->colnames = list_concat(eref->colnames,
+ list_copy_tail(tf->colnames, numaliases));
+
+ if (numaliases > list_length(tf->colnames))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("%s function has %d columns available but %d columns specified",
+ "XMLTABLE", list_length(tf->colnames), numaliases)));
+
+ rte->eref = eref;
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Tablefuncs are never checked for access rights (at least, not by the
+ * RTE permissions mechanism).
+ */
+ rte->lateral = lateral;
+ rte->inh = false; /* never true for tablefunc RTEs */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ rte->coltypes, rte->coltypmods,
+ rte->colcollations);
+}
+
+/*
+ * Add an entry for a VALUES list to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a values RTE.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForValues(ParseState *pstate,
+ List *exprs,
+ List *coltypes,
+ List *coltypmods,
+ List *colcollations,
+ Alias *alias,
+ bool lateral,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias ? alias->aliasname : pstrdup("*VALUES*");
+ Alias *eref;
+ int numaliases;
+ int numcolumns;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_VALUES;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->values_lists = exprs;
+ rte->coltypes = coltypes;
+ rte->coltypmods = coltypmods;
+ rte->colcollations = colcollations;
+ rte->alias = alias;
+
+ eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
+
+ /* fill in any unspecified alias columns */
+ numcolumns = list_length((List *) linitial(exprs));
+ numaliases = list_length(eref->colnames);
+ while (numaliases < numcolumns)
+ {
+ char attrname[64];
+
+ numaliases++;
+ snprintf(attrname, sizeof(attrname), "column%d", numaliases);
+ eref->colnames = lappend(eref->colnames,
+ makeString(pstrdup(attrname)));
+ }
+ if (numcolumns < numaliases)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified",
+ refname, numcolumns, numaliases)));
+
+ rte->eref = eref;
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Subqueries are never checked for access rights.
+ */
+ rte->lateral = lateral;
+ rte->inh = false; /* never true for values RTEs */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ rte->coltypes, rte->coltypmods,
+ rte->colcollations);
+}
+
+/*
+ * Add an entry for a join to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a join RTE.
+ * Also, it's more convenient for the caller to construct the
+ * ParseNamespaceColumn array, so we pass that in.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForJoin(ParseState *pstate,
+ List *colnames,
+ ParseNamespaceColumn *nscolumns,
+ JoinType jointype,
+ int nummergedcols,
+ List *aliasvars,
+ List *leftcols,
+ List *rightcols,
+ Alias *join_using_alias,
+ Alias *alias,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Alias *eref;
+ int numaliases;
+ ParseNamespaceItem *nsitem;
+
+ Assert(pstate != NULL);
+
+ /*
+ * Fail if join has too many columns --- we must be able to reference any
+ * of the columns with an AttrNumber.
+ */
+ if (list_length(aliasvars) > MaxAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("joins can have at most %d columns",
+ MaxAttrNumber)));
+
+ rte->rtekind = RTE_JOIN;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->jointype = jointype;
+ rte->joinmergedcols = nummergedcols;
+ rte->joinaliasvars = aliasvars;
+ rte->joinleftcols = leftcols;
+ rte->joinrightcols = rightcols;
+ rte->join_using_alias = join_using_alias;
+ rte->alias = alias;
+
+ eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL);
+ numaliases = list_length(eref->colnames);
+
+ /* fill in any unspecified alias columns */
+ if (numaliases < list_length(colnames))
+ eref->colnames = list_concat(eref->colnames,
+ list_copy_tail(colnames, numaliases));
+
+ if (numaliases > list_length(colnames))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("join expression \"%s\" has %d columns available but %d columns specified",
+ eref->aliasname, list_length(colnames), numaliases)));
+
+ rte->eref = eref;
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Joins are never checked for access rights.
+ */
+ rte->lateral = false;
+ rte->inh = false; /* never true for joins */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
+ nsitem->p_names = rte->eref;
+ nsitem->p_rte = rte;
+ nsitem->p_rtindex = list_length(pstate->p_rtable);
+ nsitem->p_nscolumns = nscolumns;
+ /* set default visibility flags; might get changed later */
+ nsitem->p_rel_visible = true;
+ nsitem->p_cols_visible = true;
+ nsitem->p_lateral_only = false;
+ nsitem->p_lateral_ok = true;
+
+ return nsitem;
+}
+
+/*
+ * Add an entry for a CTE reference to the pstate's range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * This is much like addRangeTableEntry() except that it makes a CTE RTE.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForCTE(ParseState *pstate,
+ CommonTableExpr *cte,
+ Index levelsup,
+ RangeVar *rv,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Alias *alias = rv->alias;
+ char *refname = alias ? alias->aliasname : cte->ctename;
+ Alias *eref;
+ int numaliases;
+ int varattno;
+ ListCell *lc;
+ int n_dontexpand_columns = 0;
+ ParseNamespaceItem *psi;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_CTE;
+ rte->ctename = cte->ctename;
+ rte->ctelevelsup = levelsup;
+
+ /* Self-reference if and only if CTE's parse analysis isn't completed */
+ rte->self_reference = !IsA(cte->ctequery, Query);
+ Assert(cte->cterecursive || !rte->self_reference);
+ /* Bump the CTE's refcount if this isn't a self-reference */
+ if (!rte->self_reference)
+ cte->cterefcount++;
+
+ /*
+ * We throw error if the CTE is INSERT/UPDATE/DELETE without RETURNING.
+ * This won't get checked in case of a self-reference, but that's OK
+ * because data-modifying CTEs aren't allowed to be recursive anyhow.
+ */
+ if (IsA(cte->ctequery, Query))
+ {
+ Query *ctequery = (Query *) cte->ctequery;
+
+ if (ctequery->commandType != CMD_SELECT &&
+ ctequery->returningList == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("WITH query \"%s\" does not have a RETURNING clause",
+ cte->ctename),
+ parser_errposition(pstate, rv->location)));
+ }
+
+ rte->coltypes = list_copy(cte->ctecoltypes);
+ rte->coltypmods = list_copy(cte->ctecoltypmods);
+ rte->colcollations = list_copy(cte->ctecolcollations);
+
+ rte->alias = alias;
+ if (alias)
+ eref = copyObject(alias);
+ else
+ eref = makeAlias(refname, NIL);
+ numaliases = list_length(eref->colnames);
+
+ /* fill in any unspecified alias columns */
+ varattno = 0;
+ foreach(lc, cte->ctecolnames)
+ {
+ varattno++;
+ if (varattno > numaliases)
+ eref->colnames = lappend(eref->colnames, lfirst(lc));
+ }
+ if (varattno < numaliases)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("table \"%s\" has %d columns available but %d columns specified",
+ refname, varattno, numaliases)));
+
+ rte->eref = eref;
+
+ if (cte->search_clause)
+ {
+ rte->eref->colnames = lappend(rte->eref->colnames, makeString(cte->search_clause->search_seq_column));
+ if (cte->search_clause->search_breadth_first)
+ rte->coltypes = lappend_oid(rte->coltypes, RECORDOID);
+ else
+ rte->coltypes = lappend_oid(rte->coltypes, RECORDARRAYOID);
+ rte->coltypmods = lappend_int(rte->coltypmods, -1);
+ rte->colcollations = lappend_oid(rte->colcollations, InvalidOid);
+
+ n_dontexpand_columns += 1;
+ }
+
+ if (cte->cycle_clause)
+ {
+ rte->eref->colnames = lappend(rte->eref->colnames, makeString(cte->cycle_clause->cycle_mark_column));
+ rte->coltypes = lappend_oid(rte->coltypes, cte->cycle_clause->cycle_mark_type);
+ rte->coltypmods = lappend_int(rte->coltypmods, cte->cycle_clause->cycle_mark_typmod);
+ rte->colcollations = lappend_oid(rte->colcollations, cte->cycle_clause->cycle_mark_collation);
+
+ rte->eref->colnames = lappend(rte->eref->colnames, makeString(cte->cycle_clause->cycle_path_column));
+ rte->coltypes = lappend_oid(rte->coltypes, RECORDARRAYOID);
+ rte->coltypmods = lappend_int(rte->coltypmods, -1);
+ rte->colcollations = lappend_oid(rte->colcollations, InvalidOid);
+
+ n_dontexpand_columns += 2;
+ }
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Subqueries are never checked for access rights.
+ */
+ rte->lateral = false;
+ rte->inh = false; /* never true for subqueries */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+ rte->insertedCols = NULL;
+ rte->updatedCols = NULL;
+ rte->extraUpdatedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ psi = buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ rte->coltypes, rte->coltypmods,
+ rte->colcollations);
+
+ /*
+ * The columns added by search and cycle clauses are not included in star
+ * expansion in queries contained in the CTE.
+ */
+ if (rte->ctelevelsup > 0)
+ for (int i = 0; i < n_dontexpand_columns; i++)
+ psi->p_nscolumns[list_length(psi->p_names->colnames) - 1 - i].p_dontexpand = true;
+
+ return psi;
+}
+
+/*
+ * Add an entry for an ephemeral named relation reference to the pstate's
+ * range table (p_rtable).
+ * Then, construct and return a ParseNamespaceItem for the new RTE.
+ *
+ * It is expected that the RangeVar, which up until now is only known to be an
+ * ephemeral named relation, will (in conjunction with the QueryEnvironment in
+ * the ParseState), create a RangeTblEntry for a specific *kind* of ephemeral
+ * named relation, based on enrtype.
+ *
+ * This is much like addRangeTableEntry() except that it makes an RTE for an
+ * ephemeral named relation.
+ */
+ParseNamespaceItem *
+addRangeTableEntryForENR(ParseState *pstate,
+ RangeVar *rv,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Alias *alias = rv->alias;
+ char *refname = alias ? alias->aliasname : rv->relname;
+ EphemeralNamedRelationMetadata enrmd;
+ TupleDesc tupdesc;
+ int attno;
+
+ Assert(pstate != NULL);
+ enrmd = get_visible_ENR(pstate, rv->relname);
+ Assert(enrmd != NULL);
+
+ switch (enrmd->enrtype)
+ {
+ case ENR_NAMED_TUPLESTORE:
+ rte->rtekind = RTE_NAMEDTUPLESTORE;
+ break;
+
+ default:
+ elog(ERROR, "unexpected enrtype: %d", enrmd->enrtype);
+ return NULL; /* for fussy compilers */
+ }
+
+ /*
+ * Record dependency on a relation. This allows plans to be invalidated
+ * if they access transition tables linked to a table that is altered.
+ */
+ rte->relid = enrmd->reliddesc;
+
+ /*
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names.
+ */
+ tupdesc = ENRMetadataGetTupDesc(enrmd);
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(tupdesc, alias, rte->eref);
+
+ /* Record additional data for ENR, including column type info */
+ rte->enrname = enrmd->name;
+ rte->enrtuples = enrmd->enrtuples;
+ rte->coltypes = NIL;
+ rte->coltypmods = NIL;
+ rte->colcollations = NIL;
+ for (attno = 1; attno <= tupdesc->natts; ++attno)
+ {
+ Form_pg_attribute att = TupleDescAttr(tupdesc, attno - 1);
+
+ if (att->attisdropped)
+ {
+ /* Record zeroes for a dropped column */
+ rte->coltypes = lappend_oid(rte->coltypes, InvalidOid);
+ rte->coltypmods = lappend_int(rte->coltypmods, 0);
+ rte->colcollations = lappend_oid(rte->colcollations, InvalidOid);
+ }
+ else
+ {
+ /* Let's just make sure we can tell this isn't dropped */
+ if (att->atttypid == InvalidOid)
+ elog(ERROR, "atttypid is invalid for non-dropped column in \"%s\"",
+ rv->relname);
+ rte->coltypes = lappend_oid(rte->coltypes, att->atttypid);
+ rte->coltypmods = lappend_int(rte->coltypmods, att->atttypmod);
+ rte->colcollations = lappend_oid(rte->colcollations,
+ att->attcollation);
+ }
+ }
+
+ /*
+ * Set flags and access permissions.
+ *
+ * ENRs are never checked for access rights.
+ */
+ rte->lateral = false;
+ rte->inh = false; /* never true for ENRs */
+ rte->inFromCl = inFromCl;
+
+ rte->requiredPerms = 0;
+ rte->checkAsUser = InvalidOid;
+ rte->selectedCols = NULL;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable),
+ tupdesc);
+}
+
+
+/*
+ * Has the specified refname been selected FOR UPDATE/FOR SHARE?
+ *
+ * This is used when we have not yet done transformLockingClause, but need
+ * to know the correct lock to take during initial opening of relations.
+ *
+ * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE,
+ * since the table-level lock is the same either way.
+ */
+bool
+isLockedRefname(ParseState *pstate, const char *refname)
+{
+ ListCell *l;
+
+ /*
+ * If we are in a subquery specified as locked FOR UPDATE/SHARE from
+ * parent level, then act as though there's a generic FOR UPDATE here.
+ */
+ if (pstate->p_locked_from_parent)
+ return true;
+
+ foreach(l, pstate->p_locking_clause)
+ {
+ LockingClause *lc = (LockingClause *) lfirst(l);
+
+ if (lc->lockedRels == NIL)
+ {
+ /* all tables used in query */
+ return true;
+ }
+ else
+ {
+ /* just the named tables */
+ ListCell *l2;
+
+ foreach(l2, lc->lockedRels)
+ {
+ RangeVar *thisrel = (RangeVar *) lfirst(l2);
+
+ if (strcmp(refname, thisrel->relname) == 0)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * Add the given nsitem/RTE as a top-level entry in the pstate's join list
+ * and/or namespace list. (We assume caller has checked for any
+ * namespace conflicts.) The nsitem is always marked as unconditionally
+ * visible, that is, not LATERAL-only.
+ */
+void
+addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem,
+ bool addToJoinList,
+ bool addToRelNameSpace, bool addToVarNameSpace)
+{
+ if (addToJoinList)
+ {
+ RangeTblRef *rtr = makeNode(RangeTblRef);
+
+ rtr->rtindex = nsitem->p_rtindex;
+ pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+ }
+ if (addToRelNameSpace || addToVarNameSpace)
+ {
+ /* Set the new nsitem's visibility flags correctly */
+ nsitem->p_rel_visible = addToRelNameSpace;
+ nsitem->p_cols_visible = addToVarNameSpace;
+ nsitem->p_lateral_only = false;
+ nsitem->p_lateral_ok = true;
+ pstate->p_namespace = lappend(pstate->p_namespace, nsitem);
+ }
+}
+
+/*
+ * expandRTE -- expand the columns of a rangetable entry
+ *
+ * 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.
+ *
+ * rtindex, sublevels_up, and location are the varno, varlevelsup, and location
+ * values to use in the created Vars. Ordinarily rtindex should match the
+ * actual position of the RTE in its rangetable.
+ *
+ * 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(RangeTblEntry *rte, int rtindex, int sublevels_up,
+ int location, bool include_dropped,
+ List **colnames, List **colvars)
+{
+ int varattno;
+
+ if (colnames)
+ *colnames = NIL;
+ if (colvars)
+ *colvars = NIL;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ /* Ordinary relation RTE */
+ expandRelation(rte->relid, rte->eref,
+ rtindex, sublevels_up, location,
+ include_dropped, colnames, colvars);
+ break;
+ case RTE_SUBQUERY:
+ {
+ /* Subquery RTE */
+ ListCell *aliasp_item = list_head(rte->eref->colnames);
+ ListCell *tlistitem;
+
+ varattno = 0;
+ foreach(tlistitem, rte->subquery->targetList)
+ {
+ TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+ if (te->resjunk)
+ continue;
+ varattno++;
+ Assert(varattno == te->resno);
+
+ /*
+ * In scenarios where columns have been added to a view
+ * since the outer query was originally parsed, there can
+ * be more items in the subquery tlist than the outer
+ * query expects. We should ignore such extra column(s)
+ * --- compare the behavior for composite-returning
+ * functions, in the RTE_FUNCTION case below.
+ */
+ if (!aliasp_item)
+ break;
+
+ if (colnames)
+ {
+ char *label = strVal(lfirst(aliasp_item));
+
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, varattno,
+ exprType((Node *) te->expr),
+ exprTypmod((Node *) te->expr),
+ exprCollation((Node *) te->expr),
+ sublevels_up);
+ varnode->location = location;
+
+ *colvars = lappend(*colvars, varnode);
+ }
+
+ aliasp_item = lnext(rte->eref->colnames, aliasp_item);
+ }
+ }
+ break;
+ case RTE_FUNCTION:
+ {
+ /* Function RTE */
+ int atts_done = 0;
+ ListCell *lc;
+
+ foreach(lc, rte->functions)
+ {
+ RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+ TypeFuncClass functypclass;
+ Oid funcrettype;
+ TupleDesc tupdesc;
+
+ functypclass = get_expr_result_type(rtfunc->funcexpr,
+ &funcrettype,
+ &tupdesc);
+ if (functypclass == TYPEFUNC_COMPOSITE ||
+ functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
+ {
+ /* Composite data type, e.g. a table's row type */
+ Assert(tupdesc);
+ expandTupleDesc(tupdesc, rte->eref,
+ rtfunc->funccolcount, atts_done,
+ rtindex, sublevels_up, location,
+ include_dropped, colnames, colvars);
+ }
+ else if (functypclass == TYPEFUNC_SCALAR)
+ {
+ /* Base data type, i.e. scalar */
+ if (colnames)
+ *colnames = lappend(*colnames,
+ list_nth(rte->eref->colnames,
+ atts_done));
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, atts_done + 1,
+ funcrettype,
+ exprTypmod(rtfunc->funcexpr),
+ exprCollation(rtfunc->funcexpr),
+ sublevels_up);
+ varnode->location = location;
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ if (colnames)
+ {
+ List *namelist;
+
+ /* extract appropriate subset of column list */
+ namelist = list_copy_tail(rte->eref->colnames,
+ atts_done);
+ namelist = list_truncate(namelist,
+ rtfunc->funccolcount);
+ *colnames = list_concat(*colnames, namelist);
+ }
+
+ if (colvars)
+ {
+ ListCell *l1;
+ ListCell *l2;
+ ListCell *l3;
+ int attnum = atts_done;
+
+ forthree(l1, rtfunc->funccoltypes,
+ l2, rtfunc->funccoltypmods,
+ l3, rtfunc->funccolcollations)
+ {
+ Oid attrtype = lfirst_oid(l1);
+ int32 attrtypmod = lfirst_int(l2);
+ Oid attrcollation = lfirst_oid(l3);
+ Var *varnode;
+
+ attnum++;
+ varnode = makeVar(rtindex,
+ attnum,
+ attrtype,
+ attrtypmod,
+ attrcollation,
+ sublevels_up);
+ varnode->location = location;
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ }
+ else
+ {
+ /* addRangeTableEntryForFunction should've caught this */
+ elog(ERROR, "function in FROM has unsupported return type");
+ }
+ atts_done += rtfunc->funccolcount;
+ }
+
+ /* Append the ordinality column if any */
+ if (rte->funcordinality)
+ {
+ if (colnames)
+ *colnames = lappend(*colnames,
+ llast(rte->eref->colnames));
+
+ if (colvars)
+ {
+ Var *varnode = makeVar(rtindex,
+ atts_done + 1,
+ INT8OID,
+ -1,
+ InvalidOid,
+ sublevels_up);
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ }
+ break;
+ case RTE_JOIN:
+ {
+ /* Join RTE */
+ ListCell *colname;
+ ListCell *aliasvar;
+
+ Assert(list_length(rte->eref->colnames) == list_length(rte->joinaliasvars));
+
+ varattno = 0;
+ forboth(colname, rte->eref->colnames, aliasvar, rte->joinaliasvars)
+ {
+ Node *avar = (Node *) lfirst(aliasvar);
+
+ varattno++;
+
+ /*
+ * During ordinary parsing, there will never be any
+ * deleted columns in the join. While this function is
+ * also used by the rewriter and planner, they do not
+ * currently call it on any JOIN RTEs. Therefore, this
+ * next bit is dead code, but it seems prudent to handle
+ * the case correctly anyway.
+ */
+ if (avar == NULL)
+ {
+ if (include_dropped)
+ {
+ if (colnames)
+ *colnames = lappend(*colnames,
+ makeString(pstrdup("")));
+ if (colvars)
+ {
+ /*
+ * Can't use join's column type here (it might
+ * be dropped!); but it doesn't really matter
+ * what type the Const claims to be.
+ */
+ *colvars = lappend(*colvars,
+ makeNullConst(INT4OID, -1,
+ InvalidOid));
+ }
+ }
+ continue;
+ }
+
+ if (colnames)
+ {
+ char *label = strVal(lfirst(colname));
+
+ *colnames = lappend(*colnames,
+ makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ /*
+ * If the joinaliasvars entry is a simple Var, just
+ * copy it (with adjustment of varlevelsup and
+ * location); otherwise it is a JOIN USING column and
+ * we must generate a join alias Var. This matches
+ * the results that expansion of "join.*" by
+ * expandNSItemVars would have produced, if we had
+ * access to the ParseNamespaceItem for the join.
+ */
+ if (IsA(avar, Var))
+ {
+ varnode = copyObject((Var *) avar);
+ varnode->varlevelsup = sublevels_up;
+ }
+ else
+ varnode = makeVar(rtindex, varattno,
+ exprType(avar),
+ exprTypmod(avar),
+ exprCollation(avar),
+ sublevels_up);
+ varnode->location = location;
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+ }
+ break;
+ case RTE_TABLEFUNC:
+ case RTE_VALUES:
+ case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
+ {
+ /* Tablefunc, Values, CTE, or ENR RTE */
+ ListCell *aliasp_item = list_head(rte->eref->colnames);
+ ListCell *lct;
+ ListCell *lcm;
+ ListCell *lcc;
+
+ varattno = 0;
+ forthree(lct, rte->coltypes,
+ lcm, rte->coltypmods,
+ lcc, rte->colcollations)
+ {
+ Oid coltype = lfirst_oid(lct);
+ int32 coltypmod = lfirst_int(lcm);
+ Oid colcoll = lfirst_oid(lcc);
+
+ varattno++;
+
+ if (colnames)
+ {
+ /* Assume there is one alias per output column */
+ if (OidIsValid(coltype))
+ {
+ char *label = strVal(lfirst(aliasp_item));
+
+ *colnames = lappend(*colnames,
+ makeString(pstrdup(label)));
+ }
+ else if (include_dropped)
+ *colnames = lappend(*colnames,
+ makeString(pstrdup("")));
+
+ aliasp_item = lnext(rte->eref->colnames, aliasp_item);
+ }
+
+ if (colvars)
+ {
+ if (OidIsValid(coltype))
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, varattno,
+ coltype, coltypmod, colcoll,
+ sublevels_up);
+ varnode->location = location;
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ else if (include_dropped)
+ {
+ /*
+ * It doesn't really matter what type the Const
+ * claims to be.
+ */
+ *colvars = lappend(*colvars,
+ makeNullConst(INT4OID, -1,
+ InvalidOid));
+ }
+ }
+ }
+ }
+ break;
+ case RTE_RESULT:
+ /* These expose no columns, so nothing to do */
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
+ }
+}
+
+/*
+ * expandRelation -- expandRTE subroutine
+ */
+static void
+expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up,
+ int location, bool include_dropped,
+ List **colnames, List **colvars)
+{
+ Relation rel;
+
+ /* Get the tupledesc and turn it over to expandTupleDesc */
+ rel = relation_open(relid, AccessShareLock);
+ expandTupleDesc(rel->rd_att, eref, rel->rd_att->natts, 0,
+ rtindex, sublevels_up,
+ location, include_dropped,
+ colnames, colvars);
+ relation_close(rel, AccessShareLock);
+}
+
+/*
+ * expandTupleDesc -- expandRTE subroutine
+ *
+ * Generate names and/or Vars for the first "count" attributes of the tupdesc,
+ * and append them to colnames/colvars. "offset" is added to the varattno
+ * that each Var would otherwise have, and we also skip the first "offset"
+ * entries in eref->colnames. (These provisions allow use of this code for
+ * an individual composite-returning function in an RTE_FUNCTION RTE.)
+ */
+static void
+expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
+ int rtindex, int sublevels_up,
+ int location, bool include_dropped,
+ List **colnames, List **colvars)
+{
+ ListCell *aliascell;
+ int varattno;
+
+ aliascell = (offset < list_length(eref->colnames)) ?
+ list_nth_cell(eref->colnames, offset) : NULL;
+
+ Assert(count <= tupdesc->natts);
+ for (varattno = 0; varattno < count; varattno++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupdesc, 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, -1, InvalidOid));
+ }
+ }
+ if (aliascell)
+ aliascell = lnext(eref->colnames, aliascell);
+ continue;
+ }
+
+ if (colnames)
+ {
+ char *label;
+
+ if (aliascell)
+ {
+ label = strVal(lfirst(aliascell));
+ aliascell = lnext(eref->colnames, aliascell);
+ }
+ else
+ {
+ /* If we run out of aliases, use the underlying name */
+ label = NameStr(attr->attname);
+ }
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
+
+ if (colvars)
+ {
+ Var *varnode;
+
+ varnode = makeVar(rtindex, varattno + offset + 1,
+ attr->atttypid, attr->atttypmod,
+ attr->attcollation,
+ sublevels_up);
+ varnode->location = location;
+
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
+}
+
+/*
+ * expandNSItemVars
+ * Produce a list of Vars, and optionally a list of column names,
+ * for the non-dropped columns of the nsitem.
+ *
+ * The emitted Vars are marked with the given sublevels_up and location.
+ *
+ * If colnames isn't NULL, a list of String items for the columns is stored
+ * there; note that it's just a subset of the RTE's eref list, and hence
+ * the list elements mustn't be modified.
+ */
+List *
+expandNSItemVars(ParseNamespaceItem *nsitem,
+ int sublevels_up, int location,
+ List **colnames)
+{
+ List *result = NIL;
+ int colindex;
+ ListCell *lc;
+
+ if (colnames)
+ *colnames = NIL;
+ colindex = 0;
+ foreach(lc, nsitem->p_names->colnames)
+ {
+ Value *colnameval = (Value *) lfirst(lc);
+ const char *colname = strVal(colnameval);
+ ParseNamespaceColumn *nscol = nsitem->p_nscolumns + colindex;
+
+ if (nscol->p_dontexpand)
+ {
+ /* skip */
+ }
+ else if (colname[0])
+ {
+ Var *var;
+
+ Assert(nscol->p_varno > 0);
+ var = makeVar(nscol->p_varno,
+ nscol->p_varattno,
+ nscol->p_vartype,
+ nscol->p_vartypmod,
+ nscol->p_varcollid,
+ sublevels_up);
+ /* makeVar doesn't offer parameters for these, so set by hand: */
+ var->varnosyn = nscol->p_varnosyn;
+ var->varattnosyn = nscol->p_varattnosyn;
+ var->location = location;
+ result = lappend(result, var);
+ if (colnames)
+ *colnames = lappend(*colnames, colnameval);
+ }
+ else
+ {
+ /* dropped column, ignore */
+ Assert(nscol->p_varno == 0);
+ }
+ colindex++;
+ }
+ return result;
+}
+
+/*
+ * expandNSItemAttrs -
+ * Workhorse for "*" expansion: produce a list of targetentries
+ * for the attributes of the nsitem
+ *
+ * pstate->p_next_resno determines the resnos assigned to the TLEs.
+ * The referenced columns are marked as requiring SELECT access.
+ */
+List *
+expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
+ int sublevels_up, int location)
+{
+ RangeTblEntry *rte = nsitem->p_rte;
+ List *names,
+ *vars;
+ ListCell *name,
+ *var;
+ List *te_list = NIL;
+
+ vars = expandNSItemVars(nsitem, sublevels_up, location, &names);
+
+ /*
+ * Require read access to the table. This is normally redundant with the
+ * markVarForSelectPriv calls below, but not if the table has zero
+ * columns. We need not do anything if the nsitem is for a join: its
+ * component tables will have been marked ACL_SELECT when they were added
+ * to the rangetable. (This step changes things only for the target
+ * relation of UPDATE/DELETE, which cannot be under a join.)
+ */
+ if (rte->rtekind == RTE_RELATION)
+ rte->requiredPerms |= ACL_SELECT;
+
+ forboth(name, names, var, vars)
+ {
+ char *label = strVal(lfirst(name));
+ Var *varnode = (Var *) lfirst(var);
+ TargetEntry *te;
+
+ te = makeTargetEntry((Expr *) varnode,
+ (AttrNumber) pstate->p_next_resno++,
+ label,
+ false);
+ te_list = lappend(te_list, te);
+
+ /* Require read access to each column */
+ markVarForSelectPriv(pstate, varnode);
+ }
+
+ Assert(name == NULL && var == NULL); /* lists not the same length? */
+
+ return te_list;
+}
+
+/*
+ * get_rte_attribute_name
+ * Get an attribute name from a RangeTblEntry
+ *
+ * This is unlike get_attname() because we use aliases if available.
+ * In particular, it will work on an RTE for a subselect or join, whereas
+ * get_attname() only works on real relations.
+ *
+ * "*" is returned if the given attnum is InvalidAttrNumber --- this case
+ * occurs when a Var represents a whole tuple of a relation.
+ *
+ * It is caller's responsibility to not call this on a dropped attribute.
+ * (You will get some answer for such cases, but it might not be sensible.)
+ */
+char *
+get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
+{
+ if (attnum == InvalidAttrNumber)
+ return "*";
+
+ /*
+ * If there is a user-written column alias, use it.
+ */
+ if (rte->alias &&
+ attnum > 0 && attnum <= list_length(rte->alias->colnames))
+ return strVal(list_nth(rte->alias->colnames, attnum - 1));
+
+ /*
+ * If the RTE is a relation, go to the system catalogs not the
+ * eref->colnames list. This is a little slower but it will give the
+ * right answer if the column has been renamed since the eref list was
+ * built (which can easily happen for rules).
+ */
+ if (rte->rtekind == RTE_RELATION)
+ return get_attname(rte->relid, attnum, false);
+
+ /*
+ * Otherwise use the column name from eref. There should always be one.
+ */
+ if (attnum > 0 && attnum <= list_length(rte->eref->colnames))
+ return strVal(list_nth(rte->eref->colnames, attnum - 1));
+
+ /* else caller gave us a bogus attnum */
+ elog(ERROR, "invalid attnum %d for rangetable entry %s",
+ attnum, rte->eref->aliasname);
+ return NULL; /* keep compiler quiet */
+}
+
+/*
+ * get_rte_attribute_is_dropped
+ * Check whether attempted attribute ref is to a dropped column
+ */
+bool
+get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
+{
+ bool result;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ {
+ /*
+ * Plain relation RTE --- get the attribute's catalog entry
+ */
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(rte->relid),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(tp)) /* shouldn't happen */
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, rte->relid);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ result = att_tup->attisdropped;
+ ReleaseSysCache(tp);
+ }
+ break;
+ case RTE_SUBQUERY:
+ case RTE_TABLEFUNC:
+ case RTE_VALUES:
+ case RTE_CTE:
+
+ /*
+ * Subselect, Table Functions, Values, CTE RTEs never have dropped
+ * columns
+ */
+ result = false;
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ {
+ /* Check dropped-ness by testing for valid coltype */
+ if (attnum <= 0 ||
+ attnum > list_length(rte->coltypes))
+ elog(ERROR, "invalid varattno %d", attnum);
+ result = !OidIsValid((list_nth_oid(rte->coltypes, attnum - 1)));
+ }
+ 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. This will be
+ * signaled to us by a null pointer in the joinaliasvars list.
+ */
+ Var *aliasvar;
+
+ if (attnum <= 0 ||
+ attnum > list_length(rte->joinaliasvars))
+ elog(ERROR, "invalid varattno %d", attnum);
+ aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
+
+ result = (aliasvar == NULL);
+ }
+ break;
+ case RTE_FUNCTION:
+ {
+ /* Function RTE */
+ ListCell *lc;
+ int atts_done = 0;
+
+ /*
+ * Dropped attributes are only possible with functions that
+ * return named composite types. In such a case we have to
+ * look up the result type to see if it currently has this
+ * column dropped. So first, loop over the funcs until we
+ * find the one that covers the requested column.
+ */
+ foreach(lc, rte->functions)
+ {
+ RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+
+ if (attnum > atts_done &&
+ attnum <= atts_done + rtfunc->funccolcount)
+ {
+ TupleDesc tupdesc;
+
+ tupdesc = get_expr_result_tupdesc(rtfunc->funcexpr,
+ true);
+ if (tupdesc)
+ {
+ /* Composite data type, e.g. a table's row type */
+ Form_pg_attribute att_tup;
+
+ Assert(tupdesc);
+ Assert(attnum - atts_done <= tupdesc->natts);
+ att_tup = TupleDescAttr(tupdesc,
+ attnum - atts_done - 1);
+ return att_tup->attisdropped;
+ }
+ /* Otherwise, it can't have any dropped columns */
+ return false;
+ }
+ atts_done += rtfunc->funccolcount;
+ }
+
+ /* If we get here, must be looking for the ordinality column */
+ if (rte->funcordinality && attnum == atts_done + 1)
+ return false;
+
+ /* this probably can't happen ... */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column %d of relation \"%s\" does not exist",
+ attnum,
+ rte->eref->aliasname)));
+ result = false; /* keep compiler quiet */
+ }
+ break;
+ case RTE_RESULT:
+ /* this probably can't happen ... */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column %d of relation \"%s\" does not exist",
+ attnum,
+ rte->eref->aliasname)));
+ result = false; /* keep compiler quiet */
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
+ result = false; /* keep compiler quiet */
+ }
+
+ return result;
+}
+
+/*
+ * Given a targetlist and a resno, return the matching TargetEntry
+ *
+ * Returns NULL if resno is not present in list.
+ *
+ * Note: we need to search, rather than just indexing with list_nth(),
+ * because not all tlists are sorted by resno.
+ */
+TargetEntry *
+get_tle_by_resno(List *tlist, AttrNumber resno)
+{
+ ListCell *l;
+
+ foreach(l, tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+ if (tle->resno == resno)
+ return tle;
+ }
+ return NULL;
+}
+
+/*
+ * Given a Query and rangetable index, return relation's RowMarkClause if any
+ *
+ * Returns NULL if relation is not selected FOR UPDATE/SHARE
+ */
+RowMarkClause *
+get_parse_rowmark(Query *qry, Index rtindex)
+{
+ ListCell *l;
+
+ foreach(l, qry->rowMarks)
+ {
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ if (rc->rti == rtindex)
+ return rc;
+ }
+ return NULL;
+}
+
+/*
+ * given relation and att name, return attnum of variable
+ *
+ * Returns InvalidAttrNumber if the attr doesn't exist (or is dropped).
+ *
+ * This should only be used if the relation is already
+ * table_open()'ed. Use the cache version get_attnum()
+ * for access to non-opened relations.
+ */
+int
+attnameAttNum(Relation rd, const char *attname, bool sysColOK)
+{
+ int i;
+
+ for (i = 0; i < RelationGetNumberOfAttributes(rd); i++)
+ {
+ Form_pg_attribute att = TupleDescAttr(rd->rd_att, i);
+
+ if (namestrcmp(&(att->attname), attname) == 0 && !att->attisdropped)
+ return i + 1;
+ }
+
+ if (sysColOK)
+ {
+ if ((i = specialAttNum(attname)) != InvalidAttrNumber)
+ return i;
+ }
+
+ /* on failure */
+ return InvalidAttrNumber;
+}
+
+/* specialAttNum()
+ *
+ * Check attribute name to see if it is "special", e.g. "xmin".
+ * - thomas 2000-02-07
+ *
+ * Note: this only discovers whether the name could be a system attribute.
+ * Caller needs to ensure that it really is an attribute of the rel.
+ */
+static int
+specialAttNum(const char *attname)
+{
+ const FormData_pg_attribute *sysatt;
+
+ sysatt = SystemAttributeByName(attname);
+ if (sysatt != NULL)
+ return sysatt->attnum;
+ return InvalidAttrNumber;
+}
+
+
+/*
+ * given attribute id, return name of that attribute
+ *
+ * This should only be used if the relation is already
+ * table_open()'ed. Use the cache version get_atttype()
+ * for access to non-opened relations.
+ */
+const NameData *
+attnumAttName(Relation rd, int attid)
+{
+ if (attid <= 0)
+ {
+ const FormData_pg_attribute *sysatt;
+
+ sysatt = SystemAttributeDefinition(attid);
+ return &sysatt->attname;
+ }
+ if (attid > rd->rd_att->natts)
+ elog(ERROR, "invalid attribute number %d", attid);
+ return &TupleDescAttr(rd->rd_att, attid - 1)->attname;
+}
+
+/*
+ * given attribute id, return type of that attribute
+ *
+ * This should only be used if the relation is already
+ * table_open()'ed. Use the cache version get_atttype()
+ * for access to non-opened relations.
+ */
+Oid
+attnumTypeId(Relation rd, int attid)
+{
+ if (attid <= 0)
+ {
+ const FormData_pg_attribute *sysatt;
+
+ sysatt = SystemAttributeDefinition(attid);
+ return sysatt->atttypid;
+ }
+ if (attid > rd->rd_att->natts)
+ elog(ERROR, "invalid attribute number %d", attid);
+ return TupleDescAttr(rd->rd_att, attid - 1)->atttypid;
+}
+
+/*
+ * given attribute id, return collation of that attribute
+ *
+ * This should only be used if the relation is already table_open()'ed.
+ */
+Oid
+attnumCollationId(Relation rd, int attid)
+{
+ if (attid <= 0)
+ {
+ /* All system attributes are of noncollatable types. */
+ return InvalidOid;
+ }
+ if (attid > rd->rd_att->natts)
+ elog(ERROR, "invalid attribute number %d", attid);
+ return TupleDescAttr(rd->rd_att, attid - 1)->attcollation;
+}
+
+/*
+ * Generate a suitable error about a missing RTE.
+ *
+ * Since this is a very common type of error, we work rather hard to
+ * produce a helpful message.
+ */
+void
+errorMissingRTE(ParseState *pstate, RangeVar *relation)
+{
+ RangeTblEntry *rte;
+ const char *badAlias = NULL;
+
+ /*
+ * Check to see if there are any potential matches in the query's
+ * rangetable. (Note: cases involving a bad schema name in the RangeVar
+ * will throw error immediately here. That seems OK.)
+ */
+ rte = searchRangeTableForRel(pstate, relation);
+
+ /*
+ * If we found a match that has an alias and the alias is visible in the
+ * namespace, then the problem is probably use of the relation's real name
+ * instead of its alias, ie "SELECT foo.* FROM foo f". This mistake is
+ * common enough to justify a specific hint.
+ *
+ * If we found a match that doesn't meet those criteria, assume the
+ * problem is illegal use of a relation outside its scope, as in the
+ * MySQL-ism "SELECT ... FROM a, b LEFT JOIN c ON (a.x = c.y)".
+ */
+ if (rte && rte->alias &&
+ strcmp(rte->eref->aliasname, relation->relname) != 0)
+ {
+ ParseNamespaceItem *nsitem;
+ int sublevels_up;
+
+ nsitem = refnameNamespaceItem(pstate, NULL, rte->eref->aliasname,
+ relation->location,
+ &sublevels_up);
+ if (nsitem && nsitem->p_rte == rte)
+ badAlias = rte->eref->aliasname;
+ }
+
+ if (rte)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("invalid reference to FROM-clause entry for table \"%s\"",
+ relation->relname),
+ (badAlias ?
+ errhint("Perhaps you meant to reference the table alias \"%s\".",
+ badAlias) :
+ errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
+ rte->eref->aliasname)),
+ parser_errposition(pstate, relation->location)));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("missing FROM-clause entry for table \"%s\"",
+ relation->relname),
+ parser_errposition(pstate, relation->location)));
+}
+
+/*
+ * Generate a suitable error about a missing column.
+ *
+ * Since this is a very common type of error, we work rather hard to
+ * produce a helpful message.
+ */
+void
+errorMissingColumn(ParseState *pstate,
+ const char *relname, const char *colname, int location)
+{
+ FuzzyAttrMatchState *state;
+ char *closestfirst = NULL;
+
+ /*
+ * Search the entire rtable looking for possible matches. If we find one,
+ * emit a hint about it.
+ *
+ * TODO: improve this code (and also errorMissingRTE) to mention using
+ * LATERAL if appropriate.
+ */
+ state = searchRangeTableForCol(pstate, relname, colname, location);
+
+ /*
+ * Extract closest col string for best match, if any.
+ *
+ * Infer an exact match referenced despite not being visible from the fact
+ * that an attribute number was not present in state passed back -- this
+ * is what is reported when !closestfirst. There might also be an exact
+ * match that was qualified with an incorrect alias, in which case
+ * closestfirst will be set (so hint is the same as generic fuzzy case).
+ */
+ if (state->rfirst && AttributeNumberIsValid(state->first))
+ closestfirst = strVal(list_nth(state->rfirst->eref->colnames,
+ state->first - 1));
+
+ if (!state->rsecond)
+ {
+ /*
+ * Handle case where there is zero or one column suggestions to hint,
+ * including exact matches referenced but not visible.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ relname ?
+ errmsg("column %s.%s does not exist", relname, colname) :
+ errmsg("column \"%s\" does not exist", colname),
+ state->rfirst ? closestfirst ?
+ errhint("Perhaps you meant to reference the column \"%s.%s\".",
+ state->rfirst->eref->aliasname, closestfirst) :
+ errhint("There is a column named \"%s\" in table \"%s\", but it cannot be referenced from this part of the query.",
+ colname, state->rfirst->eref->aliasname) : 0,
+ parser_errposition(pstate, location)));
+ }
+ else
+ {
+ /* Handle case where there are two equally useful column hints */
+ char *closestsecond;
+
+ closestsecond = strVal(list_nth(state->rsecond->eref->colnames,
+ state->second - 1));
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ relname ?
+ errmsg("column %s.%s does not exist", relname, colname) :
+ errmsg("column \"%s\" does not exist", colname),
+ errhint("Perhaps you meant to reference the column \"%s.%s\" or the column \"%s.%s\".",
+ state->rfirst->eref->aliasname, closestfirst,
+ state->rsecond->eref->aliasname, closestsecond),
+ parser_errposition(pstate, location)));
+ }
+}
+
+
+/*
+ * Examine a fully-parsed query, and return true iff any relation underlying
+ * the query is a temporary relation (table, view, or materialized view).
+ */
+bool
+isQueryUsingTempRelation(Query *query)
+{
+ return isQueryUsingTempRelation_walker((Node *) query, NULL);
+}
+
+static bool
+isQueryUsingTempRelation_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+
+ if (IsA(node, Query))
+ {
+ Query *query = (Query *) node;
+ ListCell *rtable;
+
+ foreach(rtable, query->rtable)
+ {
+ RangeTblEntry *rte = lfirst(rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ Relation rel = table_open(rte->relid, AccessShareLock);
+ char relpersistence = rel->rd_rel->relpersistence;
+
+ table_close(rel, AccessShareLock);
+ if (relpersistence == RELPERSISTENCE_TEMP)
+ return true;
+ }
+ }
+
+ return query_tree_walker(query,
+ isQueryUsingTempRelation_walker,
+ context,
+ QTW_IGNORE_JOINALIASES);
+ }
+
+ return expression_tree_walker(node,
+ isQueryUsingTempRelation_walker,
+ context);
+}