diff options
author | Dean Rasheed | 2022-07-20 08:29:42 +0000 |
---|---|---|
committer | Dean Rasheed | 2022-07-20 08:29:42 +0000 |
commit | bcedd8f5fce0b69970cf0cee7bca560833d05869 (patch) | |
tree | c37888d24735977b7a0463b0277c1cbe459ac69f /src/backend/parser | |
parent | 1caf915ff31e91031f0a0b8e1016df2b59d6f9de (diff) |
Make subquery aliases optional in the FROM clause.
This allows aliases for sub-SELECTs and VALUES clauses in the FROM
clause to be omitted.
This is an extension of the SQL standard, supported by some other
database systems, and so eases the transition from such systems, as
well as removing the minor inconvenience caused by requiring these
aliases.
Patch by me, reviewed by Tom Lane.
Discussion: https://postgr.es/m/CAEZATCUCGCf82=hxd9N5n6xGHPyYpQnxW8HneeH+uP7yNALkWA@mail.gmail.com
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 19 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 44 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 18 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 29 |
4 files changed, 41 insertions, 69 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 8ed2c4b8c7f..6688c2a865b 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 df5ceea910c..d649a1b8d13 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 c655d188c76..5a18107e799 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 926dcbf30e2..4e635561aaf 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; |