summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c3
-rw-r--r--src/backend/parser/parse_clause.c199
-rw-r--r--src/backend/parser/parse_relation.c83
-rw-r--r--src/backend/parser/parse_target.c18
4 files changed, 165 insertions, 138 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 447a61ef8c..748bebffc1 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1734,7 +1734,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
targetnames,
sortnscolumns,
JOIN_INNER,
+ 0,
targetvars,
+ NIL,
+ NIL,
NULL,
false);
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 5fa42d307a..36a3efff87 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -52,9 +52,9 @@
#include "utils/syscache.h"
-static void extractRemainingColumns(List *common_colnames,
- List *src_colnames, List *src_colvars,
- ParseNamespaceColumn *src_nscolumns,
+static int extractRemainingColumns(ParseNamespaceColumn *src_nscolumns,
+ List *src_colnames,
+ List **src_colnos,
List **res_colnames, List **res_colvars,
ParseNamespaceColumn *res_nscolumns);
static Node *transformJoinUsingClause(ParseState *pstate,
@@ -76,6 +76,7 @@ static ParseNamespaceItem *getNSItemForSpecialRelationTypes(ParseState *pstate,
static Node *transformFromClauseItem(ParseState *pstate, Node *n,
ParseNamespaceItem **top_nsitem,
List **namespace);
+static Var *buildVarFromNSColumn(ParseNamespaceColumn *nscol);
static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
Var *l_colvar, Var *r_colvar);
static void setNamespaceColumnVisibility(List *namespace, bool cols_visible);
@@ -237,64 +238,61 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
/*
* Extract all not-in-common columns from column lists of a source table
*
- * We hand back new lists in *res_colnames and *res_colvars. But
- * res_nscolumns points to a caller-allocated array that we fill in
- * the next few entries in.
+ * src_nscolumns and src_colnames describe the source table.
+ *
+ * *src_colnos initially contains the column numbers of the already-merged
+ * columns. We add to it the column number of each additional column.
+ * Also append to *res_colnames the name of each additional column,
+ * append to *res_colvars a Var for each additional column, and copy the
+ * columns' nscolumns data into res_nscolumns[] (which is caller-allocated
+ * space that had better be big enough).
+ *
+ * Returns the number of columns added.
*/
-static void
-extractRemainingColumns(List *common_colnames,
- List *src_colnames, List *src_colvars,
- ParseNamespaceColumn *src_nscolumns,
+static int
+extractRemainingColumns(ParseNamespaceColumn *src_nscolumns,
+ List *src_colnames,
+ List **src_colnos,
List **res_colnames, List **res_colvars,
ParseNamespaceColumn *res_nscolumns)
{
- List *new_colnames = NIL;
- List *new_colvars = NIL;
- ListCell *lnames,
- *lvars;
-
- Assert(list_length(src_colnames) == list_length(src_colvars));
+ int colcount = 0;
+ Bitmapset *prevcols;
+ int attnum;
+ ListCell *lc;
- forboth(lnames, src_colnames, lvars, src_colvars)
+ /*
+ * While we could just test "list_member_int(*src_colnos, attnum)" to
+ * detect already-merged columns in the loop below, that would be O(N^2)
+ * for a wide input table. Instead build a bitmapset of just the merged
+ * USING columns, which we won't add to within the main loop.
+ */
+ prevcols = NULL;
+ foreach(lc, *src_colnos)
{
- char *colname = strVal(lfirst(lnames));
- bool match = false;
- ListCell *cnames;
-
- /*
- * Ignore any dropped columns in the src_nscolumns input. (The list
- * inputs won't contain entries for dropped columns.)
- */
- while (src_nscolumns->p_varno == 0)
- src_nscolumns++;
-
- /* is current src column already accounted for in common_colnames? */
- foreach(cnames, common_colnames)
- {
- char *ccolname = strVal(lfirst(cnames));
+ prevcols = bms_add_member(prevcols, lfirst_int(lc));
+ }
- if (strcmp(colname, ccolname) == 0)
- {
- match = true;
- break;
- }
- }
+ attnum = 0;
+ foreach(lc, src_colnames)
+ {
+ char *colname = strVal(lfirst(lc));
- if (!match)
+ attnum++;
+ /* Non-dropped and not already merged? */
+ if (colname[0] != '\0' && !bms_is_member(attnum, prevcols))
{
- /* Nope, so emit it as next output column */
- new_colnames = lappend(new_colnames, lfirst(lnames));
- new_colvars = lappend(new_colvars, lfirst(lvars));
+ /* Yes, so emit it as next output column */
+ *src_colnos = lappend_int(*src_colnos, attnum);
+ *res_colnames = lappend(*res_colnames, lfirst(lc));
+ *res_colvars = lappend(*res_colvars,
+ buildVarFromNSColumn(src_nscolumns + attnum - 1));
/* Copy the input relation's nscolumn data for this column */
- *res_nscolumns = *src_nscolumns;
- res_nscolumns++;
+ res_nscolumns[colcount] = src_nscolumns[attnum - 1];
+ colcount++;
}
-
- src_nscolumns++;
}
-
- *res_colnames = new_colnames;
- *res_colvars = new_colvars;
+ return colcount;
}
/* transformJoinUsingClause()
@@ -1154,10 +1152,12 @@ transformFromClauseItem(ParseState *pstate, Node *n,
*l_colnames,
*r_colnames,
*res_colnames,
- *l_colvars,
- *r_colvars,
+ *l_colnos,
+ *r_colnos,
*res_colvars;
- ParseNamespaceColumn *res_nscolumns;
+ ParseNamespaceColumn *l_nscolumns,
+ *r_nscolumns,
+ *res_nscolumns;
int res_colindex;
bool lateral_ok;
int sv_namespace_length;
@@ -1211,12 +1211,15 @@ transformFromClauseItem(ParseState *pstate, Node *n,
my_namespace = list_concat(l_namespace, r_namespace);
/*
- * Extract column name and var lists from both subtrees
- *
- * Note: expandNSItemVars returns new lists, safe for me to modify
+ * We'll work from the nscolumns data and eref alias column names for
+ * each of the input nsitems. Note that these include dropped
+ * columns, which is helpful because we can keep track of physical
+ * input column numbers more easily.
*/
- l_colvars = expandNSItemVars(l_nsitem, 0, -1, &l_colnames);
- r_colvars = expandNSItemVars(r_nsitem, 0, -1, &r_colnames);
+ l_nscolumns = l_nsitem->p_nscolumns;
+ l_colnames = l_nsitem->p_rte->eref->colnames;
+ r_nscolumns = r_nsitem->p_nscolumns;
+ r_colnames = r_nsitem->p_rte->eref->colnames;
/*
* Natural join does not explicitly specify columns; must generate
@@ -1240,6 +1243,9 @@ transformFromClauseItem(ParseState *pstate, Node *n,
char *l_colname = strVal(lfirst(lx));
Value *m_name = NULL;
+ if (l_colname[0] == '\0')
+ continue; /* ignore dropped columns */
+
foreach(rx, r_colnames)
{
char *r_colname = strVal(lfirst(rx));
@@ -1262,6 +1268,8 @@ transformFromClauseItem(ParseState *pstate, Node *n,
/*
* Now transform the join qualifications, if any.
*/
+ l_colnos = NIL;
+ r_colnos = NIL;
res_colnames = NIL;
res_colvars = NIL;
@@ -1297,6 +1305,8 @@ transformFromClauseItem(ParseState *pstate, Node *n,
Node *u_colvar;
ParseNamespaceColumn *res_nscolumn;
+ Assert(u_colname[0] != '\0');
+
/* Check for USING(foo,foo) */
foreach(col, res_colnames)
{
@@ -1331,6 +1341,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" specified in USING clause does not exist in left table",
u_colname)));
+ l_colnos = lappend_int(l_colnos, l_index + 1);
/* Find it in right input */
ndx = 0;
@@ -1354,10 +1365,11 @@ transformFromClauseItem(ParseState *pstate, Node *n,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" specified in USING clause does not exist in right table",
u_colname)));
+ r_colnos = lappend_int(r_colnos, r_index + 1);
- l_colvar = list_nth(l_colvars, l_index);
+ l_colvar = buildVarFromNSColumn(l_nscolumns + l_index);
l_usingvars = lappend(l_usingvars, l_colvar);
- r_colvar = list_nth(r_colvars, r_index);
+ r_colvar = buildVarFromNSColumn(r_nscolumns + r_index);
r_usingvars = lappend(r_usingvars, r_colvar);
res_colnames = lappend(res_colnames, lfirst(ucol));
@@ -1371,26 +1383,12 @@ transformFromClauseItem(ParseState *pstate, Node *n,
if (u_colvar == (Node *) l_colvar)
{
/* Merged column is equivalent to left input */
- res_nscolumn->p_varno = l_colvar->varno;
- res_nscolumn->p_varattno = l_colvar->varattno;
- res_nscolumn->p_vartype = l_colvar->vartype;
- res_nscolumn->p_vartypmod = l_colvar->vartypmod;
- res_nscolumn->p_varcollid = l_colvar->varcollid;
- /* XXX these are not quite right, but doesn't matter yet */
- res_nscolumn->p_varnosyn = l_colvar->varno;
- res_nscolumn->p_varattnosyn = l_colvar->varattno;
+ *res_nscolumn = l_nscolumns[l_index];
}
else if (u_colvar == (Node *) r_colvar)
{
/* Merged column is equivalent to right input */
- res_nscolumn->p_varno = r_colvar->varno;
- res_nscolumn->p_varattno = r_colvar->varattno;
- res_nscolumn->p_vartype = r_colvar->vartype;
- res_nscolumn->p_vartypmod = r_colvar->vartypmod;
- res_nscolumn->p_varcollid = r_colvar->varcollid;
- /* XXX these are not quite right, but doesn't matter yet */
- res_nscolumn->p_varnosyn = r_colvar->varno;
- res_nscolumn->p_varattnosyn = r_colvar->varattno;
+ *res_nscolumn = r_nscolumns[r_index];
}
else
{
@@ -1427,22 +1425,14 @@ transformFromClauseItem(ParseState *pstate, Node *n,
}
/* Add remaining columns from each side to the output columns */
- extractRemainingColumns(res_colnames,
- l_colnames, l_colvars,
- l_nsitem->p_nscolumns,
- &l_colnames, &l_colvars,
- res_nscolumns + res_colindex);
- res_colindex += list_length(l_colvars);
- extractRemainingColumns(res_colnames,
- r_colnames, r_colvars,
- r_nsitem->p_nscolumns,
- &r_colnames, &r_colvars,
- res_nscolumns + res_colindex);
- res_colindex += list_length(r_colvars);
- res_colnames = list_concat(res_colnames, l_colnames);
- res_colvars = list_concat(res_colvars, l_colvars);
- res_colnames = list_concat(res_colnames, r_colnames);
- res_colvars = list_concat(res_colvars, r_colvars);
+ res_colindex +=
+ extractRemainingColumns(l_nscolumns, l_colnames, &l_colnos,
+ &res_colnames, &res_colvars,
+ res_nscolumns + res_colindex);
+ res_colindex +=
+ extractRemainingColumns(r_nscolumns, r_colnames, &r_colnos,
+ &res_colnames, &res_colvars,
+ res_nscolumns + res_colindex);
/*
* Check alias (AS clause), if any.
@@ -1468,7 +1458,10 @@ transformFromClauseItem(ParseState *pstate, Node *n,
res_colnames,
res_nscolumns,
j->jointype,
+ list_length(j->usingClause),
res_colvars,
+ l_colnos,
+ r_colnos,
j->alias,
true);
@@ -1538,6 +1531,30 @@ transformFromClauseItem(ParseState *pstate, Node *n,
}
/*
+ * buildVarFromNSColumn -
+ * build a Var node using ParseNamespaceColumn data
+ *
+ * We assume varlevelsup should be 0, and no location is specified
+ */
+static Var *
+buildVarFromNSColumn(ParseNamespaceColumn *nscol)
+{
+ Var *var;
+
+ Assert(nscol->p_varno > 0); /* i.e., not deleted column */
+ var = makeVar(nscol->p_varno,
+ nscol->p_varattno,
+ nscol->p_vartype,
+ nscol->p_vartypmod,
+ nscol->p_varcollid,
+ 0);
+ /* makeVar doesn't offer parameters for these, so set by hand: */
+ var->varnosyn = nscol->p_varnosyn;
+ var->varattnosyn = nscol->p_varattnosyn;
+ return var;
+}
+
+/*
* buildMergedJoinVar -
* generate a suitable replacement expression for a merged join column
*/
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index ceed0ceb48..b875a50646 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -714,12 +714,15 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem,
colname,
rte->eref->aliasname)));
- var = makeVar(nsitem->p_rtindex,
- attnum,
+ var = makeVar(nscol->p_varno,
+ nscol->p_varattno,
nscol->p_vartype,
nscol->p_vartypmod,
nscol->p_varcollid,
sublevels_up);
+ /* makeVar doesn't offer parameters for these, so set them by hand: */
+ var->varnosyn = nscol->p_varnosyn;
+ var->varattnosyn = nscol->p_varattnosyn;
}
else
{
@@ -991,9 +994,10 @@ searchRangeTableForCol(ParseState *pstate, const char *alias, const char *colnam
*
* col == InvalidAttrNumber means a "whole row" reference
*
- * The caller should pass the actual RTE if it has it handy; otherwise pass
- * NULL, and we'll look it up here. (This uglification of the API is
- * worthwhile because nearly all external callers have the RTE at hand.)
+ * External callers should always pass the Var's RTE. Internally, we
+ * allow NULL to be passed for the RTE and then look it up if needed;
+ * this takes less code than requiring each internal recursion site
+ * to perform a lookup.
*/
static void
markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
@@ -1062,21 +1066,11 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
else
{
/*
- * Regular join attribute, look at the alias-variable list.
- *
- * The aliasvar could be either a Var or a COALESCE expression,
- * but in the latter case we should already have marked the two
- * referent variables as being selected, due to their use in the
- * JOIN clause. So we need only be concerned with the Var case.
- * But we do need to drill down through implicit coercions.
+ * Join alias Vars for ordinary columns must refer to merged JOIN
+ * USING columns. We don't need to do anything here, because the
+ * join input columns will also be referenced in the join's qual
+ * clause, and will get marked for select privilege there.
*/
- Var *aliasvar;
-
- Assert(col > 0 && col <= list_length(rte->joinaliasvars));
- aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1);
- aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar);
- if (aliasvar && IsA(aliasvar, Var))
- markVarForSelectPriv(pstate, aliasvar, NULL);
}
}
/* other RTE types don't require privilege marking */
@@ -1085,9 +1079,6 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
/*
* markVarForSelectPriv
* Mark the RTE referenced by a Var as requiring SELECT privilege
- *
- * The caller should pass the Var's referenced RTE if it has it handy
- * (nearly all do); otherwise pass NULL.
*/
void
markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte)
@@ -2110,7 +2101,10 @@ addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
ParseNamespaceColumn *nscolumns,
JoinType jointype,
+ int nummergedcols,
List *aliasvars,
+ List *leftcols,
+ List *rightcols,
Alias *alias,
bool inFromCl)
{
@@ -2135,7 +2129,10 @@ addRangeTableEntryForJoin(ParseState *pstate,
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->jointype = jointype;
+ rte->joinmergedcols = nummergedcols;
rte->joinaliasvars = aliasvars;
+ rte->joinleftcols = leftcols;
+ rte->joinrightcols = rightcols;
rte->alias = alias;
eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL);
@@ -2713,11 +2710,11 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
/*
* During ordinary parsing, there will never be any
- * deleted columns in the join; but we have to check since
- * this routine is also used by the rewriter, and joins
- * found in stored rules might have join columns for
- * since-deleted columns. This will be signaled by a null
- * pointer in the alias-vars list.
+ * deleted columns in the join. While this function is
+ * also used by the rewriter and planner, they do not
+ * currently call it on any JOIN RTEs. Therefore, this
+ * next bit is dead code, but it seems prudent to handle
+ * the case correctly anyway.
*/
if (avar == NULL)
{
@@ -2753,11 +2750,26 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
{
Var *varnode;
- varnode = makeVar(rtindex, varattno,
- exprType(avar),
- exprTypmod(avar),
- exprCollation(avar),
- sublevels_up);
+ /*
+ * If the joinaliasvars entry is a simple Var, just
+ * copy it (with adjustment of varlevelsup and
+ * location); otherwise it is a JOIN USING column and
+ * we must generate a join alias Var. This matches
+ * the results that expansion of "join.*" by
+ * expandNSItemVars would have produced, if we had
+ * access to the ParseNamespaceItem for the join.
+ */
+ if (IsA(avar, Var))
+ {
+ varnode = copyObject((Var *) avar);
+ varnode->varlevelsup = sublevels_up;
+ }
+ else
+ varnode = makeVar(rtindex, varattno,
+ exprType(avar),
+ exprTypmod(avar),
+ exprCollation(avar),
+ sublevels_up);
varnode->location = location;
*colvars = lappend(*colvars, varnode);
@@ -2971,12 +2983,15 @@ expandNSItemVars(ParseNamespaceItem *nsitem,
Var *var;
Assert(nscol->p_varno > 0);
- var = makeVar(nsitem->p_rtindex,
- colindex + 1,
+ var = makeVar(nscol->p_varno,
+ nscol->p_varattno,
nscol->p_vartype,
nscol->p_vartypmod,
nscol->p_varcollid,
sublevels_up);
+ /* makeVar doesn't offer parameters for these, so set by hand: */
+ var->varnosyn = nscol->p_varnosyn;
+ var->varattnosyn = nscol->p_varattnosyn;
var->location = location;
result = lappend(result, var);
if (colnames)
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 8476d3cb3f..566c517837 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -346,8 +346,11 @@ markTargetListOrigins(ParseState *pstate, List *targetlist)
*
* levelsup is an extra offset to interpret the Var's varlevelsup correctly.
*
- * This is split out so it can recurse for join references. Note that we
- * do not drill down into views, but report the view as the column owner.
+ * Note that we do not drill down into views, but report the view as the
+ * column owner. There's also no need to drill down into joins: if we see
+ * a join alias Var, it must be a merged JOIN USING column (or possibly a
+ * whole-row Var); that is not a direct reference to any plain table column,
+ * so we don't report it.
*/
static void
markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
@@ -385,17 +388,6 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
}
break;
case RTE_JOIN:
- /* Join RTE --- recursively inspect the alias variable */
- if (attnum != InvalidAttrNumber)
- {
- Var *aliasvar;
-
- Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
- aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
- /* We intentionally don't strip implicit coercions here */
- markTargetListOrigin(pstate, tle, aliasvar, netlevelsup);
- }
- break;
case RTE_FUNCTION:
case RTE_VALUES:
case RTE_TABLEFUNC: