diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 2a26a78929cf..272721ab9ff9 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -284,7 +284,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
}
return type;
}),
- null,
+ InferTypes.RETURN_TYPE,
OperandTypes.STRING_SAME_SAME_OR_ARRAY_SAME_SAME);
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index e25c6f31ccf2..45b980b1ebae 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -5967,6 +5967,17 @@ private CalciteAssert.AssertQuery withEmpDept(String sql) {
});
}
+ /** Test case for
+ * [CALCITE-7407]
+ * Illegal use of dynamic parameter with ||. */
+ @Test void testPreparedStatementConcatDynamicParameter() {
+ CalciteAssert.hr()
+ .query("select \"name\" from \"hr\".\"emps\"\n"
+ + "where \"name\" = (? || 'odore')")
+ .consumesPreparedStatement(p -> p.setString(1, "The"))
+ .returns("name=Theodore\n");
+ }
+
/** Test case for
* [CALCITE-2061]
* Dynamic parameters in offset/fetch. */
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index dd66362819a7..f6d26c6af7ed 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -822,6 +822,10 @@ static SqlOperatorTable operatorTableFor(SqlLibrary library) {
.columnType("CHAR(6) NOT NULL");
expr("'a'||'b'||cast('cde' as VARCHAR(3))|| 'f'")
.columnType("VARCHAR(6) NOT NULL");
+ expr("null||'b'")
+ .columnType("VARCHAR");
+ expr("cast(null as ANY)||cast(null as ANY)")
+ .columnType("ANY");
expr("_UTF16'a'||_UTF16'b'||_UTF16'c'").ok();
}
@@ -8287,6 +8291,24 @@ void testGroupExpressionEquivalenceParams() {
sql("select 1 from emp having sum(sal) < ?").ok();
}
+ /** Test case for
+ * [CALCITE-7407]
+ * Illegal use of dynamic parameter with ||. */
+ @Test void testBindConcat() {
+ sql("select * from emp where ename = (? || 'KI')").ok();
+ sql("select * from emp where ename = ('SM' || ?)").ok();
+ }
+
+ /** Test case for
+ * [CALCITE-7407]
+ * Illegal use of dynamic parameter with ||. */
+ @Test void testConcatNullAndAnyWithContext() {
+ sql("select * from emp where ename = (null || 'KI')").ok();
+ sql("select * from emp where ename = ('SM' || null)").ok();
+ sql("select * from emp where ename = (cast(null as any) || 'KI')").ok();
+ sql("select * from emp where ename = ('SM' || cast(null as any))").ok();
+ }
+
/** Test case for
* [CALCITE-1310]
* Infer type of arguments to BETWEEN operator. */