This is an automated email from the ASF dual-hosted git repository.
jhyde 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 9df9b536b7 [CALCITE-5741] Add CONCAT_WS function (enabled in MSSQL,
MySQL, Postgres libraries)
9df9b536b7 is described below
commit 9df9b536b77463229bc46322167667c36414a828
Author: ILuffZhe <[email protected]>
AuthorDate: Sun Jun 18 00:35:02 2023 +0800
[CALCITE-5741] Add CONCAT_WS function (enabled in MSSQL, MySQL, Postgres
libraries)
Close apache/calcite#3271
---
.../calcite/adapter/enumerable/RexImpTable.java | 11 +-
.../org/apache/calcite/runtime/SqlFunctions.java | 19 +++
.../main/java/org/apache/calcite/sql/SqlKind.java | 3 +
.../calcite/sql/fun/SqlLibraryOperators.java | 46 ++++++
.../org/apache/calcite/sql/type/ReturnTypes.java | 88 ++++++++++-
.../org/apache/calcite/util/BuiltInMethod.java | 8 +-
.../org/apache/calcite/test/SqlFunctionsTest.java | 14 ++
core/src/test/resources/sql/functions.iq | 167 +++++++++++++++++++++
site/_docs/reference.md | 12 +-
.../org/apache/calcite/test/SqlOperatorTest.java | 54 ++++++-
10 files changed, 410 insertions(+), 12 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 ec7c628262..f5a27adb3f 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
@@ -148,6 +148,8 @@ import static
org.apache.calcite.sql.fun.SqlLibraryOperators.COMPRESS;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT2;
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.COSH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.COTH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.CSC;
@@ -501,7 +503,14 @@ public class RexImpTable {
NullPolicy.STRICT);
defineMethod(CONCAT_FUNCTION_WITH_NULL,
BuiltInMethod.MULTI_STRING_CONCAT_WITH_NULL.method, NullPolicy.NONE);
- defineMethod(CONCAT2, BuiltInMethod.STRING_CONCAT_WITH_NULL.method,
NullPolicy.ALL);
+ defineMethod(CONCAT2, BuiltInMethod.STRING_CONCAT_WITH_NULL.method,
+ NullPolicy.ALL);
+ defineMethod(CONCAT_WS,
+ BuiltInMethod.MULTI_STRING_CONCAT_WITH_SEPARATOR.method,
+ NullPolicy.ARG0);
+ defineMethod(CONCAT_WS_MSSQL,
+ BuiltInMethod.MULTI_STRING_CONCAT_WITH_SEPARATOR.method,
+ NullPolicy.NONE);
defineMethod(OVERLAY, BuiltInMethod.OVERLAY.method, NullPolicy.STRICT);
defineMethod(POSITION, BuiltInMethod.POSITION.method, NullPolicy.STRICT);
defineMethod(ASCII, BuiltInMethod.ASCII.method, NullPolicy.STRICT);
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 e7473b4790..26990d03f6 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -802,6 +802,25 @@ public class SqlFunctions {
return sb.toString();
}
+ /** SQL {@code CONCAT_WS(sep, arg1, arg2, ...)} function;
+ * treats null arguments as empty strings. */
+ public static String concatMultiWithSeparator(String... args) {
+ // the separator arg could be null
+ final String sep = args[0] == null ? "" : args[0];
+ StringBuilder sb = new StringBuilder();
+ for (int i = 1; i < args.length; i++) {
+ if (args[i] != null) {
+ if (i < args.length - 1) {
+ sb.append(args[i]).append(sep);
+ } else {
+ // no separator after the last arg
+ sb.append(args[i]);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
/** 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 ed48b413bf..a49423d684 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -427,6 +427,9 @@ public enum SqlKind {
/** The {@code CONCAT} function (Postgresql and MSSQL) that ignores NULL. */
CONCAT_WITH_NULL,
+ /** The {@code CONCAT_WS} function (MSSQL). */
+ CONCAT_WS_MSSQL,
+
/** The "IF" function (BigQuery, Hive, Spark). */
IF,
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 fa9c4e4f85..4ceffd43e7 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
@@ -906,6 +906,52 @@ public abstract class SqlLibraryOperators {
.withOperandTypeInference(InferTypes.RETURN_TYPE)
.withKind(SqlKind.CONCAT2);
+ /** The "CONCAT_WS(separator, arg1, ...)" function (MySQL, Postgres);
+ * concatenates strings with separator, and treats null arguments as empty
+ * strings. For example:
+ *
+ * <ul>
+ * <li>{@code CONCAT_WS(',', 'a')} returns "{@code a}";
+ * <li>{@code CONCAT_WS(',', 'a', 'b')} returns "{@code a,b}".
+ * </ul>
+ *
+ * <p>Returns null if the separator arg is null.
+ * For example, {@code CONCAT_WS(null, 'a', 'b')} returns null.
+ *
+ * <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})
+ public static final SqlFunction CONCAT_WS =
+ SqlBasicFunction.create("CONCAT_WS",
+ ReturnTypes.MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_ARG0_NULLABLE,
+ OperandTypes.repeat(SqlOperandCountRanges.from(2),
+ OperandTypes.STRING),
+ SqlFunctionCategory.STRING)
+ .withOperandTypeInference(InferTypes.RETURN_TYPE);
+
+ /** The "CONCAT_WS(separator, arg1, arg2, ...)" function in (MSSQL).
+ *
+ * <p>Differs from {@link #CONCAT_WS} (MySQL, Postgres) in that it accepts
+ * between 3 and 254 arguments, and never returns null (even if the separator
+ * is null). For example:
+ *
+ * <ul>
+ * <li>{@code CONCAT_WS(',', 'a', 'b')} returns "{@code a,b}";
+ * <li>{@code CONCAT_WS(null, 'a', 'b')} returns "{@code ab}";
+ * <li>{@code CONCAT_WS(',', null, null)} returns "";
+ * <li>{@code CONCAT_WS(null, null, null)} returns "".
+ * </ul> */
+ @LibraryOperator(libraries = {MSSQL})
+ public static final SqlFunction CONCAT_WS_MSSQL =
+ SqlBasicFunction.create("CONCAT_WS",
+ ReturnTypes.MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_NOT_NULLABLE,
+ OperandTypes.repeat(SqlOperandCountRanges.between(3, 254),
+ OperandTypes.STRING),
+ SqlFunctionCategory.STRING)
+ .withOperandTypeInference(InferTypes.RETURN_TYPE)
+ .withKind(SqlKind.CONCAT_WS_MSSQL);
+
private static RelDataType arrayReturnType(SqlOperatorBinding opBinding) {
RelDataType type =
opBinding.getOperandCount() > 0
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index e2dffc5e89..901d3dc395 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -947,7 +947,7 @@ public abstract class ReturnTypes {
* <p>concat(cast('a' as varchar(2)), cast('b' as varchar(3)),cast('c' as
varchar(2)))
* returns varchar(7).
*
- * <p>concat(cast('a' as varchar), cast('b' as varchar(2), cast('c' as
varchar(2))))
+ * <p>concat(cast('a' as varchar), cast('b' as varchar(2)), cast('c' as
varchar(2)))
* returns varchar.
*
* <p>concat(cast('a' as varchar(65535)), cast('b' as varchar(2)), cast('c'
as varchar(2)))
@@ -984,6 +984,68 @@ public abstract class ReturnTypes {
.createSqlType(SqlTypeName.VARCHAR, typePrecision);
};
+ /**
+ * Type-inference strategy for String concatenation with separator.
+ * The precision of separator should be calculated during combining.
+ * Result is varying if either input is; otherwise fixed.
+ *
+ * <p>For example:
+ *
+ * <ul>
+ * <li>{@code concat_ws(',', cast('a' as varchar(2), cast('b' as
+ * varchar(3)), cast('c' as varchar(2)))}
+ * returns {@code varchar(9)};
+ *
+ * <li>{@code concat_ws(',', cast('a' as varchar), cast('b' as
+ * varchar(2)), cast('c' as varchar(2)))}
+ * returns {@code varchar};
+ *
+ * <li>{@code concat_ws(',', cast('a' as varchar(65535)), cast('b'
+ * as varchar(2)), cast('c' as varchar(2)))}
+ * returns {@code varchar}.
+ * </ul>
+ */
+ public static final SqlReturnTypeInference
MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION =
+ ReturnTypes::multivalentStringWithSepSumPrecision;
+
+ private static RelDataType multivalentStringWithSepSumPrecision(
+ SqlOperatorBinding opBinding) {
+ boolean hasPrecisionNotSpecifiedOperand = false;
+ boolean precisionOverflow = false;
+ int typePrecision = RelDataType.PRECISION_NOT_SPECIFIED;
+ long amount = 0;
+ List<RelDataType> operandTypes = opBinding.collectOperandTypes();
+ final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
+ final RelDataTypeSystem typeSystem = typeFactory.getTypeSystem();
+ int separatorPrecision = operandTypes.get(0).getPrecision();
+ // when separator's precision is not specified
+ if (separatorPrecision == typePrecision) {
+ return typeFactory.createSqlType(SqlTypeName.VARCHAR, typePrecision);
+ }
+ for (int i = 1; i < operandTypes.size(); i++) {
+ int operandPrecision = operandTypes.get(i).getPrecision();
+ amount = (long) operandPrecision + amount;
+ // separator's Precision shouldn't be added when encountering null value
+ // or the last string arg
+ if (operandPrecision >= 0 && i < operandTypes.size() - 1) {
+ amount = amount + separatorPrecision;
+ }
+ if (operandPrecision == RelDataType.PRECISION_NOT_SPECIFIED) {
+ hasPrecisionNotSpecifiedOperand = true;
+ break;
+ }
+ if (amount > typeSystem.getMaxPrecision(SqlTypeName.VARCHAR)) {
+ precisionOverflow = true;
+ break;
+ }
+ }
+ if (!(hasPrecisionNotSpecifiedOperand || precisionOverflow)) {
+ typePrecision = (int) amount;
+ }
+
+ return typeFactory.createSqlType(SqlTypeName.VARCHAR, typePrecision);
+ }
+
/**
* Same as {@link #MULTIVALENT_STRING_SUM_PRECISION} and using
* {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NULLABLE}.
@@ -996,14 +1058,34 @@ public abstract class ReturnTypes {
* {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NOT_NULLABLE}.
*/
public static final SqlReturnTypeInference
MULTIVALENT_STRING_SUM_PRECISION_NOT_NULLABLE =
-
MULTIVALENT_STRING_SUM_PRECISION.andThen(SqlTypeTransforms.TO_NOT_NULLABLE);
+ MULTIVALENT_STRING_SUM_PRECISION
+ .andThen(SqlTypeTransforms.TO_NOT_NULLABLE);
+
+ /**
+ * Same as {@link #MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION} and using
+ * {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NOT_NULLABLE}.
+ */
+ public static final SqlReturnTypeInference
+ MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_NOT_NULLABLE =
+ MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION
+ .andThen(SqlTypeTransforms.TO_NOT_NULLABLE);
+
+ /**
+ * Same as {@link #MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION} and using
+ * {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NULLABLE_ALL}.
+ */
+ public static final SqlReturnTypeInference
+ MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION_ARG0_NULLABLE =
+ MULTIVALENT_STRING_WITH_SEP_SUM_PRECISION
+ .andThen(SqlTypeTransforms.ARG0_NULLABLE);
/**
* Same as {@link #MULTIVALENT_STRING_SUM_PRECISION} and using
* {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NULLABLE_ALL}.
*/
public static final SqlReturnTypeInference
MULTIVALENT_STRING_SUM_PRECISION_NULLABLE_ALL =
-
MULTIVALENT_STRING_SUM_PRECISION.andThen(SqlTypeTransforms.TO_NULLABLE_ALL);
+ MULTIVALENT_STRING_SUM_PRECISION
+ .andThen(SqlTypeTransforms.TO_NULLABLE_ALL);
/**
* Same as {@link #DYADIC_STRING_SUM_PRECISION} and using
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 a0826d1a5d..6dda17513f 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -422,9 +422,13 @@ public enum BuiltInMethod {
OCTET_LENGTH(SqlFunctions.class, "octetLength", ByteString.class),
CHAR_LENGTH(SqlFunctions.class, "charLength", String.class),
STRING_CONCAT(SqlFunctions.class, "concat", String.class, String.class),
- STRING_CONCAT_WITH_NULL(SqlFunctions.class, "concatWithNull", String.class,
String.class),
+ STRING_CONCAT_WITH_NULL(SqlFunctions.class, "concatWithNull", String.class,
+ String.class),
MULTI_STRING_CONCAT(SqlFunctions.class, "concatMulti", String[].class),
- MULTI_STRING_CONCAT_WITH_NULL(SqlFunctions.class, "concatMultiWithNull",
String[].class),
+ MULTI_STRING_CONCAT_WITH_NULL(SqlFunctions.class, "concatMultiWithNull",
+ String[].class),
+ MULTI_STRING_CONCAT_WITH_SEPARATOR(SqlFunctions.class,
+ "concatMultiWithSeparator", String[].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 1b47ba7ec7..2dbce65720 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.charLength;
import static org.apache.calcite.runtime.SqlFunctions.concat;
import static org.apache.calcite.runtime.SqlFunctions.concatMulti;
import static org.apache.calcite.runtime.SqlFunctions.concatMultiWithNull;
+import static org.apache.calcite.runtime.SqlFunctions.concatMultiWithSeparator;
import static org.apache.calcite.runtime.SqlFunctions.concatWithNull;
import static org.apache.calcite.runtime.SqlFunctions.fromBase64;
import static org.apache.calcite.runtime.SqlFunctions.greater;
@@ -170,6 +171,19 @@ class SqlFunctionsTest {
assertThat(concatMultiWithNull("a", null, "b"), is("ab"));
}
+ @Test void testConcatMultiWithSeparator() {
+ assertThat(concatMultiWithSeparator(",", "a"), is("a"));
+ assertThat(concatMultiWithSeparator(",", "a b", "cd"), is("a b,cd"));
+ assertThat(concatMultiWithSeparator(",", "a b", null, "cd", null, "e"),
is("a b,cd,e"));
+ assertThat(concatMultiWithSeparator(",", null, null), is(""));
+ assertThat(concatMultiWithSeparator(",", "", ""), is(","));
+ assertThat(concatMultiWithSeparator("", "a", "b", null, "c"), is("abc"));
+ assertThat(concatMultiWithSeparator("", null, null), is(""));
+ // The separator could be null, and it is treated as empty string
+ assertThat(concatMultiWithSeparator(null, "a", "b", null, "c"), is("abc"));
+ assertThat(concatMultiWithSeparator(null, null, null), is(""));
+ }
+
@Test void testPosixRegex() {
assertThat(posixRegex("abc", "abc", true), is(true));
assertThat(posixRegex("abc", "^a", true), is(true));
diff --git a/core/src/test/resources/sql/functions.iq
b/core/src/test/resources/sql/functions.iq
index 2c0bf3cb0a..92dc4f15b9 100644
--- a/core/src/test/resources/sql/functions.iq
+++ b/core/src/test/resources/sql/functions.iq
@@ -103,6 +103,96 @@ from t;
!ok
+# [CALCITE-5741] Add CONCAT_WS function (enabled in MSSQL, MySQL, Postgres
libraries)
+# CONCAT_WS in Postgres and MySQL returns null only when the separator arg is
null.
+select concat_ws(',', 'a');
++--------+
+| EXPR$0 |
++--------+
+| a |
++--------+
+(1 row)
+
+!ok
+
+select concat_ws(',', 'a', 'b', 'c');
++--------+
+| EXPR$0 |
++--------+
+| a,b,c |
++--------+
+(1 row)
+
+!ok
+
+select concat_ws(',', 'a', cast(null as varchar), 'b');
++--------+
+| EXPR$0 |
++--------+
+| a,b |
++--------+
+(1 row)
+
+!ok
+
+select concat_ws(',', '', '', '');
++--------+
+| EXPR$0 |
++--------+
+| ,, |
++--------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws(',', '') as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws('', '', '') as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws(',', cast(null as varchar), cast(null as varchar))
as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws(cast(null as varchar), 'a', 'b') as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | true |
++---+-----------+
+(1 row)
+
+!ok
+
# Compression Functions
SELECT COMPRESS('sample');
@@ -514,6 +604,83 @@ select CONVERT(DATE, '05/01/2000', 103);
!ok
!}
+# CONCAT_WS in MSSQL
+select concat_ws(',', 'a', 'b');
++--------+
+| EXPR$0 |
++--------+
+| a,b |
++--------+
+(1 row)
+
+!ok
+
+select concat_ws(',', 'a', cast(null as varchar), 'b');
++--------+
+| EXPR$0 |
++--------+
+| a,b |
++--------+
+(1 row)
+
+!ok
+
+select concat_ws(',', '', '', '');
++--------+
+| EXPR$0 |
++--------+
+| ,, |
++--------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws('', '', '') as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws(',', cast(null as varchar), cast(null as varchar))
as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+with t as (select concat_ws(cast(null as varchar), '', '', '') as c)
+select c, c is null as c_is_null
+from t;
++---+-----------+
+| C | C_IS_NULL |
++---+-----------+
+| | false |
++---+-----------+
+(1 row)
+
+!ok
+
+select concat_ws(cast(null as varchar), 'a', cast(null as varchar), 'b');
++--------+
+| EXPR$0 |
++--------+
+| ab |
++--------+
+(1 row)
+
+!ok
+
# [CALCITE-5771] Apply two different NULL semantics for CONCAT
function(enabled in MySQL, Postgresql, BigQuery and MSSQL)
with t as (select concat(null) as c)
select c, c is null as c_is_null
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index f6aabaef0b..a701fe9eda 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -1280,8 +1280,8 @@ completeness.
| string1 NOT LIKE string2 [ ESCAPE string3 ] | Whether *string1* does
not match pattern *string2*
| string1 SIMILAR TO string2 [ ESCAPE string3 ] | Whether *string1*
matches regular expression *string2*
| string1 NOT SIMILAR TO string2 [ ESCAPE string3 ] | Whether *string1* does
not match regular expression *string2*
-| value IN (value [, value]*) | Whether *value* is equal
to a value in a list
-| value NOT IN (value [, value]*) | Whether *value* is not
equal to every value in a list
+| value IN (value [, value ]*) | Whether *value* is equal
to a value in a list
+| value NOT IN (value [, value ]*) | Whether *value* is not
equal to every value in a list
| value IN (sub-query) | Whether *value* is equal
to a row returned by *sub-query*
| value NOT IN (sub-query) | Whether *value* is not
equal to every row returned by *sub-query*
| value comparison SOME (sub-query or collection) | Whether *value*
*comparison* at least one row returned by *sub-query* or *collection*
@@ -2497,7 +2497,7 @@ Not implemented:
Not implemented:
* ST_TriangleAspect(geom) Returns the aspect of a triangle
-* ST_TriangleContouring(query \[, z1, z2, z3 ]\[, varArgs]*) Splits triangles
into smaller triangles according to classes
+* ST_TriangleContouring(query \[, z1, z2, z3 ]\[, varArgs ]*) Splits triangles
into smaller triangles according to classes
* ST_TriangleDirection(geom) Computes the direction of steepest ascent of a
triangle and returns it as a line-string
* ST_TriangleSlope(geom) Computes the slope of a triangle as a percentage
* ST_Voronoi(geom [, outDimension [, envelopePolygon ]]) Creates a Voronoi
diagram
@@ -2679,6 +2679,8 @@ BigQuery's type system uses confusingly different names
for types and functions:
| o | 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
+| 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
| m | COMPRESS(string) | Compresses a string
using zlib compression and returns the result as a binary string
| q | CONVERT(type, expression [ , style ]) | Equivalent to
`CAST(expression AS type)`; ignores the *style* operand
| p | CONVERT_TIMEZONE(tz1, tz2, datetime) | Converts the timezone
of *datetime* from *tz1* to *tz2*
@@ -2735,8 +2737,8 @@ BigQuery's type system uses confusingly different names
for types and functions:
| m | JSON_INSERT(jsonValue, path, val [, path, val ]*) | Returns a JSON
document insert a data of *jsonValue*, *path*, *val*
| m | JSON_KEYS(jsonValue [, path ]) | Returns a string
indicating the keys of a JSON *jsonValue*
| m | JSON_REMOVE(jsonValue, path [, path ]) | Removes data from
*jsonValue* using a series of *path* expressions and returns the result
-| m | JSON_REPLACE(jsonValue, path, val[, path, val]*) | Returns a JSON
document replace a data of *jsonValue*, *path*, *val*
-| m | JSON_SET(jsonValue, path, val[, path, val]*) | Returns a JSON document
set a data of *jsonValue*, *path*, *val*
+| m | JSON_REPLACE(jsonValue, path, val [, path, val ]*) | Returns a JSON
document replace a data of *jsonValue*, *path*, *val*
+| m | JSON_SET(jsonValue, path, val [, path, val ]*) | Returns a JSON document
set a data of *jsonValue*, *path*, *val*
| m | JSON_STORAGE_SIZE(jsonValue) | Returns the number of
bytes used to store the binary representation of *jsonValue*
| b o | LEAST(expr [, expr ]* ) | Returns the least of
the expressions
| b m p | LEFT(string, length) | Returns the leftmost
*length* characters from the *string*
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 4553073d89..2add67eb87 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -1963,7 +1963,7 @@ public class SqlOperatorTest {
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-5771">[CALCITE-5771]
* Apply two different NULL semantics for CONCAT function(enabled in MySQL,
- * Postgresql, BigQuery and MSSQL)</a>. */
+ * Postgres, BigQuery and MSSQL)</a>. */
private static void checkConcatFuncWithNull(SqlOperatorFixture f) {
f.setFor(SqlLibraryOperators.CONCAT_FUNCTION_WITH_NULL);
f.checkString("concat('a', 'b', 'c')", "abc", "VARCHAR(3) NOT NULL");
@@ -1999,6 +1999,58 @@ public class SqlOperatorTest {
f.checkFails("^concat('a')^", INVALID_ARGUMENTS_NUMBER, false);
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5741">[CALCITE-5741]
+ * Add CONCAT_WS function (enabled in MSSQL, MySQL, Postgres
+ * libraries)</a>. */
+ @Test void testConcatWSFunc() {
+ final SqlOperatorFixture f = fixture();
+ checkConcatWithSeparator(f.withLibrary(SqlLibrary.MYSQL));
+ checkConcatWithSeparator(f.withLibrary(SqlLibrary.POSTGRESQL));
+ checkConcatWithSeparatorInMSSQL(f.withLibrary(SqlLibrary.MSSQL));
+ }
+
+ private static void checkConcatWithSeparator(SqlOperatorFixture f) {
+ f.setFor(SqlLibraryOperators.CONCAT_WS);
+ f.checkString("concat_ws(',', 'a')", "a", "VARCHAR(1) NOT NULL");
+ f.checkString("concat_ws(',', 'a', 'b', null, 'c')", "a,b,c",
+ "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', cast('a' as varchar), cast('b' as varchar))",
+ "a,b", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', cast('a' as varchar(2)), cast('b' as
varchar(1)))",
+ "a,b", "VARCHAR(4) NOT NULL");
+ f.checkString("concat_ws(',', '', '', '')", ",,", "VARCHAR(2) NOT NULL");
+ f.checkString("concat_ws(',', null, null, null)", "", "VARCHAR NOT NULL");
+ // returns null if the separator is null
+ f.checkNull("concat_ws(null, 'a', 'b')");
+ f.checkNull("concat_ws(null, null, null)");
+ f.checkFails("^concat_ws(',')^", INVALID_ARGUMENTS_NUMBER, false);
+ // if the separator is empty string, it's equivalent to CONCAT
+ f.checkString("concat_ws('', cast('a' as varchar(2)), cast('b' as
varchar(1)))",
+ "ab", "VARCHAR(3) NOT NULL");
+ f.checkString("concat_ws('', '', '', '')", "", "VARCHAR(0) NOT NULL");
+ }
+
+ private static void checkConcatWithSeparatorInMSSQL(SqlOperatorFixture f) {
+ f.setFor(SqlLibraryOperators.CONCAT_WS_MSSQL);
+ f.checkString("concat_ws(',', 'a', 'b', null, 'c')", "a,b,c",
+ "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', cast('a' as varchar), cast('b' as varchar))",
+ "a,b", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(',', cast('a' as varchar(2)), cast('b' as
varchar(1)))",
+ "a,b", "VARCHAR(4) NOT NULL");
+ f.checkString("concat_ws(',', '', '', '')", ",,", "VARCHAR(2) NOT NULL");
+ f.checkString("concat_ws(',', null, null, null)", "", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(null, 'a', 'b')", "ab", "VARCHAR NOT NULL");
+ f.checkString("concat_ws(null, null, null)", "", "VARCHAR NOT NULL");
+ f.checkFails("^concat_ws(',')^", INVALID_ARGUMENTS_NUMBER, false);
+ f.checkFails("^concat_ws(',', 'a')^", INVALID_ARGUMENTS_NUMBER, false);
+ // if the separator is empty string, it's equivalent to CONCAT
+ f.checkString("concat_ws('', cast('a' as varchar(2)), cast('b' as
varchar(1)))",
+ "ab", "VARCHAR(3) NOT NULL");
+ f.checkString("concat_ws('', '', '', '')", "", "VARCHAR(0) NOT NULL");
+ }
+
@Test void testModOperator() {
// "%" is allowed under BIG_QUERY, MYSQL_5 SQL conformance levels
final SqlOperatorFixture f0 = fixture()