From b55778a6a34e6b315debe5701b8bf2595be6d611 Mon Sep 17 00:00:00 2001 From: Jerome Haltom Date: Sat, 12 Sep 2026 10:10:13 -0500 Subject: [PATCH] [CALCITE-7777] JDBC adapter generates || for concatenation, which SQL Server rejects --- .../org/apache/calcite/sql/dialect/MssqlSqlDialect.java | 5 +++++ .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java index 964fa03f8f91..7a45559284a0 100644 --- a/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java +++ b/core/src/main/java/org/apache/calcite/sql/dialect/MssqlSqlDialect.java @@ -212,6 +212,11 @@ private static SqlNode createDatetimeCastSpec(String typeAlias, RelDataType type final SqlWriter.Frame frame = writer.startFunCall("CEILING"); call.operand(0).unparse(writer, leftPrec, rightPrec); writer.endFunCall(frame); + } else if (call.getOperator().equals(SqlStdOperatorTable.CONCAT)) { + // MSSQL has no || operator. + concatenates and propagates NULL, as || + // does; the CONCAT function does not, reading a NULL operand as ''. + SqlSyntax.BINARY.unparse(writer, SqlStdOperatorTable.PLUS, call, leftPrec, + rightPrec); } else { switch (call.getKind()) { case FLOOR: diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java index 877a1e99ee62..8e43c4400da0 100644 --- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java +++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java @@ -11820,6 +11820,15 @@ private void checkLiteral2(String expression, String expected) { sql(query).dialect(MssqlSqlDialect.DEFAULT).ok(mssqlExpected); } + /** Test case for + * [CALCITE-7777] + * JDBC adapter generates || for concatenation, which SQL Server rejects. */ + @Test void testConcatOperatorEmulationForMSSQL() { + final String query = "select \"brand_name\" || \"product_name\" from \"product\""; + final String mssqlExpected = "SELECT [brand_name] + [product_name]\n" + + "FROM [foodmart].[product]"; + sql(query).withMssql().ok(mssqlExpected); + } /** Test case for * [CALCITE-6655]