From 5247059d2f98c9faa69c9f50a1831b8af93e1df5 Mon Sep 17 00:00:00 2001 From: Sree Charan Manamala <155449160+sreemanamala@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:14:42 +0530 Subject: [PATCH] Allow Double & null values in sql type array through dynamic params (#16274) --- .../planner/SqlParameterizerShuttle.java | 11 +-- .../sql/calcite/CalciteArraysQueryTest.java | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/SqlParameterizerShuttle.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/SqlParameterizerShuttle.java index 29da45b085f..e5fccf66fc5 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/SqlParameterizerShuttle.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/SqlParameterizerShuttle.java @@ -129,18 +129,19 @@ public class SqlParameterizerShuttle extends SqlShuttle List args = new ArrayList<>(list.size()); for (int i = 0, listSize = list.size(); i < listSize; i++) { Object element = list.get(i); - if (element == null) { - throw InvalidSqlInput.exception("parameter [%d] is an array, with an illegal null at index [%d]", posn + 1, i); - } SqlNode node; - if (element instanceof String) { + if (element == null) { + node = SqlLiteral.createNull(SqlParserPos.ZERO); + } else if (element instanceof String) { node = SqlLiteral.createCharString((String) element, SqlParserPos.ZERO); } else if (element instanceof Integer || element instanceof Long) { // No direct way to create a literal from an Integer or Long, have // to parse a string, sadly. node = SqlLiteral.createExactNumeric(element.toString(), SqlParserPos.ZERO); + } else if (element instanceof Double || element instanceof Float) { + node = SqlLiteral.createApproxNumeric(element.toString(), SqlParserPos.ZERO); } else if (element instanceof Boolean) { - node = SqlLiteral.createBoolean((Boolean) value, SqlParserPos.ZERO); + node = SqlLiteral.createBoolean((Boolean) element, SqlParserPos.ZERO); } else { throw InvalidSqlInput.exception( "parameter [%d] is an array, with an illegal value of type [%s] at index [%d]", diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java index 1adb8ec20e1..7c388f697d3 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteArraysQueryTest.java @@ -22,6 +22,7 @@ package org.apache.druid.sql.calcite; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.apache.calcite.avatica.SqlType; import org.apache.druid.common.config.NullHandling; import org.apache.druid.guice.DruidInjectorBuilder; import org.apache.druid.guice.NestedDataModule; @@ -70,6 +71,7 @@ import org.apache.druid.segment.join.JoinType; import org.apache.druid.segment.virtual.ExpressionVirtualColumn; import org.apache.druid.sql.calcite.filtration.Filtration; import org.apache.druid.sql.calcite.util.CalciteTests; +import org.apache.druid.sql.http.SqlParameter; import org.junit.Assert; import org.junit.jupiter.api.Test; @@ -933,6 +935,40 @@ public class CalciteArraysQueryTest extends BaseCalciteQueryTest ); } + @Test + public void testArrayOverlapFilterWithDynamicParameter() + { + Druids.ScanQueryBuilder builder = newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE3) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(expressionFilter("array_overlap(array(1.0,1.7,null),array(\"d1\"))")) + .columns("dim3") + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .limit(5) + .context(QUERY_CONTEXT_DEFAULT); + + testQuery( + PLANNER_CONFIG_DEFAULT, + QUERY_CONTEXT_DEFAULT, + ImmutableList.of( + new SqlParameter(SqlType.ARRAY, Arrays.asList(1.0, 1.7, null)) + ), + "SELECT dim3 FROM druid.numfoo WHERE ARRAY_OVERLAP(?, ARRAY[d1]) LIMIT 5", + CalciteTests.REGULAR_USER_AUTH_RESULT, + ImmutableList.of(builder.build()), + NullHandling.sqlCompatible() ? ImmutableList.of( + new Object[]{"[\"a\",\"b\"]"}, + new Object[]{"[\"b\",\"c\"]"}, + new Object[]{""}, + new Object[]{null}, + new Object[]{null} + ) : ImmutableList.of( + new Object[]{"[\"a\",\"b\"]"}, + new Object[]{"[\"b\",\"c\"]"} + ) + ); + } + @Test public void testArrayContainsFilter() { @@ -1270,6 +1306,39 @@ public class CalciteArraysQueryTest extends BaseCalciteQueryTest ); } + @Test + public void testArrayContainsFilterWithDynamicParameter() + { + Druids.ScanQueryBuilder builder = newScanQueryBuilder() + .dataSource(CalciteTests.DATASOURCE3) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(expressionFilter("array_contains(array(1,null),array((\"d1\" > 1)))")) + .columns("dim3") + .resultFormat(ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST) + .limit(5) + .context(QUERY_CONTEXT_DEFAULT); + + testQuery( + PLANNER_CONFIG_DEFAULT, + QUERY_CONTEXT_DEFAULT, + ImmutableList.of( + new SqlParameter(SqlType.ARRAY, Arrays.asList(true, null)) + ), + "SELECT dim3 FROM druid.numfoo WHERE ARRAY_CONTAINS(?, ARRAY[d1>1]) LIMIT 5", + CalciteTests.REGULAR_USER_AUTH_RESULT, + ImmutableList.of(builder.build()), + NullHandling.sqlCompatible() ? ImmutableList.of( + new Object[]{"[\"b\",\"c\"]"}, + new Object[]{""}, + new Object[]{null}, + new Object[]{null} + ) : ImmutableList.of( + new Object[]{"[\"b\",\"c\"]"} + ) + ); + } + + @Test public void testArraySlice() {