diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 85d7a96406..0656279654 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1344,7 +1344,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) int sublist_length = -1; bool lateral = false; RangeTblEntry *rte; - int rtindex; + ParseNamespaceItem *nsitem; ListCell *lc; ListCell *lc2; int i; @@ -1516,15 +1516,15 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) NULL, lateral, true); addRTEtoQuery(pstate, rte, true, true, true); - /* assume new rte is at end */ - rtindex = list_length(pstate->p_rtable); - Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); + /* grab the namespace item made by addRTEtoQuery */ + nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace); + Assert(rte == nsitem->p_rte); /* * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); - qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1); + qry->targetList = expandNSItemAttrs(pstate, nsitem, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index fe41918f33..ebbba2d7b5 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -52,7 +52,8 @@ #include "utils/syscache.h" /* Convenience macro for the most common makeNamespaceItem() case */ -#define makeDefaultNSItem(rte) makeNamespaceItem(rte, true, true, false, true) +#define makeDefaultNSItem(rte, rti) \ + makeNamespaceItem(rte, rti, true, true, false, true) static void extractRemainingColumns(List *common_colnames, List *src_colnames, List *src_colvars, @@ -78,7 +79,7 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n, List **namespace); static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); -static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, +static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, int rtindex, bool rel_visible, bool cols_visible, bool lateral_only, bool lateral_ok); static void setNamespaceColumnVisibility(List *namespace, bool cols_visible); @@ -216,12 +217,15 @@ setTargetTable(ParseState *pstate, RangeVar *relation, rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation, RowExclusiveLock, relation->alias, inh, false); - pstate->p_target_rangetblentry = rte; /* assume new rte is at end */ rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); + /* remember the RTE as being the query target */ + pstate->p_target_rangetblentry = rte; + pstate->p_target_rtindex = rtindex; + /* * Override addRangeTableEntry's default ACL_SELECT permissions check, and * instead mark target table as requiring exactly the specified @@ -1084,7 +1088,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *namespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte, rtindex)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1102,7 +1106,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *namespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte, rtindex)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1120,7 +1124,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *namespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte, rtindex)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1138,7 +1142,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *namespace = list_make1(makeDefaultNSItem(rte)); + *namespace = list_make1(makeDefaultNSItem(rte, rtindex)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -1481,6 +1485,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, */ *namespace = lappend(my_namespace, makeNamespaceItem(rte, + j->rtindex, (j->alias != NULL), true, false, @@ -1617,13 +1622,15 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, * Convenience subroutine to construct a ParseNamespaceItem. */ static ParseNamespaceItem * -makeNamespaceItem(RangeTblEntry *rte, bool rel_visible, bool cols_visible, +makeNamespaceItem(RangeTblEntry *rte, int rtindex, + bool rel_visible, bool cols_visible, bool lateral_only, bool lateral_ok) { ParseNamespaceItem *nsitem; nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_rte = rte; + nsitem->p_rtindex = rtindex; nsitem->p_rel_visible = rel_visible; nsitem->p_cols_visible = cols_visible; nsitem->p_lateral_only = lateral_only; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index eb91da2d87..25e92de0b3 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -114,8 +114,9 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); -static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, - int location); +static Node *transformWholeRowRef(ParseState *pstate, + ParseNamespaceItem *nsitem, + int sublevels_up, int location); static Node *transformIndirection(ParseState *pstate, A_Indirection *ind); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); static Node *transformCollateClause(ParseState *pstate, CollateClause *c); @@ -510,7 +511,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) char *nspname = NULL; char *relname = NULL; char *colname = NULL; - RangeTblEntry *rte; + ParseNamespaceItem *nsitem; int levels_up; enum { @@ -653,11 +654,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * PostQUEL-inspired syntax. The preferred form now is * "rel.*". */ - rte = refnameRangeTblEntry(pstate, NULL, colname, - cref->location, - &levels_up); - if (rte) - node = transformWholeRowRef(pstate, rte, + nsitem = refnameNamespaceItem(pstate, NULL, colname, + cref->location, + &levels_up); + if (nsitem) + node = transformWholeRowRef(pstate, nsitem, levels_up, cref->location); } break; @@ -670,11 +671,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) Assert(IsA(field1, String)); relname = strVal(field1); - /* Locate the referenced RTE */ - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); - if (rte == NULL) + /* Locate the referenced nsitem */ + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); + if (nsitem == NULL) { crerr = CRERR_NO_RTE; break; @@ -683,20 +684,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (IsA(field2, A_Star)) { - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); break; } Assert(IsA(field2, String)); colname = strVal(field2); - /* Try to identify as a column of the RTE */ - node = scanRTEForColumn(pstate, rte, colname, cref->location, - 0, NULL); + /* Try to identify as a column of the nsitem */ + node = scanNSItemForColumn(pstate, nsitem, levels_up, colname, + cref->location); if (node == NULL) { /* Try it as a function call on the whole row */ - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), @@ -718,11 +721,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) Assert(IsA(field2, String)); relname = strVal(field2); - /* Locate the referenced RTE */ - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); - if (rte == NULL) + /* Locate the referenced nsitem */ + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); + if (nsitem == NULL) { crerr = CRERR_NO_RTE; break; @@ -731,20 +734,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (IsA(field3, A_Star)) { - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); break; } Assert(IsA(field3, String)); colname = strVal(field3); - /* Try to identify as a column of the RTE */ - node = scanRTEForColumn(pstate, rte, colname, cref->location, - 0, NULL); + /* Try to identify as a column of the nsitem */ + node = scanNSItemForColumn(pstate, nsitem, levels_up, colname, + cref->location); if (node == NULL) { /* Try it as a function call on the whole row */ - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), @@ -779,11 +784,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) break; } - /* Locate the referenced RTE */ - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); - if (rte == NULL) + /* Locate the referenced nsitem */ + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); + if (nsitem == NULL) { crerr = CRERR_NO_RTE; break; @@ -792,20 +797,22 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (IsA(field4, A_Star)) { - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); break; } Assert(IsA(field4, String)); colname = strVal(field4); - /* Try to identify as a column of the RTE */ - node = scanRTEForColumn(pstate, rte, colname, cref->location, - 0, NULL); + /* Try to identify as a column of the nsitem */ + node = scanNSItemForColumn(pstate, nsitem, levels_up, colname, + cref->location); if (node == NULL) { /* Try it as a function call on the whole row */ - node = transformWholeRowRef(pstate, rte, cref->location); + node = transformWholeRowRef(pstate, nsitem, levels_up, + cref->location); node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), @@ -2648,14 +2655,9 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b) static Node * transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) { - int sublevels_up; - /* CURRENT OF can only appear at top level of UPDATE/DELETE */ - Assert(pstate->p_target_rangetblentry != NULL); - cexpr->cvarno = RTERangeTablePosn(pstate, - pstate->p_target_rangetblentry, - &sublevels_up); - Assert(sublevels_up == 0); + Assert(pstate->p_target_rtindex > 0); + cexpr->cvarno = pstate->p_target_rtindex; /* * Check to see if the cursor name matches a parameter of type REFCURSOR. @@ -2703,14 +2705,10 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) * Construct a whole-row reference to represent the notation "relation.*". */ static Node * -transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) +transformWholeRowRef(ParseState *pstate, ParseNamespaceItem *nsitem, + int sublevels_up, int location) { Var *result; - int vnum; - int sublevels_up; - - /* Find the RTE's rangetable location */ - vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); /* * Build the appropriate referencing node. Note that if the RTE is a @@ -2720,13 +2718,14 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) * historically. One argument for it is that "rel" and "rel.*" mean the * same thing for composite relations, so why not for scalar functions... */ - result = makeWholeRowVar(rte, vnum, sublevels_up, true); + result = makeWholeRowVar(nsitem->p_rte, nsitem->p_rtindex, + sublevels_up, true); /* location is not filled in by makeWholeRowVar */ result->location = location; /* mark relation as requiring whole-row SELECT access */ - markVarForSelectPriv(pstate, result, rte); + markVarForSelectPriv(pstate, result, nsitem->p_rte); return (Node *) result; } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index d9c6dc1901..7efea6e24c 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -1913,13 +1913,15 @@ ParseComplexProjection(ParseState *pstate, const char *funcname, Node *first_arg if (IsA(first_arg, Var) && ((Var *) first_arg)->varattno == InvalidAttrNumber) { - RangeTblEntry *rte; + ParseNamespaceItem *nsitem; - rte = GetRTEByRangeTablePosn(pstate, - ((Var *) first_arg)->varno, - ((Var *) first_arg)->varlevelsup); + nsitem = GetNSItemByRangeTablePosn(pstate, + ((Var *) first_arg)->varno, + ((Var *) first_arg)->varlevelsup); /* Return a Var if funcname matches a column, else NULL */ - return scanRTEForColumn(pstate, rte, funcname, location, 0, NULL); + return scanNSItemForColumn(pstate, nsitem, + ((Var *) first_arg)->varlevelsup, + funcname, location); } /* diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index bc832e79dc..3936b60b75 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -180,27 +180,6 @@ pcb_error_callback(void *arg) } -/* - * make_var - * Build a Var node for an attribute identified by RTE and attrno - */ -Var * -make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) -{ - Var *result; - int vnum, - sublevels_up; - Oid vartypeid; - int32 type_mod; - Oid varcollid; - - vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid); - result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up); - result->location = location; - return result; -} - /* * transformContainerType() * Identify the types involved in a subscripting operation for container diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 47188fcd4f..4888311f44 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; + ListCell *lc; - if (sublevels_up) - *sublevels_up = 0; - - 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, } /* - * scanRTEForColumn - * Search the column names of a single RTE for the given name. + * 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 RTE, raise error. + * If the name proves ambiguous within this nsitem, raise error. * - * Side effect: if we find a match, mark the RTE as requiring read access - * for the column. - * - * Additional side effect: if fuzzystate is non-NULL, check non-system columns - * for an approximate match and update fuzzystate accordingly. + * Side effect: if we find a match, mark the item's RTE as requiring read + * access for the column. */ Node * -scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname, - int location, int fuzzy_rte_penalty, +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 the attnum (possibly negative, for a system column); + * else return InvalidAttrNumber. + * If the name proves ambiguous within this RTE, raise error. + * + * 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, + 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, diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 30d419e087..46fe6c6055 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -62,8 +62,9 @@ static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, static List *ExpandAllTables(ParseState *pstate, int location); static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, bool make_target_entry, ParseExprKind exprKind); -static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte, - int location, bool make_target_entry); +static List *ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, + int sublevels_up, int location, + bool make_target_entry); static List *ExpandRowReference(ParseState *pstate, Node *expr, bool make_target_entry); static int FigureColnameInternal(Node *node, char **name); @@ -548,10 +549,13 @@ transformAssignedExpr(ParseState *pstate, /* * Build a Var for the column to be updated. */ - colVar = (Node *) make_var(pstate, - pstate->p_target_rangetblentry, - attrno, - location); + Var *var; + + var = makeVar(pstate->p_target_rtindex, attrno, + attrtype, attrtypmod, attrcollation, 0); + var->location = location; + + colVar = (Node *) var; } expr = (Expr *) @@ -1127,7 +1131,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, */ char *nspname = NULL; char *relname = NULL; - RangeTblEntry *rte = NULL; + ParseNamespaceItem *nsitem = NULL; int levels_up; enum { @@ -1153,16 +1157,16 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, { case 2: relname = strVal(linitial(fields)); - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); break; case 3: nspname = strVal(linitial(fields)); relname = strVal(lsecond(fields)); - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); break; case 4: { @@ -1178,9 +1182,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, } nspname = strVal(lsecond(fields)); relname = strVal(lthird(fields)); - rte = refnameRangeTblEntry(pstate, nspname, relname, - cref->location, - &levels_up); + nsitem = refnameNamespaceItem(pstate, nspname, relname, + cref->location, + &levels_up); break; } default: @@ -1193,17 +1197,19 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * bit by passing the RangeTblEntry, not a Var, as the planned * translation. (A single Var wouldn't be strictly correct anyway. * This convention allows hooks that really care to know what is - * happening.) + * happening. It might be better to pass the nsitem, but we'd have to + * promote that struct to a full-fledged Node type so that callees + * could identify its type.) */ if (pstate->p_post_columnref_hook != NULL) { Node *node; node = pstate->p_post_columnref_hook(pstate, cref, - (Node *) rte); + (Node *) (nsitem ? nsitem->p_rte : NULL)); if (node != NULL) { - if (rte != NULL) + if (nsitem != NULL) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("column reference \"%s\" is ambiguous", @@ -1216,7 +1222,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, /* * Throw error if no translation found. */ - if (rte == NULL) + if (nsitem == NULL) { switch (crserr) { @@ -1242,9 +1248,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, } /* - * OK, expand the RTE into fields. + * OK, expand the nsitem into fields. */ - return ExpandSingleTable(pstate, rte, cref->location, make_target_entry); + return ExpandSingleTable(pstate, nsitem, levels_up, cref->location, + make_target_entry); } } @@ -1269,7 +1276,6 @@ ExpandAllTables(ParseState *pstate, int location) foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); - RangeTblEntry *rte = nsitem->p_rte; /* Ignore table-only items */ if (!nsitem->p_cols_visible) @@ -1280,12 +1286,10 @@ ExpandAllTables(ParseState *pstate, int location) found_table = true; target = list_concat(target, - expandRelAttrs(pstate, - rte, - RTERangeTablePosn(pstate, rte, - NULL), - 0, - location)); + expandNSItemAttrs(pstate, + nsitem, + 0, + location)); } /* @@ -1341,26 +1345,21 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, * The referenced columns are marked as requiring SELECT access. */ static List * -ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte, - int location, bool make_target_entry) +ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, + int sublevels_up, int location, bool make_target_entry) { - int sublevels_up; - int rtindex; - - rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); - if (make_target_entry) { - /* expandRelAttrs handles permissions marking */ - return expandRelAttrs(pstate, rte, rtindex, sublevels_up, - location); + /* expandNSItemAttrs handles permissions marking */ + return expandNSItemAttrs(pstate, nsitem, sublevels_up, location); } else { + RangeTblEntry *rte = nsitem->p_rte; List *vars; ListCell *l; - expandRTE(rte, rtindex, sublevels_up, location, false, + expandRTE(rte, nsitem->p_rtindex, sublevels_up, location, false, NULL, &vars); /* @@ -1411,10 +1410,10 @@ ExpandRowReference(ParseState *pstate, Node *expr, ((Var *) expr)->varattno == InvalidAttrNumber) { Var *var = (Var *) expr; - RangeTblEntry *rte; + ParseNamespaceItem *nsitem; - rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup); - return ExpandSingleTable(pstate, rte, var->location, make_target_entry); + nsitem = GetNSItemByRangeTablePosn(pstate, var->varno, var->varlevelsup); + return ExpandSingleTable(pstate, nsitem, var->varlevelsup, var->location, make_target_entry); } /* diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 7c099e7084..674acc5d3c 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -134,6 +134,8 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * * p_target_rangetblentry: target relation's entry in the rtable list. * + * p_target_rtindex: target relation's index in the rtable list. + * * p_is_insert: true to process assignment expressions like INSERT, false * to process them like UPDATE. (Note this can change intra-statement, for * cases like INSERT ON CONFLICT UPDATE.) @@ -185,7 +187,8 @@ struct ParseState List *p_future_ctes; /* common table exprs not yet in namespace */ CommonTableExpr *p_parent_cte; /* this query's containing CTE */ Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */ - RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */ + RangeTblEntry *p_target_rangetblentry; /* target rel's RTE, or NULL */ + int p_target_rtindex; /* target rel's RT index, or 0 */ bool p_is_insert; /* process assignment like INSERT not UPDATE */ List *p_windowdefs; /* raw representations of window clauses */ ParseExprKind p_expr_kind; /* what kind of expression we're parsing */ @@ -249,6 +252,7 @@ struct ParseState typedef struct ParseNamespaceItem { RangeTblEntry *p_rte; /* The relation's rangetable entry */ + int p_rtindex; /* The relation's index in the rangetable */ bool p_rel_visible; /* Relation name is visible? */ bool p_cols_visible; /* Column names visible as unqualified refs? */ bool p_lateral_only; /* Is only visible to LATERAL expressions? */ @@ -272,8 +276,6 @@ extern void setup_parser_errposition_callback(ParseCallbackState *pcbstate, ParseState *pstate, int location); extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate); -extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, - int location); extern Oid transformContainerType(Oid *containerType, int32 *containerTypmod); extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate, diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index f7e078172d..b09a71ea69 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -17,45 +17,28 @@ #include "parser/parse_node.h" -/* - * Support for fuzzily matching column. - * - * 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; - - -extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate, - const char *schemaname, - const char *refname, - int location, - int *sublevels_up); +extern ParseNamespaceItem *refnameNamespaceItem(ParseState *pstate, + const char *schemaname, + const char *refname, + int location, + int *sublevels_up); extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate, const char *refname, Index *ctelevelsup); extern bool scanNameSpaceForENR(ParseState *pstate, const char *refname); extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1, List *namespace2); -extern int RTERangeTablePosn(ParseState *pstate, - RangeTblEntry *rte, - int *sublevels_up); +extern ParseNamespaceItem *GetNSItemByRangeTablePosn(ParseState *pstate, + int varno, + int sublevels_up); extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate, int varno, int sublevels_up); extern CommonTableExpr *GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup); -extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, - const char *colname, int location, - int fuzzy_rte_penalty, FuzzyAttrMatchState *fuzzystate); +extern Node *scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem, + int sublevels_up, const char *colname, + int location); extern Node *colNameToVar(ParseState *pstate, const char *colname, bool localonly, int location); extern void markVarForSelectPriv(ParseState *pstate, Var *var, @@ -122,8 +105,8 @@ extern void errorMissingColumn(ParseState *pstate, extern void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); -extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, - int rtindex, int sublevels_up, int location); +extern List *expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, + int sublevels_up, int location); extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK); extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid);