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) ...@@ -401,8 +401,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
List *exprList = NIL; List *exprList = NIL;
bool isGeneralSelect; bool isGeneralSelect;
List *sub_rtable; List *sub_rtable;
List *sub_relnamespace; List *sub_namespace;
List *sub_varnamespace;
List *icolumns; List *icolumns;
List *attrnos; List *attrnos;
RangeTblEntry *rte; RangeTblEntry *rte;
...@@ -454,16 +453,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -454,16 +453,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{ {
sub_rtable = pstate->p_rtable; sub_rtable = pstate->p_rtable;
pstate->p_rtable = NIL; pstate->p_rtable = NIL;
sub_relnamespace = pstate->p_relnamespace; sub_namespace = pstate->p_namespace;
pstate->p_relnamespace = NIL; pstate->p_namespace = NIL;
sub_varnamespace = pstate->p_varnamespace;
pstate->p_varnamespace = NIL;
} }
else else
{ {
sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_rtable = NIL; /* not used, but keep compiler quiet */
sub_relnamespace = NIL; sub_namespace = NIL;
sub_varnamespace = NIL;
} }
/* /*
...@@ -513,8 +509,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -513,8 +509,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*/ */
sub_pstate->p_rtable = sub_rtable; sub_pstate->p_rtable = sub_rtable;
sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
sub_pstate->p_relnamespace = sub_relnamespace; sub_pstate->p_namespace = sub_namespace;
sub_pstate->p_varnamespace = sub_varnamespace;
selectQuery = transformStmt(sub_pstate, stmt->selectStmt); selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
...@@ -751,8 +746,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -751,8 +746,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
*/ */
if (stmt->returningList) if (stmt->returningList)
{ {
pstate->p_relnamespace = NIL; pstate->p_namespace = NIL;
pstate->p_varnamespace = NIL;
addRTEtoQuery(pstate, pstate->p_target_rangetblentry, addRTEtoQuery(pstate, pstate->p_target_rangetblentry,
false, true, true); false, true, true);
qry->returningList = transformReturningList(pstate, qry->returningList = transformReturningList(pstate,
...@@ -1305,8 +1299,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1305,8 +1299,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
*l; *l;
List *targetvars, List *targetvars,
*targetnames, *targetnames,
*sv_relnamespace, *sv_namespace;
*sv_varnamespace;
int sv_rtable_length; int sv_rtable_length;
RangeTblEntry *jrte; RangeTblEntry *jrte;
int tllen; int tllen;
...@@ -1433,7 +1426,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1433,7 +1426,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/* /*
* As a first step towards supporting sort clauses that are expressions * 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 * output columns visible. A Join RTE node is handy for this, since we
* can easily control the Vars generated upon matches. * can easily control the Vars generated upon matches.
* *
...@@ -1450,12 +1443,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1450,12 +1443,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
NULL, NULL,
false); false);
sv_relnamespace = pstate->p_relnamespace; sv_namespace = pstate->p_namespace;
sv_varnamespace = pstate->p_varnamespace; pstate->p_namespace = NIL;
pstate->p_relnamespace = NIL;
pstate->p_varnamespace = NIL;
/* add jrte to varnamespace only */ /* add jrte to column namespace only */
addRTEtoQuery(pstate, jrte, false, false, true); addRTEtoQuery(pstate, jrte, false, false, true);
/* /*
...@@ -1472,9 +1463,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1472,9 +1463,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
false /* no unknowns expected */ , false /* no unknowns expected */ ,
false /* allow SQL92 rules */ ); 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_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)) if (tllen != list_length(qry->targetList))
ereport(ERROR, ereport(ERROR,
...@@ -1595,7 +1586,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, ...@@ -1595,7 +1586,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
* because the namespace will be empty, but it could happen if we are * because the namespace will be empty, but it could happen if we are
* inside a rule. * inside a rule.
*/ */
if (pstate->p_relnamespace || pstate->p_varnamespace) if (pstate->p_namespace)
{ {
if (contain_vars_of_level((Node *) selectQuery, 1)) if (contain_vars_of_level((Node *) selectQuery, 1))
ereport(ERROR, ereport(ERROR,
......
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
#include "utils/rel.h" #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 */ /* clause types for findTargetlistEntrySQL92 */
#define ORDER_CLAUSE 0 #define ORDER_CLAUSE 0
#define GROUP_CLAUSE 1 #define GROUP_CLAUSE 1
...@@ -56,9 +59,7 @@ static Node *transformJoinUsingClause(ParseState *pstate, ...@@ -56,9 +59,7 @@ static Node *transformJoinUsingClause(ParseState *pstate,
RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, RangeTblEntry *leftRTE, RangeTblEntry *rightRTE,
List *leftVars, List *rightVars); List *leftVars, List *rightVars);
static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
RangeTblEntry *l_rte, List *namespace);
RangeTblEntry *r_rte,
List *relnamespace);
static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r); static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r, static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r,
CommonTableExpr *cte, Index levelsup); CommonTableExpr *cte, Index levelsup);
...@@ -68,11 +69,13 @@ static RangeTblEntry *transformRangeFunction(ParseState *pstate, ...@@ -68,11 +69,13 @@ static RangeTblEntry *transformRangeFunction(ParseState *pstate,
RangeFunction *r); RangeFunction *r);
static Node *transformFromClauseItem(ParseState *pstate, Node *n, static Node *transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry **top_rte, int *top_rti, RangeTblEntry **top_rte, int *top_rti,
List **relnamespace); List **namespace);
static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
Var *l_colvar, Var *r_colvar); Var *l_colvar, Var *r_colvar);
static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte,
bool rel_visible, bool cols_visible,
bool lateral_only, bool lateral_ok); bool lateral_only, bool lateral_ok);
static void setNamespaceColumnVisibility(List *namespace, bool cols_visible);
static void setNamespaceLateralState(List *namespace, static void setNamespaceLateralState(List *namespace,
bool lateral_only, bool lateral_ok); bool lateral_only, bool lateral_ok);
static void checkExprIsVarFree(ParseState *pstate, Node *n, static void checkExprIsVarFree(ParseState *pstate, Node *n,
...@@ -97,10 +100,10 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions, ...@@ -97,10 +100,10 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions,
/* /*
* transformFromClause - * transformFromClause -
* Process the FROM clause and add items to the query's range table, * 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 * Note: we assume that the pstate's p_rtable, p_joinlist, and p_namespace
* p_varnamespace lists were initialized to NIL when the pstate was created. * lists were initialized to NIL when the pstate was created.
* We will add onto any entries already present --- this is needed for rule * We will add onto any entries already present --- this is needed for rule
* processing, as well as for UPDATE and DELETE. * processing, as well as for UPDATE and DELETE.
*/ */
...@@ -113,7 +116,7 @@ transformFromClause(ParseState *pstate, List *frmList) ...@@ -113,7 +116,7 @@ transformFromClause(ParseState *pstate, List *frmList)
* The grammar will have produced a list of RangeVars, RangeSubselects, * The grammar will have produced a list of RangeVars, RangeSubselects,
* RangeFunctions, and/or JoinExprs. Transform each one (possibly adding * RangeFunctions, and/or JoinExprs. Transform each one (possibly adding
* entries to the rtable), check for duplicate refnames, and then add it * 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 * Note we must process the items left-to-right for proper handling of
* LATERAL references. * LATERAL references.
...@@ -123,22 +126,20 @@ transformFromClause(ParseState *pstate, List *frmList) ...@@ -123,22 +126,20 @@ transformFromClause(ParseState *pstate, List *frmList)
Node *n = lfirst(fl); Node *n = lfirst(fl);
RangeTblEntry *rte; RangeTblEntry *rte;
int rtindex; int rtindex;
List *relnamespace; List *namespace;
n = transformFromClauseItem(pstate, n, n = transformFromClauseItem(pstate, n,
&rte, &rte,
&rtindex, &rtindex,
&relnamespace); &namespace);
/* Mark the new relnamespace items as visible to LATERAL */
setNamespaceLateralState(relnamespace, true, true); checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace);
checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace); /* Mark the new namespace items as visible only to LATERAL */
setNamespaceLateralState(namespace, true, true);
pstate->p_joinlist = lappend(pstate->p_joinlist, n); pstate->p_joinlist = lappend(pstate->p_joinlist, n);
pstate->p_relnamespace = list_concat(pstate->p_relnamespace, pstate->p_namespace = list_concat(pstate->p_namespace, namespace);
relnamespace);
pstate->p_varnamespace = lappend(pstate->p_varnamespace,
makeNamespaceItem(rte, true, true));
} }
/* /*
...@@ -147,8 +148,7 @@ transformFromClause(ParseState *pstate, List *frmList) ...@@ -147,8 +148,7 @@ transformFromClause(ParseState *pstate, List *frmList)
* for any namespace items that were already present when we were called; * for any namespace items that were already present when we were called;
* but those should have been that way already. * but those should have been that way already.
*/ */
setNamespaceLateralState(pstate->p_relnamespace, false, true); setNamespaceLateralState(pstate->p_namespace, false, true);
setNamespaceLateralState(pstate->p_varnamespace, false, true);
} }
/* /*
...@@ -217,7 +217,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, ...@@ -217,7 +217,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
rte->requiredPerms = requiredPerms; rte->requiredPerms = requiredPerms;
/* /*
* If UPDATE/DELETE, add table to joinlist and namespaces. * If UPDATE/DELETE, add table to joinlist and namespace.
*/ */
if (alsoSource) if (alsoSource)
addRTEtoQuery(pstate, rte, true, true, true); addRTEtoQuery(pstate, rte, true, true, true);
...@@ -383,14 +383,10 @@ transformJoinUsingClause(ParseState *pstate, ...@@ -383,14 +383,10 @@ transformJoinUsingClause(ParseState *pstate,
* Result is a transformed qualification expression. * Result is a transformed qualification expression.
*/ */
static Node * static Node *
transformJoinOnClause(ParseState *pstate, JoinExpr *j, transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace)
RangeTblEntry *l_rte,
RangeTblEntry *r_rte,
List *relnamespace)
{ {
Node *result; Node *result;
List *save_relnamespace; List *save_namespace;
List *save_varnamespace;
/* /*
* The namespace that the join expression should see is just the two * The namespace that the join expression should see is just the two
...@@ -400,19 +396,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -400,19 +396,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
* already did.) All namespace items are marked visible regardless of * already did.) All namespace items are marked visible regardless of
* LATERAL state. * LATERAL state.
*/ */
save_relnamespace = pstate->p_relnamespace; setNamespaceLateralState(namespace, false, true);
save_varnamespace = pstate->p_varnamespace;
setNamespaceLateralState(relnamespace, false, true);
pstate->p_relnamespace = relnamespace;
pstate->p_varnamespace = list_make2(makeNamespaceItem(l_rte, false, true), save_namespace = pstate->p_namespace;
makeNamespaceItem(r_rte, false, true)); pstate->p_namespace = namespace;
result = transformWhereClause(pstate, j->quals, "JOIN/ON"); result = transformWhereClause(pstate, j->quals, "JOIN/ON");
pstate->p_relnamespace = save_relnamespace; pstate->p_namespace = save_namespace;
pstate->p_varnamespace = save_varnamespace;
return result; return result;
} }
...@@ -592,7 +583,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -592,7 +583,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* transformFromClauseItem - * transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the * Transform a FROM-clause item, adding any required entries to the
* range table list being built in the ParseState, and return 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. * This routine can recurse to handle SQL92 JOIN expressions.
* *
* The function return value is the node to add to the jointree (a * The function return value is the node to add to the jointree (a
...@@ -604,17 +596,14 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -604,17 +596,14 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
* *
* *top_rti: receives the rangetable index of top_rte. (Ditto.) * *top_rti: receives the rangetable index of top_rte. (Ditto.)
* *
* *relnamespace: receives a List of ParseNamespaceItems for the RTEs exposed * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed
* as relation names by this item. (The lateral_only flags in these items * 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.) * 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 * static Node *
transformFromClauseItem(ParseState *pstate, Node *n, transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry **top_rte, int *top_rti, RangeTblEntry **top_rte, int *top_rti,
List **relnamespace) List **namespace)
{ {
if (IsA(n, RangeVar)) if (IsA(n, RangeVar))
{ {
...@@ -644,7 +633,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -644,7 +633,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte; *top_rte = rte;
*top_rti = rtindex; *top_rti = rtindex;
*relnamespace = list_make1(makeNamespaceItem(rte, false, true)); *namespace = list_make1(makeDefaultNSItem(rte));
rtr = makeNode(RangeTblRef); rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex; rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
...@@ -662,7 +651,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -662,7 +651,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte; *top_rte = rte;
*top_rti = rtindex; *top_rti = rtindex;
*relnamespace = list_make1(makeNamespaceItem(rte, false, true)); *namespace = list_make1(makeDefaultNSItem(rte));
rtr = makeNode(RangeTblRef); rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex; rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
...@@ -680,7 +669,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -680,7 +669,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*top_rte = rte; *top_rte = rte;
*top_rti = rtindex; *top_rti = rtindex;
*relnamespace = list_make1(makeNamespaceItem(rte, false, true)); *namespace = list_make1(makeDefaultNSItem(rte));
rtr = makeNode(RangeTblRef); rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex; rtr->rtindex = rtindex;
return (Node *) rtr; return (Node *) rtr;
...@@ -693,9 +682,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -693,9 +682,9 @@ transformFromClauseItem(ParseState *pstate, Node *n,
RangeTblEntry *r_rte; RangeTblEntry *r_rte;
int l_rtindex; int l_rtindex;
int r_rtindex; int r_rtindex;
List *l_relnamespace, List *l_namespace,
*r_relnamespace, *r_namespace,
*my_relnamespace, *my_namespace,
*l_colnames, *l_colnames,
*r_colnames, *r_colnames,
*res_colnames, *res_colnames,
...@@ -703,8 +692,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -703,8 +692,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
*r_colvars, *r_colvars,
*res_colvars; *res_colvars;
bool lateral_ok; bool lateral_ok;
int sv_relnamespace_length, int sv_namespace_length;
sv_varnamespace_length;
RangeTblEntry *rte; RangeTblEntry *rte;
int k; int k;
...@@ -715,53 +703,49 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -715,53 +703,49 @@ transformFromClauseItem(ParseState *pstate, Node *n,
j->larg = transformFromClauseItem(pstate, j->larg, j->larg = transformFromClauseItem(pstate, j->larg,
&l_rte, &l_rte,
&l_rtindex, &l_rtindex,
&l_relnamespace); &l_namespace);
/* /*
* Make the left-side RTEs available for LATERAL access within the * Make the left-side RTEs available for LATERAL access within the
* right side, by temporarily adding them to the pstate's namespace * 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 * 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, * 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. * lateral_ok if the jointype is wrong.
* *
* NB: this coding relies on the fact that list_concat is not * NB: this coding relies on the fact that list_concat is not
* destructive to its second argument. * destructive to its second argument.
*/ */
lateral_ok = (j->jointype == JOIN_INNER || j->jointype == JOIN_LEFT); lateral_ok = (j->jointype == JOIN_INNER || j->jointype == JOIN_LEFT);
setNamespaceLateralState(l_relnamespace, true, lateral_ok); setNamespaceLateralState(l_namespace, true, lateral_ok);
checkNameSpaceConflicts(pstate, pstate->p_relnamespace, l_relnamespace);
sv_relnamespace_length = list_length(pstate->p_relnamespace); checkNameSpaceConflicts(pstate, pstate->p_namespace, l_namespace);
pstate->p_relnamespace = list_concat(pstate->p_relnamespace,
l_relnamespace); sv_namespace_length = list_length(pstate->p_namespace);
sv_varnamespace_length = list_length(pstate->p_varnamespace); pstate->p_namespace = list_concat(pstate->p_namespace, l_namespace);
pstate->p_varnamespace = lappend(pstate->p_varnamespace,
makeNamespaceItem(l_rte, true, lateral_ok));
/* And now we can process the RHS */ /* And now we can process the RHS */
j->rarg = transformFromClauseItem(pstate, j->rarg, j->rarg = transformFromClauseItem(pstate, j->rarg,
&r_rte, &r_rte,
&r_rtindex, &r_rtindex,
&r_relnamespace); &r_namespace);
/* Remove the left-side RTEs from the namespace lists again */ /* Remove the left-side RTEs from the namespace list again */
pstate->p_relnamespace = list_truncate(pstate->p_relnamespace, pstate->p_namespace = list_truncate(pstate->p_namespace,
sv_relnamespace_length); sv_namespace_length);
pstate->p_varnamespace = list_truncate(pstate->p_varnamespace,
sv_varnamespace_length);
/* /*
* Check for conflicting refnames in left and right subtrees. Must do * Check for conflicting refnames in left and right subtrees. Must do
* this because higher levels will assume I hand back a self- * this because higher levels will assume I hand back a self-
* consistent namespace list. * 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 * Extract column name and var lists from both subtrees
...@@ -924,9 +908,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -924,9 +908,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
else if (j->quals) else if (j->quals)
{ {
/* User-written ON-condition; transform it */ /* User-written ON-condition; transform it */
j->quals = transformJoinOnClause(pstate, j, j->quals = transformJoinOnClause(pstate, j, my_namespace);
l_rte, r_rte,
my_relnamespace);
} }
else else
{ {
...@@ -985,14 +967,30 @@ transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -985,14 +967,30 @@ transformFromClauseItem(ParseState *pstate, Node *n,
/* /*
* Prepare returned namespace list. If the JOIN has an alias then it * Prepare returned namespace list. If the JOIN has an alias then it
* hides the contained RTEs as far as the relnamespace goes; * hides the contained RTEs completely; otherwise, the contained RTEs
* otherwise, put the contained RTEs and *not* the JOIN into * are still visible as table names, but are not visible for
* relnamespace. * 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) if (j->alias != NULL)
*relnamespace = list_make1(makeNamespaceItem(rte, false, true)); my_namespace = NIL;
else 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; return (Node *) j;
} }
...@@ -1125,17 +1123,37 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, ...@@ -1125,17 +1123,37 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
* Convenience subroutine to construct a ParseNamespaceItem. * Convenience subroutine to construct a ParseNamespaceItem.
*/ */
static 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; ParseNamespaceItem *nsitem;
nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
nsitem->p_rte = rte; 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_only = lateral_only;
nsitem->p_lateral_ok = lateral_ok; nsitem->p_lateral_ok = lateral_ok;
return nsitem; 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 - * setNamespaceLateralState -
* Convenience subroutine to update LATERAL flags in a namespace list. * Convenience subroutine to update LATERAL flags in a namespace list.
......
...@@ -135,11 +135,14 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) ...@@ -135,11 +135,14 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
RangeTblEntry *result = NULL; RangeTblEntry *result = NULL;
ListCell *l; ListCell *l;
foreach(l, pstate->p_relnamespace) foreach(l, pstate->p_namespace)
{ {
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
RangeTblEntry *rte = nsitem->p_rte; RangeTblEntry *rte = nsitem->p_rte;
/* Ignore columns-only items */
if (!nsitem->p_rel_visible)
continue;
/* If not inside LATERAL, ignore lateral-only items */ /* If not inside LATERAL, ignore lateral-only items */
if (nsitem->p_lateral_only && !pstate->p_lateral_active) if (nsitem->p_lateral_only && !pstate->p_lateral_active)
continue; continue;
...@@ -181,11 +184,14 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location) ...@@ -181,11 +184,14 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
RangeTblEntry *result = NULL; RangeTblEntry *result = NULL;
ListCell *l; ListCell *l;
foreach(l, pstate->p_relnamespace) foreach(l, pstate->p_namespace)
{ {
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
RangeTblEntry *rte = nsitem->p_rte; RangeTblEntry *rte = nsitem->p_rte;
/* Ignore columns-only items */
if (!nsitem->p_rel_visible)
continue;
/* If not inside LATERAL, ignore lateral-only items */ /* If not inside LATERAL, ignore lateral-only items */
if (nsitem->p_lateral_only && !pstate->p_lateral_active) if (nsitem->p_lateral_only && !pstate->p_lateral_active)
continue; continue;
...@@ -277,7 +283,7 @@ isFutureCTE(ParseState *pstate, const char *refname) ...@@ -277,7 +283,7 @@ isFutureCTE(ParseState *pstate, const char *refname)
* *
* This is different from refnameRangeTblEntry in that it considers every * This is different from refnameRangeTblEntry in that it considers every
* entry in the ParseState's rangetable(s), not only those that are currently * 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 * 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 * valid matches, but only one will be returned). This must be used ONLY
* as a heuristic in giving suitable error messages. See errorMissingRTE. * as a heuristic in giving suitable error messages. See errorMissingRTE.
...@@ -339,7 +345,7 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) ...@@ -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. * Raise an error if any is found.
* *
* Note: we assume that each given argument does not contain conflicts * Note: we assume that each given argument does not contain conflicts
...@@ -350,7 +356,8 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) ...@@ -350,7 +356,8 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
* are for different relation OIDs (implying they are in different schemas). * are for different relation OIDs (implying they are in different schemas).
* *
* We ignore the lateral-only flags in the namespace items: the lists must * 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 void
checkNameSpaceConflicts(ParseState *pstate, List *namespace1, checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
...@@ -365,11 +372,16 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1, ...@@ -365,11 +372,16 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
const char *aliasname1 = rte1->eref->aliasname; const char *aliasname1 = rte1->eref->aliasname;
ListCell *l2; ListCell *l2;
if (!nsitem1->p_rel_visible)
continue;
foreach(l2, namespace2) foreach(l2, namespace2)
{ {
ParseNamespaceItem *nsitem2 = (ParseNamespaceItem *) lfirst(l2); ParseNamespaceItem *nsitem2 = (ParseNamespaceItem *) lfirst(l2);
RangeTblEntry *rte2 = nsitem2->p_rte; RangeTblEntry *rte2 = nsitem2->p_rte;
if (!nsitem2->p_rel_visible)
continue;
if (strcmp(rte2->eref->aliasname, aliasname1) != 0) if (strcmp(rte2->eref->aliasname, aliasname1) != 0)
continue; /* definitely no conflict */ continue; /* definitely no conflict */
if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL && if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL &&
...@@ -573,12 +585,15 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, ...@@ -573,12 +585,15 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly,
{ {
ListCell *l; ListCell *l;
foreach(l, pstate->p_varnamespace) foreach(l, pstate->p_namespace)
{ {
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
RangeTblEntry *rte = nsitem->p_rte; RangeTblEntry *rte = nsitem->p_rte;
Node *newresult; Node *newresult;
/* Ignore table-only items */
if (!nsitem->p_cols_visible)
continue;
/* If not inside LATERAL, ignore lateral-only items */ /* If not inside LATERAL, ignore lateral-only items */
if (nsitem->p_lateral_only && !pstate->p_lateral_active) if (nsitem->p_lateral_only && !pstate->p_lateral_active)
continue; continue;
...@@ -622,7 +637,7 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, ...@@ -622,7 +637,7 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly,
* *
* This is different from colNameToVar in that it considers every entry in * This is different from colNameToVar in that it considers every entry in
* the ParseState's rangetable(s), not only those that are currently visible * 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 * 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 * matches, but only one will be returned). This must be used ONLY as a
* heuristic in giving suitable error messages. See errorMissingColumn. * heuristic in giving suitable error messages. See errorMissingColumn.
...@@ -1577,7 +1592,7 @@ isLockedRefname(ParseState *pstate, const char *refname) ...@@ -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 * 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 * namespace conflicts.) The RTE is always marked as unconditionally
* visible, that is, not LATERAL-only. * visible, that is, not LATERAL-only.
*/ */
...@@ -1600,12 +1615,11 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, ...@@ -1600,12 +1615,11 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem));
nsitem->p_rte = rte; nsitem->p_rte = rte;
nsitem->p_rel_visible = addToRelNameSpace;
nsitem->p_cols_visible = addToVarNameSpace;
nsitem->p_lateral_only = false; nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true; nsitem->p_lateral_ok = true;
if (addToRelNameSpace) pstate->p_namespace = lappend(pstate->p_namespace, nsitem);
pstate->p_relnamespace = lappend(pstate->p_relnamespace, nsitem);
if (addToVarNameSpace)
pstate->p_varnamespace = lappend(pstate->p_varnamespace, nsitem);
} }
} }
......
...@@ -1108,9 +1108,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, ...@@ -1108,9 +1108,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
* ExpandAllTables() * ExpandAllTables()
* Transforms '*' (in the target list) into a list of targetlist entries. * Transforms '*' (in the target list) into a list of targetlist entries.
* *
* tlist entries are generated for each relation appearing in the query's * tlist entries are generated for each relation visible for unqualified
* varnamespace. We do not consider relnamespace because that would include * column name access. We do not consider qualified-name-only entries because
* input tables of aliasless JOINs, NEW/OLD pseudo-entries, etc. * that would include input tables of aliasless JOINs, NEW/OLD pseudo-entries,
* etc.
* *
* The referenced relations/columns are marked as requiring SELECT access. * The referenced relations/columns are marked as requiring SELECT access.
*/ */
...@@ -1118,29 +1119,42 @@ static List * ...@@ -1118,29 +1119,42 @@ static List *
ExpandAllTables(ParseState *pstate, int location) ExpandAllTables(ParseState *pstate, int location)
{ {
List *target = NIL; List *target = NIL;
bool found_table = false;
ListCell *l; ListCell *l;
/* Check for SELECT *; */ foreach(l, pstate->p_namespace)
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)
{ {
ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l);
RangeTblEntry *rte = nsitem->p_rte; 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 */ /* Should not have any lateral-only items when parsing targetlist */
Assert(!nsitem->p_lateral_only); Assert(!nsitem->p_lateral_only);
/* Remember we found a p_cols_visible item */
found_table = true;
target = list_concat(target, target = list_concat(target,
expandRelAttrs(pstate, rte, rtindex, 0, expandRelAttrs(pstate,
rte,
RTERangeTablePosn(pstate, rte,
NULL),
0,
location)); 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; return target;
} }
......
...@@ -54,25 +54,17 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, ...@@ -54,25 +54,17 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
* will become the fromlist of the query's top-level FromExpr node. * will become the fromlist of the query's top-level FromExpr node.
* *
* p_relnamespace: list of ParseNamespaceItems that represents the current * p_namespace: list of ParseNamespaceItems that represents the current
* namespace for table lookup, ie, those RTEs that are accessible by * namespace for table and column lookup. (The RTEs listed here may be just
* qualified names. (This may be just a subset of the whole rtable.) * a subset of the whole rtable. See ParseNamespaceItem comments below.)
*
* 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_lateral_active: TRUE if we are currently parsing a LATERAL subexpression * p_lateral_active: TRUE if we are currently parsing a LATERAL subexpression
* of this parse level. This makes p_lateral_only namespace items visible, * of this parse level. This makes p_lateral_only namespace items visible,
* whereas they are not visible when p_lateral_active is FALSE. * whereas they are not visible when p_lateral_active is FALSE.
* *
* p_ctenamespace: list of CommonTableExprs (WITH items) that are visible * p_ctenamespace: list of CommonTableExprs (WITH items) that are visible
* at the moment. This is entirely different from p_relnamespace because * at the moment. This is entirely different from p_namespace because a CTE
* a CTE is not an RTE, rather "visibility" means you could make an RTE. * 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 * 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. * visible due to scope rules. This is used to help improve error messages.
...@@ -94,8 +86,8 @@ struct ParseState ...@@ -94,8 +86,8 @@ struct ParseState
List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */
List *p_joinlist; /* join items so far (will become FromExpr List *p_joinlist; /* join items so far (will become FromExpr
* node's fromlist) */ * node's fromlist) */
List *p_relnamespace; /* current namespace for relations */ List *p_namespace; /* currently-referenceable RTEs (List of
List *p_varnamespace; /* current namespace for columns */ * ParseNamespaceItem) */
bool p_lateral_active; /* p_lateral_only items visible? */ bool p_lateral_active; /* p_lateral_only items visible? */
List *p_ctenamespace; /* current namespace for common table exprs */ List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */ List *p_future_ctes; /* common table exprs not yet in namespace */
...@@ -125,10 +117,37 @@ struct ParseState ...@@ -125,10 +117,37 @@ struct ParseState
void *p_ref_hook_state; /* common passthrough link for above */ 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 typedef struct ParseNamespaceItem
{ {
RangeTblEntry *p_rte; /* The relation's rangetable entry */ 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_only; /* Is only visible to LATERAL expressions? */
bool p_lateral_ok; /* If so, does join type allow use? */ bool p_lateral_ok; /* If so, does join type allow use? */
} ParseNamespaceItem; } 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