Skip to content

Commit 4c9011c

Browse files
mihaibudiulibenchao
authored andcommitted
[CALCITE-5884] ARRAY_TO_STRING function should return NULL if its 'nullValue' argument is NULL
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
1 parent 94dc167 commit 4c9011c

File tree

7 files changed

+68
-17
lines changed

7 files changed

+68
-17
lines changed

core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5583,6 +5583,10 @@ public static String arrayToString(List list, String delimiter) {
55835583

55845584
/** SQL {@code ARRAY_TO_STRING(array, delimiter, nullText)} function. */
55855585
public static String arrayToString(List list, String delimiter, @Nullable String nullText) {
5586+
// Note that the SQL function ARRAY_TO_STRING that we implement will return
5587+
// 'NULL' when the nullText argument is NULL. However, that is handled by
5588+
// the nullPolicy of the RexToLixTranslator. So here a NULL value
5589+
// for the nullText argument can only come from the above 2-argument version.
55865590
StringBuilder sb = new StringBuilder();
55875591
boolean isFirst = true;
55885592
for (Object item : list) {

core/src/main/java/org/apache/calcite/sql/SqlNumericLiteral.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public boolean isExact() {
117117
scaleValue);
118118
}
119119

120-
// else we have a a float, real or double. make them all double for
120+
// else we have a FLOAT, REAL or DOUBLE. make them all DOUBLE for
121121
// now.
122122
return typeFactory.createSqlType(SqlTypeName.DOUBLE);
123123
}

core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ public static SqlCall stripSeparator(SqlCall call) {
591591
};
592592

593593
/**
594-
* Returns the element type of a ARRAY or MULTISET.
594+
* Returns the element type of an ARRAY or MULTISET.
595595
*
596596
* <p>For example, given <code>INTEGER ARRAY or MULTISET ARRAY</code>, returns
597597
* <code>INTEGER</code>.

core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ private static boolean skipItem(RexNode expr) {
225225
}
226226

227227
/**
228-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
228+
* Test case for
229+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
229230
* Type inference for sql functions REPEAT, SPACE, XML_TRANSFORM,
230231
* and XML_EXTRACT is incorrect</a>. */
231232
@Test void testRepeat() {
@@ -246,7 +247,8 @@ private static boolean skipItem(RexNode expr) {
246247
}
247248

248249
/**
249-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
250+
* Test case for
251+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
250252
* Type inference for sql functions REPEAT, SPACE, XML_TRANSFORM,
251253
* and XML_EXTRACT is incorrect</a>. */
252254
@Test void testReplace() {
@@ -263,7 +265,8 @@ private static boolean skipItem(RexNode expr) {
263265
}
264266

265267
/**
266-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
268+
* Test case for
269+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
267270
* Type inference for RPAD and LPAD functions (BIGQUERY) is incorrect</a>. */
268271
@Test void testRpad() {
269272
HepProgramBuilder builder = new HepProgramBuilder();
@@ -283,7 +286,8 @@ private static boolean skipItem(RexNode expr) {
283286
}
284287

285288
/**
286-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
289+
* Test case for
290+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5989">[CALCITE-5989]
287291
* Type inference for RPAD and LPAD functions (BIGQUERY) is incorrect</a>. */
288292
@Test void testLpad() {
289293
HepProgramBuilder builder = new HepProgramBuilder();
@@ -303,7 +307,8 @@ private static boolean skipItem(RexNode expr) {
303307
}
304308

305309
/**
306-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5971">[CALCITE-5971]
310+
* Test case for
311+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5971">[CALCITE-5971]
307312
* Add the RelRule to rewrite the bernoulli sample as Filter</a>. */
308313
@Test void testSampleToFilter() {
309314
final String sql = "select deptno from emp tablesample bernoulli(50)";
@@ -313,7 +318,8 @@ private static boolean skipItem(RexNode expr) {
313318
}
314319

315320
/**
316-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5971">[CALCITE-5971]
321+
* Test case for
322+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5971">[CALCITE-5971]
317323
* Add the RelRule to rewrite the bernoulli sample as Filter</a>. */
318324
@Test void testSampleToFilterWithSeed() {
319325
final String sql = "select deptno from emp tablesample bernoulli(50) REPEATABLE(10)";
@@ -323,7 +329,8 @@ private static boolean skipItem(RexNode expr) {
323329
}
324330

325331
/**
326-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
332+
* Test case for
333+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5813">[CALCITE-5813]
327334
* Type inference for sql functions REPEAT, SPACE, XML_TRANSFORM,
328335
* and XML_EXTRACT is incorrect</a>. */
329336
@Test void testSpace() {
@@ -2034,6 +2041,21 @@ private void checkSemiOrAntiJoinProjectTranspose(JoinRelType type) {
20342041
.check();
20352042
}
20362043

2044+
/** Test case for
2045+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5884">[CALCITE-5884]
2046+
* ARRAY_TO_STRING function should return NULL if its 'nullValue' argument is NULL</a>. */
2047+
@Test void testArrayToString() {
2048+
final String sql = "select array_to_string(array['1','2','3','4',NULL,'6'], ',', NULL)";
2049+
// We expect the result to be NULL, since array_to_string returns NULL if
2050+
// any argument is NULL.
2051+
sql(sql).withFactory(
2052+
t -> t.withOperatorTable(
2053+
opTab -> SqlLibraryOperatorTableFactory.INSTANCE.getOperatorTable(
2054+
SqlLibrary.STANDARD, SqlLibrary.BIG_QUERY))) // for array_to_string function
2055+
.withRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS)
2056+
.check();
2057+
}
2058+
20372059
/** Test case for
20382060
* <a href="https://issues.apache.org/jira/browse/CALCITE-1558">[CALCITE-1558]
20392061
* AggregateExpandDistinctAggregatesRule gets field mapping wrong if groupKey
@@ -2733,7 +2755,8 @@ private void checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelT
27332755
}
27342756

27352757
/**
2736-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5073">[CALCITE-5073]
2758+
* Test case for
2759+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5073">[CALCITE-5073]
27372760
* JoinConditionPushRule cannot infer 'LHS.C1 = LHS.C2' from
27382761
* 'LHS.C1 = RHS.C1 AND LHS.C2 = RHS.C1'</a>.
27392762
*/
@@ -2750,7 +2773,8 @@ private void checkPushJoinThroughUnionOnRightDoesNotMatchSemiOrAntiJoin(JoinRelT
27502773
}
27512774

27522775
/**
2753-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5073">[CALCITE-5073]
2776+
* Test case for
2777+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5073">[CALCITE-5073]
27542778
* JoinConditionPushRule cannot infer 'LHS.C1 = LHS.C2' from
27552779
* 'LHS.C1 = RHS.C1 AND LHS.C2 = RHS.C1'</a>.
27562780
*/
@@ -5483,7 +5507,8 @@ private HepProgram getTransitiveProgram() {
54835507
}
54845508

54855509
/**
5486-
* Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5861">
5510+
* Test case for
5511+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5861">
54875512
* [CALCITE-5861] ReduceExpressionsRule rules should constant-fold
54885513
* expressions in window bounds</a>.
54895514
*/
@@ -5682,7 +5707,8 @@ private HepProgram getTransitiveProgram() {
56825707
.checkUnchanged();
56835708
}
56845709

5685-
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
5710+
/** Test case for
5711+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
56865712
* AssertionError during constant reduction of SPLIT expression that returns NULL</a>. */
56875713
@Test public void testSplitNull() {
56885714
final String query = "select split('1|2|3', NULL)";
@@ -5695,7 +5721,8 @@ private HepProgram getTransitiveProgram() {
56955721
.check();
56965722
}
56975723

5698-
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
5724+
/** Test case for
5725+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
56995726
* AssertionError during constant reduction of SPLIT expression that returns NULL</a>. */
57005727
@Test public void testSplitNull1() {
57015728
final String query = "select split(NULL, '|')";
@@ -5708,7 +5735,8 @@ private HepProgram getTransitiveProgram() {
57085735
.check();
57095736
}
57105737

5711-
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
5738+
/** Test case for
5739+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5879">
57125740
* AssertionError during constant reduction of SPLIT expression that returns NULL</a>. */
57135741
@Test public void testSplitNull2() {
57145742
final String query = "select split(NULL, NULL)";
@@ -5721,7 +5749,8 @@ private HepProgram getTransitiveProgram() {
57215749
.check();
57225750
}
57235751

5724-
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-5882">
5752+
/** Test case for
5753+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5882">
57255754
* [CALCITE-5882] Compile-time evaluation of SPLIT function returns incorrect result</a>. */
57265755
@Test public void testSplit() {
57275756
final String query = "select split('1|2|3', '|')";

core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,23 @@ LogicalProject(DEPTNO=[$0], EXPR$1=[OR(AND(IS NOT NULL($5), <>($2, 0)), AND(<($3
12831283
LogicalAggregate(group=[{0}], i=[LITERAL_AGG(true)])
12841284
LogicalProject(MGR=[$3])
12851285
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
1286+
]]>
1287+
</Resource>
1288+
</TestCase>
1289+
<TestCase name="testArrayToString">
1290+
<Resource name="sql">
1291+
<![CDATA[select array_to_string(array['1','2','3','4',NULL,'6'], ',', NULL)]]>
1292+
</Resource>
1293+
<Resource name="planBefore">
1294+
<![CDATA[
1295+
LogicalProject(EXPR$0=[ARRAY_TO_STRING(ARRAY('1', '2', '3', '4', null:CHAR(1), '6'), ',', null:NULL)])
1296+
LogicalValues(tuples=[[{ 0 }]])
1297+
]]>
1298+
</Resource>
1299+
<Resource name="planAfter">
1300+
<![CDATA[
1301+
LogicalProject(EXPR$0=[null:VARCHAR])
1302+
LogicalValues(tuples=[[{ 0 }]])
12861303
]]>
12871304
</Resource>
12881305
</TestCase>

site/_docs/reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2673,7 +2673,7 @@ BigQuery's type system uses confusingly different names for types and functions:
26732673
| s | ARRAY_REPEAT(element, count) | Returns the array containing element count times.
26742674
| b | ARRAY_REVERSE(array) | Reverses elements of *array*
26752675
| s | ARRAY_SIZE(array) | Synonym for `CARDINALITY`
2676-
| b | ARRAY_TO_STRING(array, delimiter [, nullText ])| Returns a concatenation of the elements in *array* as a STRING and take *delimiter* as the delimiter. If the *nullText* parameter is used, the function replaces any `NULL` values in the array with the value of *nullText*. If the *nullText* parameter is not used, the function omits the `NULL` value and its preceding delimiter
2676+
| b | ARRAY_TO_STRING(array, delimiter [, nullText ])| Returns a concatenation of the elements in *array* as a STRING and take *delimiter* as the delimiter. If the *nullText* parameter is used, the function replaces any `NULL` values in the array with the value of *nullText*. If the *nullText* parameter is not used, the function omits the `NULL` value and its preceding delimiter. Returns `NULL` if any argument is `NULL`
26772677
| s | ARRAY_UNION(array1, array2) | Returns an array of the elements in the union of *array1* and *array2*, without duplicates
26782678
| s | ARRAYS_OVERLAP(array1, array2) | Returns true if *array1 contains at least a non-null element present also in *array2*. If the arrays have no common element and they are both non-empty and either of them contains a null element null is returned, false otherwise
26792679
| s | ARRAYS_ZIP(array [, array ]*) | Returns a merged *array* of structs in which the N-th struct contains all N-th values of input arrays

testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6600,6 +6600,7 @@ private static void checkIf(SqlOperatorFixture f) {
66006600
f.checkScalar("array_to_string(array['', ''], '-')", "-", "VARCHAR NOT NULL");
66016601
f.checkNull("array_to_string(null, '-')");
66026602
f.checkNull("array_to_string(array['a', 'b', null], null)");
6603+
f.checkNull("array_to_string(array['1','2','3'], ',', null)");
66036604
f.checkFails("^array_to_string(array[1, 2, 3], '-', ' ')^",
66046605
"Cannot apply 'ARRAY_TO_STRING' to arguments of type 'ARRAY_TO_STRING"
66056606
+ "\\(<INTEGER ARRAY>, <CHAR\\(1\\)>, <CHAR\\(1\\)>\\)'\\. Supported form\\(s\\):"

0 commit comments

Comments
 (0)