Skip to content

Commit

Permalink
[CALCITE-6819] MSSQL doesn't support TRUE/FALSE keywords in its Join …
Browse files Browse the repository at this point in the history
…predicate

In MSSQL dialect use 1=1 instead of TRUE
  • Loading branch information
sreeharshar84 committed Feb 7, 2025
1 parent c456f78 commit 903bf9c
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import org.apache.calcite.rel.core.Values;
import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.rel2sql.SqlImplementor;
import org.apache.calcite.rel.rel2sql.RelToSqlConverter;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexLiteral;
Expand Down Expand Up @@ -329,7 +329,7 @@ protected JdbcJoinRule(Config config) {
* Returns whether a condition is supported by {@link JdbcJoin}.
*
* <p>Corresponds to the capabilities of
* {@link SqlImplementor#convertConditionToSqlNode}.
* {@link RelToSqlConverter#convertConditionToSqlNode}.
*
* @param node Condition
* @return Whether condition is supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,26 @@ private static class AliasReplacementShuttle extends SqlShuttle {
}
}

/**
* Converts a {@link RexNode} condition into a {@link SqlNode}.
*
* @param node Join condition
* @param leftContext Left context
* @param rightContext Right context
*
* @return SqlNode that represents the condition
*/
public SqlNode convertConditionToSqlNode(RexNode node,
Context leftContext,
Context rightContext) {
if (node.isAlwaysTrue() || node.isAlwaysFalse()) {
return dialect.emulateJoinConditionTrueFalse(node);
}
final Context joinContext =
leftContext.implementor().joinContext(leftContext, rightContext);
return joinContext.toSql(null, node);
}

/** Visits a Join; called by {@link #dispatch} via reflection. */
public Result visit(Join e) {
switch (e.getJoinType()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,29 +316,6 @@ public Result setOpToSql(SqlSetOperator operator, RelNode rel) {
return result(node, clauses, rel, null);
}

/**
* Converts a {@link RexNode} condition into a {@link SqlNode}.
*
* @param node Join condition
* @param leftContext Left context
* @param rightContext Right context
*
* @return SqlNode that represents the condition
*/
public static SqlNode convertConditionToSqlNode(RexNode node,
Context leftContext,
Context rightContext) {
if (node.isAlwaysTrue()) {
return SqlLiteral.createBoolean(true, POS);
}
if (node.isAlwaysFalse()) {
return SqlLiteral.createBoolean(false, POS);
}
final Context joinContext =
leftContext.implementor().joinContext(leftContext, rightContext);
return joinContext.toSql(null, node);
}

/** Removes cast from string.
*
* <p>For example, {@code x > CAST('2015-01-07' AS DATE)}
Expand Down
12 changes: 12 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 @@ -943,6 +943,18 @@ public JoinType emulateJoinTypeForCrossJoin() {
return JoinType.COMMA;
}

/**
* Writes TRUE/FALSE or 1 = 1/ 1 <> 1 for certain

Check failure on line 947 in core/src/main/java/org/apache/calcite/sql/SqlDialect.java

View workflow job for this annotation

GitHub Actions / Linux (JDK 11, Avatica main)

[Task :core:javadoc] malformed HTML * Writes TRUE/FALSE or 1 = 1/ 1 <> 1 for certain ^

Check failure on line 947 in core/src/main/java/org/apache/calcite/sql/SqlDialect.java

View workflow job for this annotation

GitHub Actions / Linux (JDK 11, Avatica main)

[Task :core:javadoc] bad use of '>' * Writes TRUE/FALSE or 1 = 1/ 1 <> 1 for certain ^

Check failure on line 947 in core/src/main/java/org/apache/calcite/sql/SqlDialect.java

View workflow job for this annotation

GitHub Actions / macOS (JDK 21)

[Task :core:javadoc] malformed HTML * Writes TRUE/FALSE or 1 = 1/ 1 <> 1 for certain ^
* database variants (MSSQL, currently).
*/
public SqlNode emulateJoinConditionTrueFalse(RexNode node) {
if (node.isAlwaysTrue()) {
return SqlLiteral.createBoolean(true, SqlParserPos.ZERO);
} else {
return SqlLiteral.createBoolean(false, SqlParserPos.ZERO);
}
}

protected @Nullable SqlNode emulateNullDirectionWithIsNull(SqlNode node,
boolean nullsFirst, boolean desc) {
// No need for emulation if the nulls will anyways come out the way we want
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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.RexNode;
import org.apache.calcite.sql.SqlAbstractDateTimeLiteral;
import org.apache.calcite.sql.SqlBasicFunction;
import org.apache.calcite.sql.SqlCall;
Expand All @@ -33,6 +34,7 @@
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSyntax;
import org.apache.calcite.sql.SqlUtil;
Expand All @@ -43,6 +45,8 @@
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeName;

import com.google.common.collect.ImmutableList;

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

import static java.util.Objects.requireNonNull;
Expand Down Expand Up @@ -128,6 +132,18 @@ public MssqlSqlDialect(Context context) {
}
}

@Override public SqlNode emulateJoinConditionTrueFalse(RexNode node) {
final SqlParserPos pos = SqlParserPos.ZERO;
SqlNumericLiteral oneLiteral = SqlLiteral.createExactNumeric("1", pos);
if (node.isAlwaysTrue()) {
return SqlStdOperatorTable.EQUALS.createCall(pos,
ImmutableList.of(oneLiteral, oneLiteral));
} else {
return SqlStdOperatorTable.NOT_EQUALS.createCall(pos,
ImmutableList.of(oneLiteral, oneLiteral));
}
}

@Override public void unparseOffsetFetch(SqlWriter writer, @Nullable SqlNode offset,
@Nullable SqlNode fetch) {
if (!top && offset != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9093,7 +9093,35 @@ private void checkLiteral2(String expression, String expected) {
.dialect(MssqlSqlDialect.DEFAULT).ok(mssqlExpected);
}

/** Test case for
@Test void testMSSQLJoinAlwaysTrue() {
final String query = "SELECT \"hire_date\", \"department_description\" FROM \"employee\" "
+ "LEFT JOIN \"department\" ON TRUE";
final String mssqlExpected = "SELECT [employee].[hire_date],"
+ " [department].[department_description]\nFROM [foodmart].[employee]\nLEFT JOIN"
+ " [foodmart].[department] ON 1 = 1";
final String mysqlExpected = "SELECT `employee`.`hire_date`,"
+ " `department`.`department_description`\nFROM `foodmart`.`employee`\nLEFT JOIN"
+ " `foodmart`.`department` ON TRUE";
sql(query)
.dialect(MssqlSqlDialect.DEFAULT).ok(mssqlExpected)
.dialect(MysqlSqlDialect.DEFAULT).ok(mysqlExpected);
}

@Test void testMSSQLJoinAlwaysFalse() {
final String query = "SELECT \"hire_date\", \"department_description\" FROM \"employee\" "
+ "LEFT JOIN \"department\" ON False";
final String mssqlExpected = "SELECT [employee].[hire_date],"
+ " [department].[department_description]\nFROM [foodmart].[employee]\nLEFT JOIN"
+ " [foodmart].[department] ON 1 <> 1";
final String mysqlExpected = "SELECT `employee`.`hire_date`,"
+ " `department`.`department_description`\nFROM `foodmart`.`employee`\nLEFT JOIN"
+ " `foodmart`.`department` ON FALSE";
sql(query)
.dialect(MssqlSqlDialect.DEFAULT).ok(mssqlExpected)
.dialect(MysqlSqlDialect.DEFAULT).ok(mysqlExpected);
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6726">[CALCITE-6726]
* Add translation for MOD operator in MSSQL</a>.
*/
Expand Down

0 comments on commit 903bf9c

Please sign in to comment.