Skip to content

[CALCITE-6835] Invalid unparse for IS TRUE,IS FALSE,IS NOT TRUE and I… #4214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,12 @@ public abstract class SqlImplementor {

protected final Map<CorrelationId, Context> correlTableMap = new HashMap<>();

/** Private RexBuilder for short-lived expressions. It has its own
* dedicated type factory, so don't trust the types to be canonized. */
/**
* Private RexBuilder for short-lived expressions. It has its own
* dedicated type factory, so don't trust the types to be canonized.
*
* @deprecated Replaced by {@link RexBuilder#DEFAULT}. */
@Deprecated // to be removed before 2.0
final RexBuilder rexBuilder =
new RexBuilder(new SqlTypeFactoryImpl(RelDataTypeSystemImpl.DEFAULT));

Expand Down Expand Up @@ -652,6 +656,7 @@ public SqlNode orderField(int ordinal) {
* @param rex Expression to convert
*/
public SqlNode toSql(@Nullable RexProgram program, RexNode rex) {
rex = dialect.prepareUnparse(rex);
final RexSubQuery subQuery;
final SqlNode sqlSubQuery;
final RexLiteral literal;
Expand Down Expand Up @@ -798,7 +803,7 @@ public SqlNode toSql(@Nullable RexProgram program, RexNode rex) {
//noinspection unchecked
return toSql(program, search.operands.get(0), literal.getType(), sarg);
}
return toSql(program, RexUtil.expandSearch(implementor().rexBuilder, program, search));
return toSql(program, RexUtil.expandSearch(RexBuilder.DEFAULT, program, search));

case EXISTS:
case UNIQUE:
Expand Down Expand Up @@ -953,7 +958,7 @@ private <C extends Comparable<C>> SqlNode toSql(@Nullable RexProgram program,
final RangeSets.Consumer<C> consumer =
new RangeToSql<>(operandSql, orList, v ->
toSql(program,
implementor().rexBuilder.makeLiteral(v, type)));
RexBuilder.DEFAULT.makeLiteral(v, type)));
RangeSets.forEach(sarg.rangeSet, consumer);
}
return SqlUtil.createCall(SqlStdOperatorTable.OR, POS, orList);
Expand All @@ -965,7 +970,7 @@ private <C extends Comparable<C>> SqlNode toIn(SqlNode operandSql,
final SqlNodeList list = rangeSet.asRanges().stream()
.map(range ->
toSql(program,
implementor().rexBuilder.makeLiteral(range.lowerEndpoint(),
RexBuilder.DEFAULT.makeLiteral(range.lowerEndpoint(),
type, true, true)))
.collect(SqlNode.toList());
switch (list.size()) {
Expand Down Expand Up @@ -1718,7 +1723,7 @@ private JoinContext(SqlDialect dialect, Context leftContext,
&& ((RexInputRef) op0).getIndex() >= leftContext.fieldCount) {
// Arguments were of form 'op1 = op0'
final SqlOperator op2 = requireNonNull(call.getOperator().reverse());
return (RexCall) rexBuilder.makeCall(call.getParserPosition(), op2, op1, op0);
return (RexCall) RexBuilder.DEFAULT.makeCall(call.getParserPosition(), op2, op1, op0);
}
// fall through
default:
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/java/org/apache/calcite/rex/RexBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlCollation;
Expand All @@ -42,6 +43,7 @@
import org.apache.calcite.sql.type.IntervalSqlType;
import org.apache.calcite.sql.type.MapSqlType;
import org.apache.calcite.sql.type.MultisetSqlType;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
Expand Down Expand Up @@ -94,6 +96,11 @@
* <p>Some common literal values (NULL, TRUE, FALSE, 0, 1, '') are cached.
*/
public class RexBuilder {

/** Default RexBuilder. */
public static final RexBuilder DEFAULT =
new RexBuilder(new SqlTypeFactoryImpl(RelDataTypeSystemImpl.DEFAULT));

/**
* Special operator that accesses an unadvertised field of an input record.
* This operator cannot be used in SQL queries; it is introduced temporarily
Expand Down
5 changes: 5 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 @@ -795,6 +795,11 @@ public boolean supportBooleanCaseWhen() {
return true;
}

/** Converts {@link RexNode} expression to {@link RexNode} expression before unparse. */
public RexNode prepareUnparse(RexNode rexNode) {
return rexNode;
}

/** Returns whether this dialect supports a given function or operator.
* It only applies to built-in scalar functions and operators, since
* user-defined functions and procedures should be read by JdbcSchema. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.SqlAbstractDateTimeLiteral;
import org.apache.calcite.sql.SqlAlienSystemTypeNameSpec;
import org.apache.calcite.sql.SqlBasicTypeNameSpec;
Expand All @@ -31,11 +35,16 @@
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.fun.SqlFloorFunction;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeName;

import org.checkerframework.checker.nullness.qual.Nullable;

import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_FALSE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_FALSE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_TRUE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
import static org.apache.calcite.util.RelToSqlConverterUtil.unparseHiveTrim;

/**
Expand Down Expand Up @@ -93,6 +102,65 @@ public StarRocksSqlDialect(Context context) {
return true;
}

@Override public RexNode prepareUnparse(RexNode arg) {
if (!(arg instanceof RexCall)) {
return arg;
}
RexCall call = (RexCall) arg;
switch (call.getOperator().getKind()) {
case IS_FALSE:
RexNode operandIsFalse = call.operands.get(0);
if (RexUtil.isDeterministic(operandIsFalse)) {
// A IS FALSE -> A IS NOT NULL AND NOT A
RexNode isNotNullFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operandIsFalse);
RexNode notFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.NOT, operandIsFalse);
return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.AND, isNotNullFunc, notFunc);
} else {
throw new UnsupportedOperationException("Unsupported unparse: "
+ call.getOperator().getName());
}
case IS_NOT_FALSE:
RexNode operandIsNotFalse = call.operands.get(0);
if (RexUtil.isDeterministic(operandIsNotFalse)) {
// A IS NOT FALSE -> A IS NULL OR A
RexNode isNullFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NULL, operandIsNotFalse);
return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.OR, isNullFunc, operandIsNotFalse);
} else {
throw new UnsupportedOperationException("Unsupported unparse: "
+ call.getOperator().getName());
}
case IS_TRUE:
RexNode operandIsTrue = call.operands.get(0);
if (RexUtil.isDeterministic(operandIsTrue)) {
// A IS TRUE -> A IS NOT NULL AND A
RexNode isNotNullFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operandIsTrue);
return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.AND, isNotNullFunc, operandIsTrue);
} else {
throw new UnsupportedOperationException("Unsupported unparse: "
+ call.getOperator().getName());
}
case IS_NOT_TRUE:
RexNode operandIsNotTrue = call.operands.get(0);
if (RexUtil.isDeterministic(operandIsNotTrue)) {
// A IS NOT TRUE -> A IS NULL OR NOT A
RexNode isNullFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.IS_NULL, operandIsNotTrue);
RexNode notFunc =
RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.NOT, operandIsNotTrue);
return RexBuilder.DEFAULT.makeCall(SqlStdOperatorTable.OR, isNullFunc, notFunc);
} else {
throw new UnsupportedOperationException("Unsupported unparse: "
+ call.getOperator().getName());
}
default:
return super.prepareUnparse(arg);
}
}

@Override public void unparseCall(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
switch (call.getKind()) {
case ARRAY_VALUE_CONSTRUCTOR:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ private static String toSql(RelNode root, SqlDialect dialect,
+ "WHERE `product_id` > 0\n"
+ "GROUP BY `product_id`";
final String expectedStarRocks = "SELECT"
+ " SUM(CASE WHEN `net_weight` > 0E0 IS TRUE"
+ " SUM(CASE WHEN `net_weight` > 0E0 IS NOT NULL AND `net_weight` > 0E0"
+ " THEN `shelf_width` ELSE NULL END), SUM(`shelf_width`)\n"
+ "FROM `foodmart`.`product`\n"
+ "WHERE `product_id` > 0\n"
Expand Down Expand Up @@ -8492,6 +8492,94 @@ private void checkLiteral2(String expression, String expected) {
.ok(expected);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6835">[CALCITE-6835]
* Invalid unparse for IS TRUE,IS FALSE,IS NOT TRUE and IS NOT FALSE
* in StarRocksDialect</a>. */
@Test void testIsTrue() {
final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS TRUE";
final String expected = "SELECT *\n"
+ "FROM \"SCOTT\".\"EMP\"\n"
+ "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS TRUE";
String expectedStarRocks = "SELECT *\n"
+ "FROM `SCOTT`.`EMP`\n"
+ "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NOT NULL AND CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
sql(sql)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.ok(expected)
.withStarRocks().ok(expectedStarRocks);

final String sqlNoDeterministic =
"SELECT * FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS TRUE";
sql(sqlNoDeterministic)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.withStarRocks()
.throws_("Unsupported unparse: IS TRUE");
}

@Test void testIsNotTrue() {
final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS NOT TRUE";
final String expected = "SELECT *\n"
+ "FROM \"SCOTT\".\"EMP\"\n"
+ "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS NOT TRUE";
String expectedStarRocks = "SELECT *\n"
+ "FROM `SCOTT`.`EMP`\n"
+ "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NULL OR NOT CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
sql(sql)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.ok(expected)
.withStarRocks().ok(expectedStarRocks);

final String sqlNoDeterministic = "SELECT * \n"
+ "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS NOT TRUE";
sql(sqlNoDeterministic)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.withStarRocks()
.throws_("Unsupported unparse: IS NOT TRUE");
}

@Test void testIsFalse() {
final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS FALSE";
final String expected = "SELECT *\n"
+ "FROM \"SCOTT\".\"EMP\"\n"
+ "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS FALSE";
String expectedStarRocks = "SELECT *\n"
+ "FROM `SCOTT`.`EMP`\n"
+ "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NOT NULL AND NOT CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
sql(sql)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.ok(expected)
.withStarRocks().ok(expectedStarRocks);

final String sqlNoDeterministic = "SELECT * \n"
+ "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS FALSE";
sql(sqlNoDeterministic)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.withStarRocks()
.throws_("Unsupported unparse: IS FALSE");
}

@Test void testIsNotFalse() {
final String sql = "SELECT * FROM \"EMP\" WHERE \"COMM\" > 0 IS NOT FALSE";
final String expected = "SELECT *\n"
+ "FROM \"SCOTT\".\"EMP\"\n"
+ "WHERE CAST(\"COMM\" AS DECIMAL(12, 2)) > 0.00 IS NOT FALSE";
String expectedStarRocks = "SELECT *\n"
+ "FROM `SCOTT`.`EMP`\n"
+ "WHERE CAST(`COMM` AS DECIMAL(12, 2)) > 0.00 IS NULL OR CAST(`COMM` AS DECIMAL(12, 2)) > 0.00";
sql(sql)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.ok(expected)
.withStarRocks().ok(expectedStarRocks);

final String sqlNoDeterministic = "SELECT * \n"
+ "FROM \"EMP\" WHERE \"COMM\" > RAND_INTEGER(10) IS NOT FALSE";
sql(sqlNoDeterministic)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.withStarRocks()
.throws_("Unsupported unparse: IS NOT FALSE");
}

@Test void testMerge() {
final String sql1 = "merge into \"DEPT\" as \"t\"\n"
+ "using \"DEPT\" as \"s\"\n"
Expand Down