summaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c314
1 files changed, 193 insertions, 121 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 47188fcd4ff..4888311f444 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -37,14 +37,37 @@
#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 RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
- const char *refname, int location);
-static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
- int location);
+
+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,
+ const char *colname, int location,
+ int fuzzy_rte_penalty,
+ FuzzyAttrMatchState *fuzzystate);
static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
int rtindex, AttrNumber col);
static void expandRelation(Oid relid, Alias *eref,
@@ -61,28 +84,28 @@ static bool isQueryUsingTempRelation_walker(Node *node, void *context);
/*
- * refnameRangeTblEntry
- * Given a possibly-qualified refname, look to see if it matches any RTE.
- * If so, return a pointer to the RangeTblEntry; else return NULL.
+ * 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 RTE's nesting depth (0 = current) into *sublevels_up.
+ * 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 RTE with matching
+ * An unqualified refname (schemaname == NULL) can match any item with matching
* alias, or matching unqualified relname in the case of alias-less relation
- * RTEs. It is possible that such a refname matches multiple RTEs in the
+ * 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 RTE
+ * 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.
*/
-RangeTblEntry *
-refnameRangeTblEntry(ParseState *pstate,
+ParseNamespaceItem *
+refnameNamespaceItem(ParseState *pstate,
const char *schemaname,
const char *refname,
int location,
@@ -115,7 +138,7 @@ refnameRangeTblEntry(ParseState *pstate,
while (pstate != NULL)
{
- RangeTblEntry *result;
+ ParseNamespaceItem *result;
if (OidIsValid(relId))
result = scanNameSpaceForRelid(pstate, relId, location);
@@ -136,8 +159,8 @@ refnameRangeTblEntry(ParseState *pstate,
}
/*
- * Search the query's table namespace for an RTE matching the
- * given unqualified refname. Return the RTE if a unique match, or 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
@@ -152,10 +175,10 @@ refnameRangeTblEntry(ParseState *pstate,
* this situation, and complain only if there's actually an ambiguous
* reference to "x".
*/
-static RangeTblEntry *
+static ParseNamespaceItem *
scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
{
- RangeTblEntry *result = NULL;
+ ParseNamespaceItem *result = NULL;
ListCell *l;
foreach(l, pstate->p_namespace)
@@ -179,24 +202,24 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
refname),
parser_errposition(pstate, location)));
check_lateral_ref_ok(pstate, nsitem, location);
- result = rte;
+ result = nsitem;
}
}
return result;
}
/*
- * Search the query's table namespace for a relation RTE matching the
- * given relation OID. Return the RTE if a unique match, or NULL
+ * 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 refnameRangeTblEntry to understand why this
+ * See the comments for refnameNamespaceItem to understand why this
* acts the way it does.
*/
-static RangeTblEntry *
+static ParseNamespaceItem *
scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
{
- RangeTblEntry *result = NULL;
+ ParseNamespaceItem *result = NULL;
ListCell *l;
foreach(l, pstate->p_namespace)
@@ -223,7 +246,7 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
relid),
parser_errposition(pstate, location)));
check_lateral_ref_ok(pstate, nsitem, location);
- result = rte;
+ result = nsitem;
}
}
return result;
@@ -299,7 +322,7 @@ scanNameSpaceForENR(ParseState *pstate, const char *refname)
* See if any RangeTblEntry could possibly match the RangeVar.
* If so, return a pointer to the RangeTblEntry; else return NULL.
*
- * This is different from refnameRangeTblEntry in that it considers every
+ * 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
@@ -431,6 +454,8 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
* 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
@@ -456,43 +481,35 @@ check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem,
}
/*
- * given an RTE, return RT index (starting with 1) of the entry,
- * and optionally get its nesting depth (0 = current). If sublevels_up
- * is NULL, only consider rels at the current nesting level.
- * Raises error if RTE not found.
+ * Given an RT index and nesting depth, find the corresponding
+ * ParseNamespaceItem (there must be one).
*/
-int
-RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
+ParseNamespaceItem *
+GetNSItemByRangeTablePosn(ParseState *pstate,
+ int varno,
+ int sublevels_up)
{
- int index;
- ListCell *l;
-
- if (sublevels_up)
- *sublevels_up = 0;
+ ListCell *lc;
- while (pstate != NULL)
+ while (sublevels_up-- > 0)
{
- index = 1;
- foreach(l, pstate->p_rtable)
- {
- if (rte == (RangeTblEntry *) lfirst(l))
- return index;
- index++;
- }
pstate = pstate->parentParseState;
- if (sublevels_up)
- (*sublevels_up)++;
- else
- break;
+ Assert(pstate != NULL);
}
+ foreach(lc, pstate->p_namespace)
+ {
+ ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
- elog(ERROR, "RTE not found (internal error)");
- return 0; /* keep compiler quiet */
+ 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.
- * This is the inverse of RTERangeTablePosn.
+ * (Note that the RTE need not be in the query's namespace.)
*/
RangeTblEntry *
GetRTEByRangeTablePosn(ParseState *pstate,
@@ -512,8 +529,7 @@ GetRTEByRangeTablePosn(ParseState *pstate,
* Fetch the CTE for a CTE-reference RTE.
*
* rtelevelsup is the number of query levels above the given pstate that the
- * RTE came from. Callers that don't have this information readily available
- * may pass -1 instead.
+ * RTE came from.
*/
CommonTableExpr *
GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
@@ -521,10 +537,6 @@ GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
Index levelsup;
ListCell *lc;
- /* Determine RTE's levelsup if caller didn't know it */
- if (rtelevelsup < 0)
- (void) RTERangeTablePosn(pstate, rte, &rtelevelsup);
-
Assert(rte->rtekind == RTE_CTE);
levelsup = rte->ctelevelsup + rtelevelsup;
while (levelsup-- > 0)
@@ -642,25 +654,94 @@ updateFuzzyAttrMatchState(int fuzzy_rte_penalty,
}
/*
+ * 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 item's 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;
+ Oid vartypeid;
+ int32 vartypmod;
+ Oid varcollid;
+
+ /*
+ * Scan the RTE's column names (or aliases) for a match. Complain if
+ * multiple matches.
+ */
+ attnum = scanRTEForColumn(pstate, rte,
+ 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 */
+ get_rte_attribute_type(rte, attnum,
+ &vartypeid, &vartypmod, &varcollid);
+ var = makeVar(nsitem->p_rtindex, attnum,
+ vartypeid, vartypmod, varcollid,
+ sublevels_up);
+ var->location = location;
+
+ /* Require read access to the column */
+ markVarForSelectPriv(pstate, var, rte);
+
+ return (Node *) var;
+}
+
+/*
* scanRTEForColumn
* Search the column names of a single RTE for the given name.
- * If found, return an appropriate Var node, else return NULL.
+ * If found, return the attnum (possibly negative, for a system column);
+ * else return InvalidAttrNumber.
* If the name proves ambiguous within this RTE, raise error.
*
- * Side effect: if we find a match, mark the RTE as requiring read access
- * for the column.
+ * pstate and location are passed only for error-reporting purposes.
*
- * Additional side effect: if fuzzystate is non-NULL, check non-system columns
+ * 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.
*/
-Node *
-scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
- int location, int fuzzy_rte_penalty,
+static int
+scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
+ const char *colname, int location,
+ int fuzzy_rte_penalty,
FuzzyAttrMatchState *fuzzystate)
{
- Node *result = NULL;
+ int result = InvalidAttrNumber;
int attnum = 0;
- Var *var;
ListCell *c;
/*
@@ -673,10 +754,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
*
* Should this somehow go wrong and we try to access a dropped column,
* we'll still catch it by virtue of the checks in
- * get_rte_attribute_type(), which is called by make_var(). That routine
- * has to do a cache lookup anyway, so the check there is cheap. Callers
- * interested in finding match with shortest distance need to defend
- * against this directly, though.
+ * get_rte_attribute_type(), which is called by scanNSItemForColumn().
+ * That routine has to do a cache lookup anyway, so the check there is
+ * cheap. Callers interested in finding match with shortest distance need
+ * to defend against this directly, though.
*/
foreach(c, rte->eref->colnames)
{
@@ -691,13 +772,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
errmsg("column reference \"%s\" is ambiguous",
colname),
parser_errposition(pstate, location)));
- var = make_var(pstate, rte, attnum, location);
- /* Require read access to the column */
- markVarForSelectPriv(pstate, var, rte);
- result = (Node *) var;
+ result = attnum;
}
- /* Updating fuzzy match state, if provided. */
+ /* Update fuzzy match state, if provided. */
if (fuzzystate != NULL)
updateFuzzyAttrMatchState(fuzzy_rte_penalty, fuzzystate,
rte, attcolname, colname, attnum);
@@ -720,39 +798,13 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname,
{
/* quick check to see if name could be a system column */
attnum = specialAttNum(colname);
-
- /* 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)));
-
if (attnum != InvalidAttrNumber)
{
/* now check to see if column actually is defined */
if (SearchSysCacheExists2(ATTNUM,
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum)))
- {
- var = make_var(pstate, rte, attnum, location);
- /* Require read access to the column */
- markVarForSelectPriv(pstate, var, rte);
- result = (Node *) var;
- }
+ result = attnum;
}
}
@@ -771,6 +823,7 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
int location)
{
Node *result = NULL;
+ int sublevels_up = 0;
ParseState *orig_pstate = pstate;
while (pstate != NULL)
@@ -780,7 +833,6 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
foreach(l, pstate->p_namespace)
{
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
- RangeTblEntry *rte = nsitem->p_rte;
Node *newresult;
/* Ignore table-only items */
@@ -790,9 +842,9 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
if (nsitem->p_lateral_only && !pstate->p_lateral_active)
continue;
- /* use orig_pstate here to get the right sublevels_up */
- newresult = scanRTEForColumn(orig_pstate, rte, colname, location,
- 0, NULL);
+ /* use orig_pstate here for consistency with other callers */
+ newresult = scanNSItemForColumn(orig_pstate, nsitem, sublevels_up,
+ colname, location);
if (newresult)
{
@@ -811,6 +863,7 @@ colNameToVar(ParseState *pstate, const char *colname, bool localonly,
break; /* found, or don't want to look at parent */
pstate = pstate->parentParseState;
+ sublevels_up++;
}
return result;
@@ -2182,9 +2235,23 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList,
bool addToRelNameSpace, bool addToVarNameSpace)
{
+ int rtindex;
+
+ /*
+ * Most callers have just added the RTE to the rangetable, so it's likely
+ * to be the last entry. Hence, it's a good idea to search the rangetable
+ * back-to-front.
+ */
+ for (rtindex = list_length(pstate->p_rtable); rtindex > 0; rtindex--)
+ {
+ if (rte == rt_fetch(rtindex, pstate->p_rtable))
+ break;
+ }
+ if (rtindex <= 0)
+ elog(ERROR, "RTE not found (internal error)");
+
if (addToJoinList)
{
- int rtindex = RTERangeTablePosn(pstate, rte, NULL);
RangeTblRef *rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
@@ -2196,6 +2263,7 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
nsitem->p_rte = rte;
+ nsitem->p_rtindex = rtindex;
nsitem->p_rel_visible = addToRelNameSpace;
nsitem->p_cols_visible = addToVarNameSpace;
nsitem->p_lateral_only = false;
@@ -2653,26 +2721,25 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset,
}
/*
- * expandRelAttrs -
+ * expandNSItemAttrs -
* Workhorse for "*" expansion: produce a list of targetentries
- * for the attributes of the RTE
+ * for the attributes of the nsitem
*
- * As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup
- * fields of the Vars produced, and location sets their location.
* pstate->p_next_resno determines the resnos assigned to the TLEs.
* The referenced columns are marked as requiring SELECT access.
*/
List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte,
- int rtindex, int sublevels_up, int location)
+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;
- expandRTE(rte, rtindex, sublevels_up, location, false,
+ expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false,
&names, &vars);
/*
@@ -3253,7 +3320,6 @@ void
errorMissingRTE(ParseState *pstate, RangeVar *relation)
{
RangeTblEntry *rte;
- int sublevels_up;
const char *badAlias = NULL;
/*
@@ -3274,11 +3340,17 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation)
* 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 &&
- refnameRangeTblEntry(pstate, NULL, rte->eref->aliasname,
- relation->location,
- &sublevels_up) == rte)
- badAlias = rte->eref->aliasname;
+ 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,