int sublist_length = -1;
bool lateral = false;
RangeTblEntry *rte;
- int rtindex;
+ ParseNamespaceItem *nsitem;
ListCell *lc;
ListCell *lc2;
int i;
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
#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,
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);
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
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;
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;
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;
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;
*/
*namespace = lappend(my_namespace,
makeNamespaceItem(rte,
+ j->rtindex,
(j->alias != NULL),
true,
false,
* 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;
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);
char *nspname = NULL;
char *relname = NULL;
char *colname = NULL;
- RangeTblEntry *rte;
+ ParseNamespaceItem *nsitem;
int levels_up;
enum
{
* 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;
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;
/* 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),
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;
/* 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),
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;
/* 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),
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.
* 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
* 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;
}
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);
}
/*
}
-/*
- * 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
#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,
/*
- * 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,
while (pstate != NULL)
{
- RangeTblEntry *result;
+ ParseNamespaceItem *result;
if (OidIsValid(relId))
result = scanNameSpaceForRelid(pstate, relId, location);
}
/*
- * 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
* 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)
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)
relid),
parser_errposition(pstate, location)));
check_lateral_ref_ok(pstate, nsitem, location);
- result = rte;
+ result = nsitem;
}
}
return result;
* 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
* 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
}
/*
- * 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,
* 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)
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)
}
}
+/*
+ * 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;
/*
*
* 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)
{
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);
{
/* 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;
}
}
int location)
{
Node *result = NULL;
+ int sublevels_up = 0;
ParseState *orig_pstate = pstate;
while (pstate != NULL)
foreach(l, pstate->p_namespace)
{
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
- RangeTblEntry *rte = nsitem->p_rte;
Node *newresult;
/* Ignore table-only items */
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)
{
break; /* found, or don't want to look at parent */
pstate = pstate->parentParseState;
+ sublevels_up++;
}
return result;
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;
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;
}
/*
- * 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);
/*
errorMissingRTE(ParseState *pstate, RangeVar *relation)
{
RangeTblEntry *rte;
- int sublevels_up;
const char *badAlias = NULL;
/*
* 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,
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);
/*
* 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 *)
*/
char *nspname = NULL;
char *relname = NULL;
- RangeTblEntry *rte = NULL;
+ ParseNamespaceItem *nsitem = NULL;
int levels_up;
enum
{
{
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:
{
}
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:
* 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",
/*
* Throw error if no translation found.
*/
- if (rte == NULL)
+ if (nsitem == NULL)
{
switch (crserr)
{
}
/*
- * 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);
}
}
foreach(l, pstate->p_namespace)
{
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
- RangeTblEntry *rte = nsitem->p_rte;
/* Ignore table-only items */
if (!nsitem->p_cols_visible)
found_table = true;
target = list_concat(target,
- expandRelAttrs(pstate,
- rte,
- RTERangeTablePosn(pstate, rte,
- NULL),
- 0,
- location));
+ expandNSItemAttrs(pstate,
+ nsitem,
+ 0,
+ location));
}
/*
* 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);
/*
((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);
}
/*
*
* 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.)
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 */
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? */
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,
#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,
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);