Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -3064,6 +3064,7 @@ SqlNodeList OrderBy(boolean accept) :
{
final List<SqlNode> list = new ArrayList<SqlNode>();
final Span s;
SqlNode all;
}
{
<ORDER> {
Expand All @@ -3075,10 +3076,32 @@ SqlNodeList OrderBy(boolean accept) :
throw SqlUtil.newContextException(s.pos(), RESOURCE.illegalOrderBy());
}
}
<BY> OrderItemList(list)
{
return new SqlNodeList(list, s.addAll(list).pos());
}
<BY>
(
<ALL> { all = SqlInternalOperators.ORDER_BY_ALL.createCall(getPos()); }
(
<ASC>
| <DESC> { all = SqlStdOperatorTable.DESC.createCall(getPos(), all); }
)?
(
LOOKAHEAD(2)
<NULLS> <FIRST> {
all = SqlStdOperatorTable.NULLS_FIRST.createCall(getPos(), all);
}
|
<NULLS> <LAST> {
all = SqlStdOperatorTable.NULLS_LAST.createCall(getPos(), all);
}
)?
{
list.add(all);
return new SqlNodeList(list, s.addAll(list).pos());
}
|
OrderItemList(list) {
return new SqlNodeList(list, s.addAll(list).pos());
}
)
}

<#if parser.includeSelectBy!false>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ ExInst<CalciteException> illegalArgumentForTableFunctionCall(String a0,
@BaseMessage("Streaming ORDER BY must start with monotonic expression")
ExInst<SqlValidatorException> streamMustOrderByMonotonic();

@BaseMessage("ORDER BY ALL requires an explicit SELECT list; ''*'' is not supported")
ExInst<SqlValidatorException> orderByAllRequiresExplicitSelectList();

@BaseMessage("Set operator cannot combine streaming and non-streaming inputs")
ExInst<SqlValidatorException> streamSetOpInconsistentInputs();

Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlKind.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ public enum SqlKind {
*/
ORDER_BY,

/** The ALL keyword of the ORDER BY clause. */
ORDER_BY_ALL,

/** WITH clause. */
WITH,

Expand Down Expand Up @@ -1482,6 +1485,7 @@ public enum SqlKind {
TIMESTAMP_ADD, TIMESTAMP_DIFF, TIMESTAMP_SUB,
EXTRACT, INTERVAL,
LITERAL_CHAIN, JDBC_FN, PRECEDING, FOLLOWING, ORDER_BY,
ORDER_BY_ALL,
NULLS_FIRST, NULLS_LAST, COLLECTION_TABLE, TABLESAMPLE,
VALUES, WITH, WITH_ITEM, ITEM, SKIP_TO_FIRST, SKIP_TO_LAST,
JSON_VALUE_EXPRESSION, UNNEST),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ private SqlInternalOperators() {
public static final SqlInternalOperator GROUP_BY_ALL =
new SqlRollupOperator("GROUP BY ALL", SqlKind.GROUP_BY_ALL);

/** {@code ORDER BY ALL}, a placeholder expanded during validation into
* a standard {@code ORDER BY}. */
public static final SqlInternalOperator ORDER_BY_ALL =
// High precedence so the placeholder is never wrapped in parentheses when it
// appears alone or inside DESC / NULLS FIRST | LAST in an always-parentheses writer
new SqlInternalOperator("ORDER BY ALL", SqlKind.ORDER_BY_ALL, 100) {
@Override public void unparse(SqlWriter writer, SqlCall call,
int leftPrec, int rightPrec) {
writer.keyword("ALL");
}
};

/** Fetch operator is ONLY used for its precedence during unparsing. */
public static final SqlOperator FETCH =
SqlBasicOperator.create("FETCH")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5158,6 +5158,7 @@ protected void validateOrderList(SqlSelect select) {
// ORDER BY is validated in a scope where aliases in the SELECT clause
// are visible. For example, "SELECT empno AS x FROM emp ORDER BY x"
// is valid.
rewriteOrderByAll(select);
SqlNodeList orderList = select.getOrderList();
if (orderList == null) {
return;
Expand Down Expand Up @@ -5185,6 +5186,62 @@ protected void validateOrderList(SqlSelect select) {
}
}

protected void rewriteOrderByAll(SqlSelect select) {
Comment thread
tisyabhatia marked this conversation as resolved.
final SqlNodeList orderList = select.getOrderList();
if (orderList == null || orderList.size() != 1) {
return;
}

SqlNode node = orderList.get(0);
boolean desc = false;
SqlKind nulls = null;

while (node instanceof SqlCall) {
final SqlKind kind = node.getKind();
if (kind == SqlKind.NULLS_FIRST || kind == SqlKind.NULLS_LAST) {
nulls = kind;
node = ((SqlCall) node).operand(0);
} else if (kind == SqlKind.DESCENDING) {
desc = true;
node = ((SqlCall) node).operand(0);
} else {
break;
}
}

if (node.getKind() != SqlKind.ORDER_BY_ALL) {
return;
}
final SqlParserPos pos = orderList.getParserPosition();
final List<SqlNode> keys = new ArrayList<>();

for (SqlNode selectItem : select.getSelectList()) {
final SqlNode expr = SqlUtil.stripAs(selectItem);
if (expr instanceof SqlIdentifier && ((SqlIdentifier) expr).isStar()) {
throw newValidationError(expr,
RESOURCE.orderByAllRequiresExplicitSelectList());
}
keys.add(applyOrderByAllDirection(expr, desc, nulls, pos));
}
select.setOrderBy(new SqlNodeList(keys, pos));
}

/** Wraps a single ORDER BY ALL key with the optional descending direction
* and null-ordering that apply to every expanded key. */
private static SqlNode applyOrderByAllDirection(SqlNode key, boolean desc,
@Nullable SqlKind nulls, SqlParserPos pos) {
SqlNode result = key;
if (desc) {
result = SqlStdOperatorTable.DESC.createCall(pos, result);
}
if (nulls == SqlKind.NULLS_FIRST) {
result = SqlStdOperatorTable.NULLS_FIRST.createCall(pos, result);
} else if (nulls == SqlKind.NULLS_LAST) {
result = SqlStdOperatorTable.NULLS_LAST.createCall(pos, result);
}
return result;
}

/**
* Validates an item in the GROUP BY clause of a SELECT statement.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ CannotConvertToStream=Cannot convert table ''{0}'' to stream
CannotConvertToRelation=Cannot convert stream ''{0}'' to relation
StreamMustGroupByMonotonic=Streaming aggregation requires at least one monotonic expression in GROUP BY clause
StreamMustOrderByMonotonic=Streaming ORDER BY must start with monotonic expression
OrderByAllRequiresExplicitSelectList=ORDER BY ALL requires an explicit SELECT list; ''*'' is not supported
StreamSetOpInconsistentInputs=Set operator cannot combine streaming and non-streaming inputs
CannotStreamValues=Cannot stream VALUES
CyclicDefinition=Cannot resolve ''{0}''; it references view ''{1}'', whose definition is cyclic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class SqlAdvisorTest extends SqlValidatorTestCase {
Collections.singletonList(
"KEYWORD(*)");

private static final List<String> ORDER_BY_ALL_KEYWORD =
Collections.singletonList(
"KEYWORD(ALL)");

protected static final List<String> FROM_KEYWORDS =
Arrays.asList(
"KEYWORD(()",
Expand Down Expand Up @@ -744,18 +748,20 @@ protected List<String> getJoinKeywords() {
String sql;

sql = "select emp.empno from sales.emp where empno=1 order by ^dummy";
f.withSql(sql).assertHint(EXPR_KEYWORDS, EMP_COLUMNS, EMP_TABLE);
f.withSql(sql).assertHint(EXPR_KEYWORDS, ORDER_BY_ALL_KEYWORD, EMP_COLUMNS,
EMP_TABLE);

sql = "select emp.empno from sales.emp where empno=1 order by ^";
f.withSql(sql).assertComplete(EXPR_KEYWORDS, EMP_COLUMNS, EMP_TABLE);
f.withSql(sql).assertComplete(EXPR_KEYWORDS, ORDER_BY_ALL_KEYWORD,
EMP_COLUMNS, EMP_TABLE);

sql =
"select emp.empno\n"
+ "from sales.emp as e(\n"
+ " mpno,name,ob,gr,iredate,al,omm,eptno,lacker)\n"
+ "where e.mpno=1 order by ^";
f.withSql(sql)
.assertComplete(EXPR_KEYWORDS,
.assertComplete(EXPR_KEYWORDS, ORDER_BY_ALL_KEYWORD,
Arrays.asList("COLUMN(MPNO)",
"COLUMN(NAME)",
"COLUMN(OB)",
Expand Down
24 changes: 24 additions & 0 deletions core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7183,6 +7183,30 @@ public boolean isBangEqualAllowed() {
.ok();
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-7597">[CALCITE-7597]
* Support ORDER BY ALL</a>. */
@Test void testOrderByAll() {
Comment thread
tisyabhatia marked this conversation as resolved.
// expands to all SELECT items, validates fine
sql("select deptno, sal from emp order by all").ok();
// direction applies to every expanded key
sql("select deptno, sal from emp order by all desc").ok();
// SELECT * can't be expanded here
sql("select ^*^ from emp order by all")
.fails("(?s).*ORDER BY ALL requires an explicit SELECT list.*");
// Aliases that shadow other column names must not confuse expansion
sql("select empno as deptno, deptno as empno from emp order by all").ok();
// verify "x" still resolves and the two features coexist
sql("select sal as x, x + 1 as y from emp order by all")
.withValidatorIdentifierExpansion(true)
.withConformance(SqlConformanceEnum.BABEL)
.ok();
sql("select sal as x, x + 1 as y from emp order by all desc")
.withValidatorIdentifierExpansion(true)
.withConformance(SqlConformanceEnum.BABEL)
.ok();
}

@Test void testOrder() {
final SqlConformance conformance = fixture().conformance();
sql("select empno as x from emp order by empno").ok();
Expand Down
14 changes: 14 additions & 0 deletions core/src/test/resources/sql/sort.iq
Original file line number Diff line number Diff line change
Expand Up @@ -549,4 +549,18 @@ order by au."books"[1]."title";

!ok

# [CALCITE-7597] ORDER BY ALL sorts by every SELECT expression, in list order
select x, y from (values (2, 'b'), (1, 'a'), (1, 'c')) as t(x, y)
order by all;
+---+---+
| X | Y |
+---+---+
| 1 | a |
| 1 | c |
| 2 | b |
+---+---+
(3 rows)

!ok

# End sort.iq
8 changes: 7 additions & 1 deletion site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ query:
| query MINUS [ ALL | DISTINCT ] query
| query INTERSECT [ ALL | DISTINCT ] query
}
[ ORDER BY orderItem [, orderItem ]* ]
[ ORDER BY { ALL [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ] | orderItem [, orderItem]* } ]
[ LIMIT [ start, ] { count | ALL } ]
[ OFFSET start { ROW | ROWS } ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
Expand Down Expand Up @@ -402,6 +402,12 @@ in those same conformance levels, any *column* in *insert* may be replaced by
In *orderItem*, if *expression* is a positive integer *n*, it denotes
the <em>n</em>th item in the SELECT clause.

`ORDER BY ALL` sorts by every expression in the SELECT clause,
in the order that they appear in the list; for example:
"SELECT x, y FROM t ORDER BY ALL" is equivalent to
"SELECT x, y FROM t ORDER BY x, y"
An optional trailing ASC / DESC and NULLS FIRST / NULLS LAST applies to all keys.

In *query*, *count* and *start* may each be either an unsigned integer literal
or a dynamic parameter whose value is an integer.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3947,6 +3947,36 @@ void checkPeriodPredicate(Checker checker) {
+ "ORDER BY `EMPNO`, `GENDER` DESC, `DEPTNO`, `EMPNO`, `NAME` DESC");
}

@Test void testOrderByAll() {
final String sql = "select x, y from t\n"
+ "order by all";
final String expected = "SELECT `X`, `Y`\n"
+ "FROM `T`\n"
+ "ORDER BY ALL";
sql(sql).ok(expected);

final String sql1 = "select x, y from t\n"
+ "order by all desc";
final String expected1 = "SELECT `X`, `Y`\n"
+ "FROM `T`\n"
+ "ORDER BY ALL DESC";
sql(sql1).ok(expected1);

final String sql2 = "select x, y from t\n"
Comment thread
tisyabhatia marked this conversation as resolved.
+ "order by all desc nulls last";
final String expected2 = "SELECT `X`, `Y`\n"
+ "FROM `T`\n"
+ "ORDER BY ALL DESC NULLS LAST";
sql(sql2).ok(expected2);

final String sql3 = "select x, y from t\n"
+ "order by all nulls first";
final String expected3 = "SELECT `X`, `Y`\n"
+ "FROM `T`\n"
+ "ORDER BY ALL NULLS FIRST";
sql(sql3).ok(expected3);
}

@Test void testOrderNullsFirst() {
final String sql = "select * from emp\n"
+ "order by gender desc nulls last,\n"
Expand Down
Loading