summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/parser/analyze.c19
-rw-r--r--src/backend/parser/gram.y44
-rw-r--r--src/backend/parser/parse_clause.c18
-rw-r--r--src/backend/parser/parse_relation.c29
-rw-r--r--src/backend/utils/adt/ruleutils.c6
5 files changed, 46 insertions, 70 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 8ed2c4b8c7..6688c2a865 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -3291,7 +3291,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
foreach(rt, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
- char *rtename;
+ char *rtename = rte->eref->aliasname;
++i;
if (!rte->inFromCl)
@@ -3302,15 +3302,22 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
* name and needs to be skipped (otherwise it might hide a
* base relation with the same name), except if it has a USING
* alias, which *is* visible.
+ *
+ * Subquery and values RTEs without aliases are never visible
+ * as relation names and must always be skipped.
*/
- if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
+ if (rte->alias == NULL)
{
- if (rte->join_using_alias == NULL)
+ if (rte->rtekind == RTE_JOIN)
+ {
+ if (rte->join_using_alias == NULL)
+ continue;
+ rtename = rte->join_using_alias->aliasname;
+ }
+ else if (rte->rtekind == RTE_SUBQUERY ||
+ rte->rtekind == RTE_VALUES)
continue;
- rtename = rte->join_using_alias->aliasname;
}
- else
- rtename = rte->eref->aliasname;
if (strcmp(rtename, thisrel->relname) == 0)
{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index df5ceea910..d649a1b8d1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -13379,33 +13379,6 @@ table_ref: relation_expr opt_alias_clause
n->lateral = false;
n->subquery = $1;
n->alias = $2;
- /*
- * The SQL spec does not permit a subselect
- * (<derived_table>) without an alias clause,
- * so we don't either. This avoids the problem
- * of needing to invent a unique refname for it.
- * That could be surmounted if there's sufficient
- * popular demand, but for now let's just implement
- * the spec and see if anyone complains.
- * However, it does seem like a good idea to emit
- * an error message that's better than "syntax error".
- */
- if ($2 == NULL)
- {
- if (IsA($1, SelectStmt) &&
- ((SelectStmt *) $1)->valuesLists)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("VALUES in FROM must have an alias"),
- errhint("For example, FROM (VALUES ...) [AS] foo."),
- parser_errposition(@1)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("subquery in FROM must have an alias"),
- errhint("For example, FROM (SELECT ...) [AS] foo."),
- parser_errposition(@1)));
- }
$$ = (Node *) n;
}
| LATERAL_P select_with_parens opt_alias_clause
@@ -13415,23 +13388,6 @@ table_ref: relation_expr opt_alias_clause
n->lateral = true;
n->subquery = $2;
n->alias = $3;
- /* same comment as above */
- if ($3 == NULL)
- {
- if (IsA($2, SelectStmt) &&
- ((SelectStmt *) $2)->valuesLists)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("VALUES in FROM must have an alias"),
- errhint("For example, FROM (VALUES ...) [AS] foo."),
- parser_errposition(@2)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("subquery in FROM must have an alias"),
- errhint("For example, FROM (SELECT ...) [AS] foo."),
- parser_errposition(@2)));
- }
$$ = (Node *) n;
}
| joined_table
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index c655d188c7..5a18107e79 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -404,16 +404,6 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
Query *query;
/*
- * We require user to supply an alias for a subselect, per SQL92. To relax
- * this, we'd have to be prepared to gin up a unique alias for an
- * unlabeled subselect. (This is just elog, not ereport, because the
- * grammar should have enforced it already. It'd probably be better to
- * report the error here, but we don't have a good error location here.)
- */
- if (r->alias == NULL)
- elog(ERROR, "subquery in FROM must have an alias");
-
- /*
* Set p_expr_kind to show this parse level is recursing to a subselect.
* We can't be nested within any expression, so don't need save-restore
* logic here.
@@ -430,10 +420,14 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
pstate->p_lateral_active = r->lateral;
/*
- * Analyze and transform the subquery.
+ * Analyze and transform the subquery. Note that if the subquery doesn't
+ * have an alias, it can't be explicitly selected for locking, but locking
+ * might still be required (if there is an all-tables locking clause).
*/
query = parse_sub_analyze(r->subquery, pstate, NULL,
- isLockedRefname(pstate, r->alias->aliasname),
+ isLockedRefname(pstate,
+ r->alias == NULL ? NULL :
+ r->alias->aliasname),
true);
/* Restore state */
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 926dcbf30e..4e635561aa 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1577,7 +1577,11 @@ addRangeTableEntryForRelation(ParseState *pstate,
* Then, construct and return a ParseNamespaceItem for the new RTE.
*
* This is much like addRangeTableEntry() except that it makes a subquery RTE.
- * Note that an alias clause *must* be supplied.
+ *
+ * If the subquery does not have an alias, the auto-generated relation name in
+ * the returned ParseNamespaceItem will be marked as not visible, and so only
+ * unqualified references to the subquery columns will be allowed, and the
+ * relation name will not conflict with others in the pstate's namespace list.
*/
ParseNamespaceItem *
addRangeTableEntryForSubquery(ParseState *pstate,
@@ -1587,7 +1591,6 @@ addRangeTableEntryForSubquery(ParseState *pstate,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
- char *refname = alias->aliasname;
Alias *eref;
int numaliases;
List *coltypes,
@@ -1595,6 +1598,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
*colcollations;
int varattno;
ListCell *tlistitem;
+ ParseNamespaceItem *nsitem;
Assert(pstate != NULL);
@@ -1602,7 +1606,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
rte->subquery = subquery;
rte->alias = alias;
- eref = copyObject(alias);
+ eref = alias ? copyObject(alias) : makeAlias("unnamed_subquery", NIL);
numaliases = list_length(eref->colnames);
/* fill in any unspecified alias columns, and extract column type info */
@@ -1634,7 +1638,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("table \"%s\" has %d columns available but %d columns specified",
- refname, varattno, numaliases)));
+ eref->aliasname, varattno, numaliases)));
rte->eref = eref;
@@ -1665,8 +1669,15 @@ addRangeTableEntryForSubquery(ParseState *pstate,
* Build a ParseNamespaceItem, but don't add it to the pstate's namespace
* list --- caller must do that if appropriate.
*/
- return buildNSItemFromLists(rte, list_length(pstate->p_rtable),
- coltypes, coltypmods, colcollations);
+ nsitem = buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ coltypes, coltypmods, colcollations);
+
+ /*
+ * Mark it visible as a relation name only if it had a user-written alias.
+ */
+ nsitem->p_rel_visible = (alias != NULL);
+
+ return nsitem;
}
/*
@@ -2520,6 +2531,10 @@ addRangeTableEntryForENR(ParseState *pstate,
* This is used when we have not yet done transformLockingClause, but need
* to know the correct lock to take during initial opening of relations.
*
+ * Note that refname may be NULL (for a subquery without an alias), in which
+ * case the relation can't be locked by name, but it might still be locked if
+ * a locking clause requests that all tables be locked.
+ *
* Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE,
* since the table-level lock is the same either way.
*/
@@ -2544,7 +2559,7 @@ isLockedRefname(ParseState *pstate, const char *refname)
/* all tables used in query */
return true;
}
- else
+ else if (refname != NULL)
{
/* just the named tables */
ListCell *l2;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 26cf4fa9a0..513bf0f026 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -11725,7 +11725,11 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
else if (rte->rtekind == RTE_SUBQUERY ||
rte->rtekind == RTE_VALUES)
{
- /* Alias is syntactically required for SUBQUERY and VALUES */
+ /*
+ * For a subquery, always print alias. This makes the output SQL
+ * spec-compliant, even though we allow the alias to be omitted on
+ * input.
+ */
printalias = true;
}
else if (rte->rtekind == RTE_CTE)