Skip to content
Snippets Groups Projects
Commit f6301574 authored by Tom Lane's avatar Tom Lane
Browse files

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
No related branches found
No related tags found
No related merge requests found
...@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment