Skip to content
Closed
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
7 changes: 7 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlDialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,13 @@ public boolean supportsJoinType(JoinRelType joinType) {
return true;
}

/**
* Returns whether this dialect supports "ASYMMETRIC" and "SYMMETRIC" in "BETWEEN" clause.
*/
public boolean supportsSQL99Between() {
return true;
}

/** Returns how NULL values are sorted if an ORDER BY item does not contain
* NULLS ASCENDING or NULLS DESCENDING. */
public NullCollation getNullCollation() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public HiveSqlDialect(Context context) {
return false;
}

@Override public boolean supportsSQL99Between() {
return false;
}

@Override public @Nullable SqlNode getCastSpec(final RelDataType type) {
if (type instanceof BasicSqlType) {
switch (type.getSqlTypeName()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ public SparkSqlDialect(SqlDialect.Context context) {
return false;
}

@Override public boolean supportsSQL99Between() {
return false;
}

@Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset,
@Nullable SqlNode fetch) {
unparseFetchUsingLimit(writer, offset, fetch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ public StarRocksSqlDialect(Context context) {
return true;
}

@Override public boolean supportsSQL99Between() {
return false;
}

@Override public RexNode prepareUnparse(RexNode arg) {
return RelToSqlConverterUtil.unparseIsTrueOrFalse(arg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ private static SqlBetweenOperator of(boolean negated, boolean symmetric) {
writer.startList(FRAME_TYPE, "", "");
call.operand(VALUE_OPERAND).unparse(writer, getLeftPrec(), 0);
writer.sep(super.getName());
writer.sep(flag.name());
if (writer.getDialect().supportsSQL99Between()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were some comments on the issue about SYMMETRIC that needs to output an OR of two BETWEEN conditions. Where does this happen?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pointing that out — I missed that comment. It seems we're doing this conversion in toRel

. Do we still need to do the same in unparse? Perhaps we could simply throw an exception here for unsupported SYMMETRIC calls. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the conversion was done in the convertlet table, you will never see this operator in your conversion, so there is nothing to do. You are handling the case when the conversion was not made.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the conversion was done in the convertlet table, you will never see this operator in your conversion, so there is nothing to do. You are handling the case when the conversion was not made.

Makes sense

writer.sep(flag.name());
}

// If the expression for the lower bound contains a call to an AND
// operator, we need to wrap the expression in parentheses to prevent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,86 @@ private void checkLarge(int n) {
.ok("VALUES (ROW((`A` BETWEEN ASYMMETRIC ((`B` OR (`C` AND `D`)) OR `E`) AND `F`)))");
}

/**
* Test case for [CALCITE-4471](https://issues.apache.org/jira/browse/CALCITE-4471) that
* Spark SQL's BETWEEN operator does not support the ASYMMETRIC/SYMMETRIC keywords.
*/
@Test void testBetweenForSparkSqlDialect() {
SqlParserFixture f = fixture().withDialect(SparkSqlDialect.DEFAULT);

f.sql("select * from t where price between 1 and 2")
.ok("SELECT *\n"
+ "FROM `T`\n"
+ "WHERE (`PRICE` BETWEEN 1 AND 2)");

f.sql("select * from t where price not between 1 and 2")
.ok("SELECT *\n"
+ "FROM `T`\n"
+ "WHERE (`PRICE` NOT BETWEEN 1 AND 2)");

f.sql("select * from t where price between 1 and 2+2*2")
.ok("SELECT *\n"
+ "FROM `T`\n"
+ "WHERE (`PRICE` BETWEEN 1 AND (2 + (2 * 2)))");

final String sql0 = "select * from t\n"
+ " where price > 5\n"
+ " and price not between 1 + 2 and 3 * 4 AnD price is null";
final String expected0 = "SELECT *\n"
+ "FROM `T`\n"
+ "WHERE (((`PRICE` > 5) "
+ "AND (`PRICE` NOT BETWEEN (1 + 2) AND (3 * 4))) "
+ "AND (`PRICE` IS NULL))";
f.sql(sql0).ok(expected0);

final String sql1 = "select * from t\n"
+ "where price > 5\n"
+ "and price between 1 + 2 and 3 * 4 + price is null";
final String expected1 = "SELECT *\n"
+ "FROM `T`\n"
+ "WHERE ((`PRICE` > 5) "
+ "AND ((`PRICE` BETWEEN (1 + 2) AND ((3 * 4) + `PRICE`)) "
+ "IS NULL))";
f.sql(sql1).ok(expected1);

final String sql2 = "select * from t\n"
+ "where price > 5\n"
+ "and price between 1 + 2 and 3 * 4 or price is null";
final String expected2 = "SELECT *\n"
+ "FROM `T`\n"
+ "WHERE (((`PRICE` > 5) "
+ "AND (`PRICE` BETWEEN (1 + 2) AND (3 * 4))) "
+ "OR (`PRICE` IS NULL))";
f.sql(sql2).ok(expected2);

final String sql3 = "values a between c and d and e and f between g and h";
final String expected3 = "VALUES ("
+ "(((`A` BETWEEN `C` AND `D`) AND `E`)"
+ " AND (`F` BETWEEN `G` AND `H`)))";
f.sql(sql3).ok(expected3);

f.sql("values a between b or c^")
.fails(".*BETWEEN operator has no terminating AND");

f.sql("values a ^between^")
.fails("(?s).*Encountered \"between <EOF>\" at line 1, column 10.*");

f.sql("values a between 1^")
.fails(".*BETWEEN operator has no terminating AND");

// precedence of BETWEEN is higher than AND and OR, but lower than '+'
f.sql("values a between b and c + 2 or d and e")
.ok("VALUES (((`A` BETWEEN `B` AND (`C` + 2)) OR (`D` AND `E`)))");

// '=' has slightly lower precedence than BETWEEN; both are left-assoc
f.sql("values x = a between b and c = d = e")
.ok("VALUES ((((`X` = (`A` BETWEEN `B` AND `C`)) = `D`) = `E`))");

// AND doesn't match BETWEEN if it's between parentheses!
f.sql("values a between b or (c and d) or e and f")
.ok("VALUES ((`A` BETWEEN ((`B` OR (`C` AND `D`)) OR `E`) AND `F`))");
}

@Test void testOperateOnColumn() {
sql("select c1*1,c2 + 2,c3/3,c4-4,c5*c4 from t")
.ok("SELECT (`C1` * 1), (`C2` + 2), (`C3` / 3), (`C4` - 4), (`C5` * `C4`)\n"
Expand Down
Loading