From f630157496a70f8ece4fd4c27eeead88c74b9015 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 8 Aug 2012 16:41:04 -0400 Subject: [PATCH] Merge parser's p_relnamespace and p_varnamespace lists into a single list. Now that we are storing structs in these lists, the distinction between the two lists can be represented with a couple of extra flags while using only a single list. This simplifies the code and should save a little bit of palloc traffic, since the majority of RTEs are represented in both lists anyway. --- src/backend/parser/analyze.c | 37 +++--- src/backend/parser/parse_clause.c | 178 +++++++++++++++------------- src/backend/parser/parse_relation.c | 38 ++++-- src/backend/parser/parse_target.c | 52 +++++--- src/include/parser/parse_node.h | 51 +++++--- 5 files changed, 206 insertions(+), 150 deletions(-) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 1a112cd9a4..93ef724fff 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -401,8 +401,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; - List *sub_relnamespace; - List *sub_varnamespace; + List *sub_namespace; List *icolumns; List *attrnos; RangeTblEntry *rte; @@ -454,16 +453,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; - sub_relnamespace = pstate->p_relnamespace; - pstate->p_relnamespace = NIL; - sub_varnamespace = pstate->p_varnamespace; - pstate->p_varnamespace = NIL; + sub_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ - sub_relnamespace = NIL; - sub_varnamespace = NIL; + sub_namespace = NIL; } /* @@ -513,8 +509,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ - sub_pstate->p_relnamespace = sub_relnamespace; - sub_pstate->p_varnamespace = sub_varnamespace; + sub_pstate->p_namespace = sub_namespace; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); @@ -751,8 +746,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ if (stmt->returningList) { - pstate->p_relnamespace = NIL; - pstate->p_varnamespace = NIL; + pstate->p_namespace = NIL; addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); qry->returningList = transformReturningList(pstate, @@ -1305,8 +1299,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) *l; List *targetvars, *targetnames, - *sv_relnamespace, - *sv_varnamespace; + *sv_namespace; int sv_rtable_length; RangeTblEntry *jrte; int tllen; @@ -1433,7 +1426,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * As a first step towards supporting sort clauses that are expressions - * using the output columns, generate a varnamespace entry that makes the + * using the output columns, generate a namespace entry that makes the * output columns visible. A Join RTE node is handy for this, since we * can easily control the Vars generated upon matches. * @@ -1450,12 +1443,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) NULL, false); - sv_relnamespace = pstate->p_relnamespace; - sv_varnamespace = pstate->p_varnamespace; - pstate->p_relnamespace = NIL; - pstate->p_varnamespace = NIL; + sv_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; - /* add jrte to varnamespace only */ + /* add jrte to column namespace only */ addRTEtoQuery(pstate, jrte, false, false, true); /* @@ -1472,9 +1463,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) false /* no unknowns expected */ , false /* allow SQL92 rules */ ); + /* restore namespace, remove jrte from rtable */ + pstate->p_namespace = sv_namespace; pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); - pstate->p_relnamespace = sv_relnamespace; - pstate->p_varnamespace = sv_varnamespace; if (tllen != list_length(qry->targetList)) ereport(ERROR, @@ -1595,7 +1586,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, * because the namespace will be empty, but it could happen if we are * inside a rule. */ - if (pstate->p_relnamespace || pstate->p_varnamespace) + if (pstate->p_namespace) { if (contain_vars_of_level((Node *) selectQuery, 1)) ereport(ERROR, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index f9faa11b2e..d354baf42f 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -38,6 +38,9 @@ #include "utils/rel.h" +/* Convenience macro for the most common makeNamespaceItem() case */ +#define makeDefaultNSItem(rte) makeNamespaceItem(rte, true, true, false, true) + /* clause types for findTargetlistEntrySQL92 */ #define ORDER_CLAUSE 0 #define GROUP_CLAUSE 1 @@ -56,9 +59,7 @@ static Node *transformJoinUsingClause(ParseState *pstate, RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, List *leftVars, List *rightVars); static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, - RangeTblEntry *l_rte, - RangeTblEntry *r_rte, - List *relnamespace); + List *namespace); static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r); static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r, CommonTableExpr *cte, Index levelsup); @@ -68,11 +69,13 @@ static RangeTblEntry *transformRangeFunction(ParseState *pstate, RangeFunction *r); static Node *transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, - List **relnamespace); + List **namespace); static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, + bool rel_visible, bool cols_visible, bool lateral_only, bool lateral_ok); +static void setNamespaceColumnVisibility(List *namespace, bool cols_visible); static void setNamespaceLateralState(List *namespace, bool lateral_only, bool lateral_ok); static void checkExprIsVarFree(ParseState *pstate, Node *n, @@ -97,10 +100,10 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions, /* * transformFromClause - * Process the FROM clause and add items to the query's range table, - * joinlist, and namespaces. + * joinlist, and namespace. * - * Note: we assume that pstate's p_rtable, p_joinlist, p_relnamespace, and - * p_varnamespace lists were initialized to NIL when the pstate was created. + * Note: we assume that the pstate's p_rtable, p_joinlist, and p_namespace + * lists were initialized to NIL when the pstate was created. * We will add onto any entries already present --- this is needed for rule * processing, as well as for UPDATE and DELETE. */ @@ -113,7 +116,7 @@ transformFromClause(ParseState *pstate, List *frmList) * The grammar will have produced a list of RangeVars, RangeSubselects, * RangeFunctions, and/or JoinExprs. Transform each one (possibly adding * entries to the rtable), check for duplicate refnames, and then add it - * to the joinlist and namespaces. + * to the joinlist and namespace. * * Note we must process the items left-to-right for proper handling of * LATERAL references. @@ -123,22 +126,20 @@ transformFromClause(ParseState *pstate, List *frmList) Node *n = lfirst(fl); RangeTblEntry *rte; int rtindex; - List *relnamespace; + List *namespace; n = transformFromClauseItem(pstate, n, &rte, &rtindex, - &relnamespace); - /* Mark the new relnamespace items as visible to LATERAL */ - setNamespaceLateralState(relnamespace, true, true); + &namespace); - checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace); + checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace); + + /* Mark the new namespace items as visible only to LATERAL */ + setNamespaceLateralState(namespace, true, true); pstate->p_joinlist = lappend(pstate->p_joinlist, n); - pstate->p_relnamespace = list_concat(pstate->p_relnamespace, - relnamespace); - pstate->p_varnamespace = lappend(pstate->p_varnamespace, - makeNamespaceItem(rte, true, true)); + pstate->p_namespace = list_concat(pstate->p_namespace, namespace); } /* @@ -147,8 +148,7 @@ transformFromClause(ParseState *pstate, List *frmList) * for any namespace items that were already present when we were called; * but those should have been that way already. */ - setNamespaceLateralState(pstate->p_relnamespace, false, true); - setNamespaceLateralState(pstate->p_varnamespace, false, true); + setNamespaceLateralState(pstate->p_namespace, false, true); } /* @@ -217,7 +217,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, rte->requiredPerms = requiredPerms; /* - * If UPDATE/DELETE, add table to joinlist and namespaces. + * If UPDATE/DELETE, add table to joinlist and namespace. */ if (alsoSource) addRTEtoQuery(pstate, rte, true, true, true); @@ -383,14 +383,10 @@ transformJoinUsingClause(ParseState *pstate, * Result is a transformed qualification expression. */ static Node * -transformJoinOnClause(ParseState *pstate, JoinExpr *j, - RangeTblEntry *l_rte, - RangeTblEntry *r_rte, - List *relnamespace) +transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace) { Node *result; - List *save_relnamespace; - List *save_varnamespace; + List *save_namespace; /* * The namespace that the join expression should see is just the two @@ -400,19 +396,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, * already did.) All namespace items are marked visible regardless of * LATERAL state. */ - save_relnamespace = pstate->p_relnamespace; - save_varnamespace = pstate->p_varnamespace; + setNamespaceLateralState(namespace, false, true); - setNamespaceLateralState(relnamespace, false, true); - pstate->p_relnamespace = relnamespace; - - pstate->p_varnamespace = list_make2(makeNamespaceItem(l_rte, false, true), - makeNamespaceItem(r_rte, false, true)); + save_namespace = pstate->p_namespace; + pstate->p_namespace = namespace; result = transformWhereClause(pstate, j->quals, "JOIN/ON"); - pstate->p_relnamespace = save_relnamespace; - pstate->p_varnamespace = save_varnamespace; + pstate->p_namespace = save_namespace; return result; } @@ -592,7 +583,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the * range table list being built in the ParseState, and return the - * transformed item ready to include in the joinlist and namespaces. + * transformed item ready to include in the joinlist. Also build a + * ParseNamespaceItem list describing the names exposed by this item. * This routine can recurse to handle SQL92 JOIN expressions. * * The function return value is the node to add to the jointree (a @@ -604,17 +596,14 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) * * *top_rti: receives the rangetable index of top_rte. (Ditto.) * - * *relnamespace: receives a List of ParseNamespaceItems for the RTEs exposed - * as relation names by this item. (The lateral_only flags in these items + * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed + * as table/column names by this item. (The lateral_only flags in these items * are indeterminate and should be explicitly set by the caller before use.) - * - * We do not need to pass back an explicit varnamespace value, because - * in all cases the varnamespace contribution is exactly top_rte. */ static Node * transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, - List **relnamespace) + List **namespace) { if (IsA(n, RangeVar)) { @@ -644,7 +633,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(makeNamespaceItem(rte, false, true)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -662,7 +651,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(makeNamespaceItem(rte, false, true)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -680,7 +669,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(makeNamespaceItem(rte, false, true)); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -693,9 +682,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry *r_rte; int l_rtindex; int r_rtindex; - List *l_relnamespace, - *r_relnamespace, - *my_relnamespace, + List *l_namespace, + *r_namespace, + *my_namespace, *l_colnames, *r_colnames, *res_colnames, @@ -703,8 +692,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, *r_colvars, *res_colvars; bool lateral_ok; - int sv_relnamespace_length, - sv_varnamespace_length; + int sv_namespace_length; RangeTblEntry *rte; int k; @@ -715,53 +703,49 @@ transformFromClauseItem(ParseState *pstate, Node *n, j->larg = transformFromClauseItem(pstate, j->larg, &l_rte, &l_rtindex, - &l_relnamespace); + &l_namespace); /* * Make the left-side RTEs available for LATERAL access within the * right side, by temporarily adding them to the pstate's namespace - * lists. Per SQL:2008, if the join type is not INNER or LEFT then + * list. Per SQL:2008, if the join type is not INNER or LEFT then * the left-side names must still be exposed, but it's an error to * reference them. (Stupid design, but that's what it says.) Hence, - * we always push them into the namespaces, but mark them as not + * we always push them into the namespace, but mark them as not * lateral_ok if the jointype is wrong. * * NB: this coding relies on the fact that list_concat is not * destructive to its second argument. */ lateral_ok = (j->jointype == JOIN_INNER || j->jointype == JOIN_LEFT); - setNamespaceLateralState(l_relnamespace, true, lateral_ok); - checkNameSpaceConflicts(pstate, pstate->p_relnamespace, l_relnamespace); - sv_relnamespace_length = list_length(pstate->p_relnamespace); - pstate->p_relnamespace = list_concat(pstate->p_relnamespace, - l_relnamespace); - sv_varnamespace_length = list_length(pstate->p_varnamespace); - pstate->p_varnamespace = lappend(pstate->p_varnamespace, - makeNamespaceItem(l_rte, true, lateral_ok)); + setNamespaceLateralState(l_namespace, true, lateral_ok); + + checkNameSpaceConflicts(pstate, pstate->p_namespace, l_namespace); + + sv_namespace_length = list_length(pstate->p_namespace); + pstate->p_namespace = list_concat(pstate->p_namespace, l_namespace); /* And now we can process the RHS */ j->rarg = transformFromClauseItem(pstate, j->rarg, &r_rte, &r_rtindex, - &r_relnamespace); + &r_namespace); - /* Remove the left-side RTEs from the namespace lists again */ - pstate->p_relnamespace = list_truncate(pstate->p_relnamespace, - sv_relnamespace_length); - pstate->p_varnamespace = list_truncate(pstate->p_varnamespace, - sv_varnamespace_length); + /* Remove the left-side RTEs from the namespace list again */ + pstate->p_namespace = list_truncate(pstate->p_namespace, + sv_namespace_length); /* * Check for conflicting refnames in left and right subtrees. Must do * this because higher levels will assume I hand back a self- * consistent namespace list. */ - checkNameSpaceConflicts(pstate, l_relnamespace, r_relnamespace); + checkNameSpaceConflicts(pstate, l_namespace, r_namespace); /* - * Generate combined relnamespace info for possible use below. + * Generate combined namespace info for possible use below. */ - my_relnamespace = list_concat(l_relnamespace, r_relnamespace); + my_namespace = list_concat(l_namespace, r_namespace); /* * Extract column name and var lists from both subtrees @@ -924,9 +908,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, else if (j->quals) { /* User-written ON-condition; transform it */ - j->quals = transformJoinOnClause(pstate, j, - l_rte, r_rte, - my_relnamespace); + j->quals = transformJoinOnClause(pstate, j, my_namespace); } else { @@ -985,14 +967,30 @@ transformFromClauseItem(ParseState *pstate, Node *n, /* * Prepare returned namespace list. If the JOIN has an alias then it - * hides the contained RTEs as far as the relnamespace goes; - * otherwise, put the contained RTEs and *not* the JOIN into - * relnamespace. + * hides the contained RTEs completely; otherwise, the contained RTEs + * are still visible as table names, but are not visible for + * unqualified column-name access. + * + * Note: if there are nested alias-less JOINs, the lower-level ones + * will remain in the list although they have neither p_rel_visible + * nor p_cols_visible set. We could delete such list items, but it's + * unclear that it's worth expending cycles to do so. */ - if (j->alias) - *relnamespace = list_make1(makeNamespaceItem(rte, false, true)); + if (j->alias != NULL) + my_namespace = NIL; else - *relnamespace = my_relnamespace; + setNamespaceColumnVisibility(my_namespace, false); + + /* + * The join RTE itself is always made visible for unqualified column + * names. It's visible as a relation name only if it has an alias. + */ + *namespace = lappend(my_namespace, + makeNamespaceItem(rte, + (j->alias != NULL), + true, + false, + true)); return (Node *) j; } @@ -1125,17 +1123,37 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, * Convenience subroutine to construct a ParseNamespaceItem. */ static ParseNamespaceItem * -makeNamespaceItem(RangeTblEntry *rte, bool lateral_only, bool lateral_ok) +makeNamespaceItem(RangeTblEntry *rte, 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_rel_visible = rel_visible; + nsitem->p_cols_visible = cols_visible; nsitem->p_lateral_only = lateral_only; nsitem->p_lateral_ok = lateral_ok; return nsitem; } +/* + * setNamespaceColumnVisibility - + * Convenience subroutine to update cols_visible flags in a namespace list. + */ +static void +setNamespaceColumnVisibility(List *namespace, bool cols_visible) +{ + ListCell *lc; + + foreach(lc, namespace) + { + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc); + + nsitem->p_cols_visible = cols_visible; + } +} + /* * setNamespaceLateralState - * Convenience subroutine to update LATERAL flags in a namespace list. diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 47686c8719..98ebc400d5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -135,11 +135,14 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) RangeTblEntry *result = NULL; ListCell *l; - foreach(l, pstate->p_relnamespace) + foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); RangeTblEntry *rte = nsitem->p_rte; + /* Ignore columns-only items */ + if (!nsitem->p_rel_visible) + continue; /* If not inside LATERAL, ignore lateral-only items */ if (nsitem->p_lateral_only && !pstate->p_lateral_active) continue; @@ -181,11 +184,14 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location) RangeTblEntry *result = NULL; ListCell *l; - foreach(l, pstate->p_relnamespace) + foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); RangeTblEntry *rte = nsitem->p_rte; + /* Ignore columns-only items */ + if (!nsitem->p_rel_visible) + continue; /* If not inside LATERAL, ignore lateral-only items */ if (nsitem->p_lateral_only && !pstate->p_lateral_active) continue; @@ -277,7 +283,7 @@ isFutureCTE(ParseState *pstate, const char *refname) * * This is different from refnameRangeTblEntry in that it considers every * entry in the ParseState's rangetable(s), not only those that are currently - * visible in the p_relnamespace lists. This behavior is invalid per the SQL + * visible in the p_namespace list(s). This behavior is invalid per the SQL * spec, and it may give ambiguous results (there might be multiple equally * valid matches, but only one will be returned). This must be used ONLY * as a heuristic in giving suitable error messages. See errorMissingRTE. @@ -339,7 +345,7 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) } /* - * Check for relation-name conflicts between two relnamespace lists. + * Check for relation-name conflicts between two namespace lists. * Raise an error if any is found. * * Note: we assume that each given argument does not contain conflicts @@ -350,7 +356,8 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) * are for different relation OIDs (implying they are in different schemas). * * We ignore the lateral-only flags in the namespace items: the lists must - * not conflict, even when all items are considered visible. + * not conflict, even when all items are considered visible. However, + * columns-only items should be ignored. */ void checkNameSpaceConflicts(ParseState *pstate, List *namespace1, @@ -365,11 +372,16 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1, const char *aliasname1 = rte1->eref->aliasname; ListCell *l2; + if (!nsitem1->p_rel_visible) + continue; + foreach(l2, namespace2) { ParseNamespaceItem *nsitem2 = (ParseNamespaceItem *) lfirst(l2); RangeTblEntry *rte2 = nsitem2->p_rte; + if (!nsitem2->p_rel_visible) + continue; if (strcmp(rte2->eref->aliasname, aliasname1) != 0) continue; /* definitely no conflict */ if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL && @@ -573,12 +585,15 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, { ListCell *l; - foreach(l, pstate->p_varnamespace) + foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); RangeTblEntry *rte = nsitem->p_rte; Node *newresult; + /* Ignore table-only items */ + if (!nsitem->p_cols_visible) + continue; /* If not inside LATERAL, ignore lateral-only items */ if (nsitem->p_lateral_only && !pstate->p_lateral_active) continue; @@ -622,7 +637,7 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, * * This is different from colNameToVar in that it considers every entry in * the ParseState's rangetable(s), not only those that are currently visible - * in the p_varnamespace lists. This behavior is invalid per the SQL spec, + * in the p_namespace list(s). This behavior is invalid per the SQL spec, * and it may give ambiguous results (there might be multiple equally valid * matches, but only one will be returned). This must be used ONLY as a * heuristic in giving suitable error messages. See errorMissingColumn. @@ -1577,7 +1592,7 @@ isLockedRefname(ParseState *pstate, const char *refname) /* * Add the given RTE as a top-level entry in the pstate's join list - * and/or name space lists. (We assume caller has checked for any + * and/or namespace list. (We assume caller has checked for any * namespace conflicts.) The RTE is always marked as unconditionally * visible, that is, not LATERAL-only. */ @@ -1600,12 +1615,11 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_rte = rte; + nsitem->p_rel_visible = addToRelNameSpace; + nsitem->p_cols_visible = addToVarNameSpace; nsitem->p_lateral_only = false; nsitem->p_lateral_ok = true; - if (addToRelNameSpace) - pstate->p_relnamespace = lappend(pstate->p_relnamespace, nsitem); - if (addToVarNameSpace) - pstate->p_varnamespace = lappend(pstate->p_varnamespace, nsitem); + pstate->p_namespace = lappend(pstate->p_namespace, nsitem); } } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 4d9e6e6106..ccd97fc845 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1108,9 +1108,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * ExpandAllTables() * Transforms '*' (in the target list) into a list of targetlist entries. * - * tlist entries are generated for each relation appearing in the query's - * varnamespace. We do not consider relnamespace because that would include - * input tables of aliasless JOINs, NEW/OLD pseudo-entries, etc. + * tlist entries are generated for each relation visible for unqualified + * column name access. We do not consider qualified-name-only entries because + * that would include input tables of aliasless JOINs, NEW/OLD pseudo-entries, + * etc. * * The referenced relations/columns are marked as requiring SELECT access. */ @@ -1118,29 +1119,42 @@ static List * ExpandAllTables(ParseState *pstate, int location) { List *target = NIL; + bool found_table = false; ListCell *l; - /* Check for SELECT *; */ - if (!pstate->p_varnamespace) + foreach(l, pstate->p_namespace) + { + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); + RangeTblEntry *rte = nsitem->p_rte; + + /* Ignore table-only items */ + if (!nsitem->p_cols_visible) + continue; + /* Should not have any lateral-only items when parsing targetlist */ + Assert(!nsitem->p_lateral_only); + /* Remember we found a p_cols_visible item */ + found_table = true; + + target = list_concat(target, + expandRelAttrs(pstate, + rte, + RTERangeTablePosn(pstate, rte, + NULL), + 0, + location)); + } + + /* + * Check for "SELECT *;". We do it this way, rather than checking for + * target == NIL, because we want to allow SELECT * FROM a zero_column + * table. + */ + if (!found_table) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SELECT * with no tables specified is not valid"), parser_errposition(pstate, location))); - foreach(l, pstate->p_varnamespace) - { - ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); - RangeTblEntry *rte = nsitem->p_rte; - int rtindex = RTERangeTablePosn(pstate, rte, NULL); - - /* Should not have any lateral-only items when parsing targetlist */ - Assert(!nsitem->p_lateral_only); - - target = list_concat(target, - expandRelAttrs(pstate, rte, rtindex, 0, - location)); - } - return target; } diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 13f745f6fa..200b9744e5 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -54,25 +54,17 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * will become the fromlist of the query's top-level FromExpr node. * - * p_relnamespace: list of ParseNamespaceItems that represents the current - * namespace for table lookup, ie, those RTEs that are accessible by - * qualified names. (This may be just a subset of the whole rtable.) - * - * p_varnamespace: list of ParseNamespaceItems that represents the current - * namespace for column lookup, ie, those RTEs that are accessible by - * unqualified names. This is different from p_relnamespace because a JOIN - * without an alias does not hide the contained tables (so they must be in - * p_relnamespace) but it does hide their columns (unqualified references to - * the columns must refer to the JOIN, not the member tables). Other special - * RTEs such as NEW/OLD for rules may also appear in just one of these lists. + * p_namespace: list of ParseNamespaceItems that represents the current + * namespace for table and column lookup. (The RTEs listed here may be just + * a subset of the whole rtable. See ParseNamespaceItem comments below.) * * p_lateral_active: TRUE if we are currently parsing a LATERAL subexpression * of this parse level. This makes p_lateral_only namespace items visible, * whereas they are not visible when p_lateral_active is FALSE. * * p_ctenamespace: list of CommonTableExprs (WITH items) that are visible - * at the moment. This is entirely different from p_relnamespace because - * a CTE is not an RTE, rather "visibility" means you could make an RTE. + * at the moment. This is entirely different from p_namespace because a CTE + * is not an RTE, rather "visibility" means you could make an RTE from it. * * p_future_ctes: list of CommonTableExprs (WITH items) that are not yet * visible due to scope rules. This is used to help improve error messages. @@ -94,8 +86,8 @@ struct ParseState List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ - List *p_relnamespace; /* current namespace for relations */ - List *p_varnamespace; /* current namespace for columns */ + List *p_namespace; /* currently-referenceable RTEs (List of + * ParseNamespaceItem) */ bool p_lateral_active; /* p_lateral_only items visible? */ List *p_ctenamespace; /* current namespace for common table exprs */ List *p_future_ctes; /* common table exprs not yet in namespace */ @@ -125,10 +117,37 @@ struct ParseState void *p_ref_hook_state; /* common passthrough link for above */ }; -/* An element of p_relnamespace or p_varnamespace */ +/* + * An element of a namespace list. + * + * Namespace items with p_rel_visible set define which RTEs are accessible by + * qualified names, while those with p_cols_visible set define which RTEs are + * accessible by unqualified names. These sets are different because a JOIN + * without an alias does not hide the contained tables (so they must be + * visible for qualified references) but it does hide their columns + * (unqualified references to the columns refer to the JOIN, not the member + * tables, so we must not complain that such a reference is ambiguous). + * Various special RTEs such as NEW/OLD for rules may also appear with only + * one flag set. + * + * While processing the FROM clause, namespace items may appear with + * p_lateral_only set, meaning they are visible only to LATERAL + * subexpressions. (The pstate's p_lateral_active flag tells whether we are + * inside such a subexpression at the moment.) If p_lateral_ok is not set, + * it's an error to actually use such a namespace item. One might think it + * would be better to just exclude such items from visibility, but the wording + * of SQL:2008 requires us to do it this way. + * + * At no time should a namespace list contain two entries that conflict + * according to the rules in checkNameSpaceConflicts; but note that those + * are more complicated than "must have different alias names", so in practice + * code searching a namespace list has to check for ambiguous references. + */ typedef struct ParseNamespaceItem { RangeTblEntry *p_rte; /* The relation's rangetable entry */ + 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? */ bool p_lateral_ok; /* If so, does join type allow use? */ } ParseNamespaceItem;