This is an automated email from the ASF dual-hosted git repository.
xiong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new ace3afeafb [CALCITE-6450] Postgres CONCAT_WS function throws exception
when parameter type is (<CHAR(1)>, <INTEGER ARRAY>)
ace3afeafb is described below
commit ace3afeafb3212dc96fb00f72ccd1632e355036e
Author: YiwenWu <[email protected]>
AuthorDate: Sat Jul 6 17:17:57 2024 +0800
[CALCITE-6450] Postgres CONCAT_WS function throws exception when parameter
type is (<CHAR(1)>, <INTEGER ARRAY>)
---
.../calcite/adapter/enumerable/RexImpTable.java | 4 +
.../org/apache/calcite/runtime/SqlFunctions.java | 13 +++
.../main/java/org/apache/calcite/sql/SqlKind.java | 3 +
.../calcite/sql/fun/SqlLibraryOperators.java | 28 +++++-
.../org/apache/calcite/sql/type/OperandTypes.java | 111 +++++++++++++++------
.../org/apache/calcite/util/BuiltInMethod.java | 2 +
.../org/apache/calcite/test/SqlFunctionsTest.java | 14 +++
site/_docs/reference.md | 5 +-
.../org/apache/calcite/test/SqlOperatorTest.java | 26 +++++
9 files changed, 169 insertions(+), 37 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index da7ef8eaf6..d8a3bf6f34 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -164,6 +164,7 @@ import static
org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_FUNCTION;
import static
org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_FUNCTION_WITH_NULL;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_WS;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_WS_MSSQL;
+import static
org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_WS_POSTGRESQL;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_WS_SPARK;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONTAINS_SUBSTR;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.COSH;
@@ -588,6 +589,9 @@ public class RexImpTable {
defineMethod(CONCAT_WS,
BuiltInMethod.MULTI_STRING_CONCAT_WITH_SEPARATOR.method,
NullPolicy.ARG0);
+ defineMethod(CONCAT_WS_POSTGRESQL,
+ BuiltInMethod.MULTI_TYPE_OBJECT_CONCAT_WITH_SEPARATOR.method,
+ NullPolicy.ARG0);
defineMethod(CONCAT_WS_MSSQL,
BuiltInMethod.MULTI_STRING_CONCAT_WITH_SEPARATOR.method,
NullPolicy.NONE);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 7a5dc3e3e6..b41990060b 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -136,6 +136,7 @@ import static org.apache.calcite.util.Static.RESOURCE;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
/**
* Helper methods to implement SQL functions in generated code.
@@ -1556,6 +1557,18 @@ public class SqlFunctions {
return sb.toString();
}
+ /** SQL {@code CONCAT_WS(sep[, any]+)} function,
+ * return null for null sep. */
+ public static String concatMultiObjectWithSeparator(String sep, Object...
args) {
+ if (args.length == 0) {
+ return "";
+ }
+ return Arrays.stream(args)
+ .filter(Objects::nonNull)
+ .map(Object::toString)
+ .collect(joining(sep));
+ }
+
/** SQL {@code CONVERT(s, src_charset, dest_charset)} function. */
public static String convertWithCharset(String s, String srcCharset,
String destCharset) {
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 808c85c368..77279f3ac1 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -442,6 +442,9 @@ public enum SqlKind {
/** The {@code CONCAT_WS} function (MSSQL). */
CONCAT_WS_MSSQL,
+ /** The {@code CONCAT_WS} function (Postgresql). */
+ CONCAT_WS_POSTGRESQL,
+
/** The {@code CONCAT_WS} function (Spark). */
CONCAT_WS_SPARK,
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 061a1025ae..26db9029c2 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -71,7 +71,8 @@ import static
org.apache.calcite.sql.fun.SqlLibrary.POSTGRESQL;
import static org.apache.calcite.sql.fun.SqlLibrary.REDSHIFT;
import static org.apache.calcite.sql.fun.SqlLibrary.SNOWFLAKE;
import static org.apache.calcite.sql.fun.SqlLibrary.SPARK;
-import static
org.apache.calcite.sql.type.OperandTypes.STRING_FIRST_STRING_ARRAY_OPTIONAL;
+import static
org.apache.calcite.sql.type.OperandTypes.STRING_FIRST_OBJECT_REPEAT;
+import static
org.apache.calcite.sql.type.OperandTypes.STRING_FIRST_STRING_ARRAY_REPEAT;
import static org.apache.calcite.util.Static.RESOURCE;
import static java.util.Objects.requireNonNull;
@@ -1079,7 +1080,7 @@ public abstract class SqlLibraryOperators {
.withOperandTypeInference(InferTypes.RETURN_TYPE)
.withKind(SqlKind.CONCAT2);
- /** The "CONCAT_WS(separator, arg1, ...)" function (MySQL, Postgres);
+ /** The "CONCAT_WS(separator, arg1, ...)" function (MySQL);
* concatenates strings with separator, and treats null arguments as empty
* strings. For example:
*
@@ -1094,7 +1095,7 @@ public abstract class SqlLibraryOperators {
* <p>If all the arguments except the separator are null,
* it also returns the empty string.
* For example, {@code CONCAT_WS(',', null, null)} returns "". */
- @LibraryOperator(libraries = {MYSQL, POSTGRESQL}, exceptLibraries =
{REDSHIFT})
+ @LibraryOperator(libraries = {MYSQL})
public static final SqlFunction CONCAT_WS =
SqlBasicFunction.create("CONCAT_WS",
ReturnTypes.MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_ARG0_NULLABLE,
@@ -1103,6 +1104,25 @@ public abstract class SqlLibraryOperators {
SqlFunctionCategory.STRING)
.withOperandTypeInference(InferTypes.RETURN_TYPE);
+ /** The "CONCAT_WS(separator, arg1, ...)" function (Postgres).
+ *
+ * <p>Differs from {@link #CONCAT_WS} (MySQL) in that its arg1 can be of any
type,
+ * not limited to string. For example:
+ *
+ * <ul>
+ * <li>{@code CONCAT_WS(',', 'a')} returns "{@code a}";
+ * <li>{@code CONCAT_WS(',', 'a', DATE '1945-02-24')} returns "{@code
a,1945-02-24}";
+ * <li>{@code CONCAT_WS(',', 'a', ARRAY['b', 'c'])} returns "{@code a,[b,
c]}".
+ * </ul> */
+ @LibraryOperator(libraries = {POSTGRESQL}, exceptLibraries = {REDSHIFT})
+ public static final SqlFunction CONCAT_WS_POSTGRESQL =
+ SqlBasicFunction.create("CONCAT_WS",
+
ReturnTypes.MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_ARG0_NULLABLE,
+ STRING_FIRST_OBJECT_REPEAT,
+ SqlFunctionCategory.STRING)
+ .withOperandTypeInference(InferTypes.RETURN_TYPE)
+ .withKind(SqlKind.CONCAT_WS_POSTGRESQL);
+
/** The "CONCAT_WS(separator, arg1, arg2, ...)" function in (MSSQL).
*
* <p>Differs from {@link #CONCAT_WS} (MySQL, Postgres) in that it accepts
@@ -1140,7 +1160,7 @@ public abstract class SqlLibraryOperators {
public static final SqlFunction CONCAT_WS_SPARK =
SqlBasicFunction.create("CONCAT_WS",
ReturnTypes.MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_ARG0_NULLABLE,
- STRING_FIRST_STRING_ARRAY_OPTIONAL,
+ STRING_FIRST_STRING_ARRAY_REPEAT,
SqlFunctionCategory.STRING)
.withOperandTypeInference(InferTypes.RETURN_TYPE)
.withKind(SqlKind.CONCAT_WS_SPARK);
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index f629a00e0a..c9c8c1023f 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -35,6 +35,8 @@ import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.calcite.sql.validate.SqlLambdaScope;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.sql.validate.implicit.AbstractTypeCoercion;
+import org.apache.calcite.sql.validate.implicit.TypeCoercion;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
@@ -596,50 +598,63 @@ public abstract class OperandTypes {
};
/** Operand type-checking strategy that the first argument is of string type,
- * and the remaining arguments can be of string or array of string type. */
- public static final SqlOperandTypeChecker STRING_FIRST_STRING_ARRAY_OPTIONAL
=
- new SqlOperandTypeChecker() {
- @Override public boolean checkOperandTypes(
- SqlCallBinding binding,
+ * and the remaining arguments can be any type. */
+ public static final SqlOperandTypeChecker STRING_FIRST_OBJECT_REPEAT =
+ new StringFirstAndRepeatOperandTypeChecker(
+ SqlOperandCountRanges.from(2), "(<STRING>(,<ANY>)+)") {
+ @Override public boolean checkRepeatOperandTypes(SqlCallBinding
callBinding,
boolean throwOnFailure) {
- // Check first operand is String type
- if (!STRING.checkSingleOperandType(binding, binding.operand(0), 0,
throwOnFailure)) {
- return false;
+ ImmutableList.Builder<SqlTypeFamily> builder =
ImmutableList.builder();
+ for (int i = 0; i < callBinding.getOperandCount(); i++) {
+ TypeCoercion coercion =
callBinding.getValidator().getTypeCoercion();
+ RelDataType operandType = callBinding.getOperandType(i);
+ RelDataType cast =
+ ((AbstractTypeCoercion) coercion).implicitCast(operandType,
SqlTypeFamily.STRING);
+ SqlTypeFamily family =
+ cast != null ? SqlTypeFamily.STRING
+ : operandType.getSqlTypeName().getFamily();
+ assert family != null;
+ builder.add(family);
}
+ ImmutableList<SqlTypeFamily> families = builder.build();
+ return family(families).checkOperandTypes(callBinding,
throwOnFailure);
+ }
+ };
+
+ /** Operand type-checking strategy that the first argument is of string type,
+ * and the remaining arguments can be of string or array of string type. */
+ public static final SqlOperandTypeChecker STRING_FIRST_STRING_ARRAY_REPEAT =
+ new StringFirstAndRepeatOperandTypeChecker(
+ SqlOperandCountRanges.from(1), "(<STRING>[,<STRING> |
ARRAY<STRING>]+)") {
+ @Override public boolean checkRepeatOperandTypes(SqlCallBinding
callBinding,
+ boolean throwOnFailure) {
// Check Array element is String type
- if (!checkArrayString(binding, throwOnFailure)) {
+ if (!checkArrayString(callBinding, throwOnFailure)) {
return false;
}
// Check Operand Types with Type Coercion
- return checkFamilyOperandTypes(binding, throwOnFailure);
- }
-
- @Override public SqlOperandCountRange getOperandCountRange() {
- return SqlOperandCountRanges.any();
+ return checkFamilyOperandTypes(callBinding, throwOnFailure);
}
- @Override public String getAllowedSignatures(SqlOperator op, String
opName) {
- return opName + "(<STRING>(,[<STRING> | ARRAY<STRING>]))";
- }
-
- private boolean checkFamilyOperandTypes(SqlCallBinding binding,
boolean throwOnFailure) {
+ private boolean checkFamilyOperandTypes(SqlCallBinding callBinding,
+ boolean throwOnFailure) {
ImmutableList.Builder<SqlTypeFamily> builder =
ImmutableList.builder();
- for (int i = 0; i < binding.getOperandCount(); i++) {
- SqlTypeFamily family = SqlTypeFamily.STRING;
- if (binding.getOperandType(i).getSqlTypeName() ==
SqlTypeName.ARRAY) {
- family = SqlTypeFamily.ARRAY;
- }
+ for (int i = 0; i < callBinding.getOperandCount(); i++) {
+ boolean isArray = callBinding.getOperandType(i).getSqlTypeName()
== SqlTypeName.ARRAY;
+ SqlTypeFamily family = isArray ? SqlTypeFamily.ARRAY :
SqlTypeFamily.STRING;
builder.add(family);
}
ImmutableList<SqlTypeFamily> families = builder.build();
- return family(families).checkOperandTypes(binding, throwOnFailure);
+ return family(families).checkOperandTypes(callBinding,
throwOnFailure);
}
private boolean checkArrayString(SqlCallBinding binding, boolean
throwOnFailure) {
for (int i = 0; i < binding.getOperandCount(); i++) {
RelDataType type = binding.getOperandType(i);
- if (type.getSqlTypeName() == SqlTypeName.ARRAY
- && !isString(((ArraySqlType) type).getComponentType())) {
+ RelDataType componentType = type.getComponentType();
+ boolean isString = componentType != null
+ && (SqlTypeUtil.isNull(componentType) ||
SqlTypeUtil.isString(componentType));
+ if (type.getSqlTypeName() == SqlTypeName.ARRAY && !isString) {
if (throwOnFailure) {
throw binding.newValidationSignatureError();
}
@@ -648,12 +663,46 @@ public abstract class OperandTypes {
}
return true;
}
-
- private boolean isString(RelDataType type) {
- return SqlTypeUtil.isNull(type) || SqlTypeUtil.isString(type);
- }
};
+ /** Operand type-checking strategy where the first argument is of string
type,
+ * and the remaining arguments follow a repeating type pattern.
+ *
+ * <p>The method {@link #checkRepeatOperandTypes} is an abstract method
designed
+ * to check the types of these repeating arguments. */
+ abstract static class StringFirstAndRepeatOperandTypeChecker implements
SqlOperandTypeChecker {
+
+ private final SqlOperandCountRange countRange;
+ private final String signatures;
+
+ StringFirstAndRepeatOperandTypeChecker(
+ SqlOperandCountRange countRange,
+ String signatures) {
+ this.countRange = countRange;
+ this.signatures = signatures;
+ }
+
+ @Override public boolean checkOperandTypes(SqlCallBinding callBinding,
boolean throwOnFailure) {
+ // Check first operand is String type
+ if (!STRING.checkSingleOperandType(callBinding, callBinding.operand(0),
0,
+ throwOnFailure)) {
+ return false;
+ }
+ return checkRepeatOperandTypes(callBinding, throwOnFailure);
+ }
+
+ @Override public SqlOperandCountRange getOperandCountRange() {
+ return countRange;
+ }
+
+ @Override public String getAllowedSignatures(SqlOperator op, String
opName) {
+ return opName + signatures;
+ }
+
+ public abstract boolean checkRepeatOperandTypes(SqlCallBinding callBinding,
+ boolean throwOnFailure);
+ }
+
/** Checks that returns whether a value is a multiset or an array.
* Cf Java, where list and set are collections but a map is not. */
public static final SqlSingleOperandTypeChecker COLLECTION =
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 0e0a3e5390..d90f00222a 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -483,6 +483,8 @@ public enum BuiltInMethod {
"concatMultiWithSeparator", String[].class),
MULTI_TYPE_STRING_ARRAY_CONCAT_WITH_SEPARATOR(SqlFunctions.class,
"concatMultiTypeWithSeparator", String.class, Object[].class),
+ MULTI_TYPE_OBJECT_CONCAT_WITH_SEPARATOR(SqlFunctions.class,
+ "concatMultiObjectWithSeparator", String.class, Object[].class),
FLOOR_DIV(Math.class, "floorDiv", long.class, long.class),
FLOOR_MOD(Math.class, "floorMod", long.class, long.class),
ADD_MONTHS(DateTimeUtils.class, "addMonths", long.class, int.class),
diff --git a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
index 9ede05ec95..c2dab83304 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
@@ -44,6 +44,7 @@ import static
org.apache.calcite.runtime.SqlFunctions.arraysOverlap;
import static org.apache.calcite.runtime.SqlFunctions.charLength;
import static org.apache.calcite.runtime.SqlFunctions.concat;
import static org.apache.calcite.runtime.SqlFunctions.concatMulti;
+import static
org.apache.calcite.runtime.SqlFunctions.concatMultiObjectWithSeparator;
import static
org.apache.calcite.runtime.SqlFunctions.concatMultiTypeWithSeparator;
import static org.apache.calcite.runtime.SqlFunctions.concatMultiWithNull;
import static org.apache.calcite.runtime.SqlFunctions.concatMultiWithSeparator;
@@ -246,6 +247,19 @@ class SqlFunctionsTest {
is("11,11,12,12,13,13"));
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6450">[CALCITE-6450]
+ * Postgres CONCAT_WS function </a>. */
+ @Test void testConcatMultiObjectWithSeparator() {
+ assertThat(concatMultiObjectWithSeparator("a"), is(""));
+ assertThat(concatMultiObjectWithSeparator(",", "a b", "cd"), is("a b,cd"));
+ assertThat(concatMultiObjectWithSeparator(",", "a", 1, Arrays.asList("b",
"c")),
+ is("a,1,[b, c]"));
+ assertThat(concatMultiObjectWithSeparator(",", "a", 1, Arrays.asList("b",
"c"), null),
+ is("a,1,[b, c]"));
+ assertThat(concatMultiObjectWithSeparator("abc", null, null), is(""));
+ }
+
@Test void testPosixRegex() {
final SqlFunctions.PosixRegexFunction f =
new SqlFunctions.PosixRegexFunction();
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 3e0a51eb45..b1567c8da7 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2729,9 +2729,10 @@ In the following:
| o r | CONCAT(string, string) | Concatenates two
strings, returns null only when both string arguments are null, otherwise
treats null as empty string
| b m | CONCAT(string [, string ]*) | Concatenates one or
more strings, returns null if any of the arguments is null
| p q | CONCAT(string [, string ]*) | Concatenates one or
more strings, null is treated as empty string
-| m p | CONCAT_WS(separator, str1 [, string ]*) | Concatenates one or
more strings, returns null only when separator is null, otherwise treats null
arguments as empty strings
+| m | CONCAT_WS(separator, str1 [, string ]*) | Concatenates one or
more strings, returns null only when separator is null, otherwise treats null
arguments as empty strings
+| p | CONCAT_WS(separator, any [, any ]*) | Concatenates all but
the first argument, returns null only when separator is null, otherwise treats
null arguments as empty strings
| q | CONCAT_WS(separator, str1, str2 [, string ]*) | Concatenates two or
more strings, requires at least 3 arguments (up to 254), treats null arguments
as empty strings
-| s | CONCAT_WS(separator [, string | array(string)]+) | Concatenates one or
more strings or arrays. Besides the separator, other arguments can include
strings or string arrays. returns null only when separator is null, treats
other null arguments as empty strings
+| s | CONCAT_WS(separator [, string | array(string)]*) | Concatenates one or
more strings or arrays. Besides the separator, other arguments can include
strings or string arrays. returns null only when separator is null, treats
other null arguments as empty strings
| m | COMPRESS(string) | Compresses a string
using zlib compression and returns the result as a binary string
| b | CONTAINS_SUBSTR(expression, string [ , json_scope => json_scope_value
]) | Returns whether *string* exists as a substring in *expression*. Optional
*json_scope* argument specifies what scope to search if *expression* is in JSON
format. Returns NULL if a NULL exists in *expression* that does not result in a
match
| q | CONVERT(type, expression [ , style ]) | Equivalent to
`CAST(expression AS type)`; ignores the *style* operand
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 65e1a1cb96..8b94908fbd 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -2432,6 +2432,7 @@ public class SqlOperatorTest {
final SqlOperatorFixture f = fixture();
checkConcatWithSeparator(f.withLibrary(SqlLibrary.MYSQL));
checkConcatWithSeparator(f.withLibrary(SqlLibrary.POSTGRESQL));
+ checkConcatWithSeparatorInPostgres(f.withLibrary(SqlLibrary.POSTGRESQL));
checkConcatWithSeparatorInMSSQL(f.withLibrary(SqlLibrary.MSSQL));
}
@@ -2456,6 +2457,29 @@ public class SqlOperatorTest {
f.checkString("concat_ws('', '', '', '')", "", "VARCHAR(0) NOT NULL");
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6450">[CALCITE-6450]
+ * Postgres CONCAT_WS function </a>. */
+ private static void checkConcatWithSeparatorInPostgres(SqlOperatorFixture f)
{
+ f.setFor(SqlLibraryOperators.CONCAT_WS_POSTGRESQL);
+ f.checkFails("^concat_ws(array['a'])^", INVALID_ARGUMENTS_NUMBER, false);
+ f.checkFails("^concat_ws(array['a'], 'd')^",
INVALID_ARGUMENTS_TYPE_VALIDATION_ERROR, false);
+ f.checkString("concat_ws(',', 'a', array['b', 'c'])", "a,[b, c]", "VARCHAR
NOT NULL");
+ f.checkString("concat_ws(',', 'a', 1, 4)", "a,1,4", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', 'a', array['b', 'c'], null, array[1,2])",
+ "a,[b, c],[1, 2]", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', DATE '1945-02-24')",
+ "1945-02-24", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', 'a', array['b', 'c'], DATE '1945-02-24')",
+ "a,[b, c],1945-02-24", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', timestamp '2024-07-06 12:15:48.678')",
+ "2024-07-06 12:15:48", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', time '12:34:56', time '13:00:00', 2, 'abc')",
+ "12:34:56,13:00:00,2,abc", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', null, null)", "", "VARCHAR NOT NULL");
+ f.checkNull("concat_ws(null, 'a', array['b', 'c'])");
+ }
+
private static void checkConcatWithSeparatorInMSSQL(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.CONCAT_WS_MSSQL);
f.checkString("concat_ws(',', 'a', 'b', null, 'c')", "a,b,c",
@@ -2483,6 +2507,7 @@ public class SqlOperatorTest {
final SqlOperatorFixture f = fixture()
.setFor(SqlLibraryOperators.CONCAT_WS_SPARK)
.withLibrary(SqlLibrary.SPARK);
+ f.checkString("concat_ws(',')", "", "VARCHAR(0) NOT NULL");
f.checkString("concat_ws(',', 'a')", "a", "VARCHAR(1) NOT NULL");
f.checkString("concat_ws(',', 'a', 'b', null, 'c')", "a,b,c",
"VARCHAR NOT NULL");
@@ -2515,6 +2540,7 @@ public class SqlOperatorTest {
INVALID_ARGUMENTS_TYPE_VALIDATION_ERROR, false);
f.checkFails("^concat_ws(array('a', 'b'))^",
INVALID_ARGUMENTS_TYPE_VALIDATION_ERROR, false);
+ f.checkFails("^concat_ws()^", INVALID_ARGUMENTS_NUMBER, false);
}
@Test void testModOperator() {