Commit f6301574 authored by Tom Lane's avatar Tom Lane

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.
parent 8143a568
......@@ -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,
......
This diff is collapsed.
......@@ -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);
}
}
......
......@@ -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)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("SELECT * with no tables specified is not valid"),
parser_errposition(pstate, location)));
foreach(l, pstate->p_varnamespace)
foreach(l, pstate->p_namespace)
{
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
RangeTblEntry *rte = nsitem->p_rte;
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
/* 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, rtindex, 0,
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)));
return target;
}
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment