This is an automated email from the ASF dual-hosted git repository.
abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 2ea7177f151 Allow casted literal values in SQL functions accepting
literals (#15282)
2ea7177f151 is described below
commit 2ea7177f15139071980a881f111f7dbd86287baa
Author: Laksh Singla <[email protected]>
AuthorDate: Wed Nov 1 10:38:48 2023 +0530
Allow casted literal values in SQL functions accepting literals (#15282)
Functions that accept literals also allow casted literals. This shouldn't
have an impact on the queries that the user writes. It enables the SQL
functions to accept explicit cast, which is required with JDBC.
---
codestyle/druid-forbidden-apis.txt | 1 +
.../sql/TDigestGenerateSketchSqlAggregator.java | 24 +--
.../sql/TDigestSketchQuantileSqlAggregator.java | 27 ++-
.../sql/TDigestSketchSqlAggregatorTest.java | 70 +++++++
...DoublesSketchListArgBaseOperatorConversion.java | 7 +-
.../sql/DoublesSketchSqlAggregatorTest.java | 28 ++-
.../bloom/sql/BloomFilterSqlAggregator.java | 20 +-
.../bloom/sql/BloomFilterSqlAggregatorTest.java | 8 +-
...FixedBucketsHistogramQuantileSqlAggregator.java | 74 +++----
.../histogram/sql/QuantileSqlAggregator.java | 28 ++-
...dBucketsHistogramQuantileSqlAggregatorTest.java | 82 +++++++-
.../histogram/sql/QuantileSqlAggregatorTest.java | 27 +--
.../builtin/EarliestLatestAnySqlAggregator.java | 18 +-
.../builtin/EarliestLatestBySqlAggregator.java | 22 +--
...Checker.java => DefaultOperandTypeChecker.java} | 136 +++++++------
.../calcite/expression/OperatorConversions.java | 217 ++++-----------------
.../ComplexDecodeBase64OperatorConversion.java | 12 +-
.../builtin/NestedDataOperatorConversions.java | 11 +-
.../builtin/QueryLookupOperatorConversion.java | 15 +-
.../TimeInIntervalConvertletFactory.java | 12 +-
.../sql/calcite/CalciteNestedDataQueryTest.java | 42 ++++
.../apache/druid/sql/calcite/CalciteQueryTest.java | 74 ++++++-
.../expression/OperatorConversionsTest.java | 33 ++--
23 files changed, 540 insertions(+), 448 deletions(-)
diff --git a/codestyle/druid-forbidden-apis.txt
b/codestyle/druid-forbidden-apis.txt
index a99654f1212..8da588f9d31 100644
--- a/codestyle/druid-forbidden-apis.txt
+++ b/codestyle/druid-forbidden-apis.txt
@@ -44,6 +44,7 @@ java.util.LinkedList @ Use ArrayList or ArrayDeque instead
java.util.Random#<init>() @ Use ThreadLocalRandom.current() or the constructor
with a seed (the latter in tests only!)
java.lang.Math#random() @ Use ThreadLocalRandom.current()
java.util.regex.Pattern#matches(java.lang.String,java.lang.CharSequence) @ Use
String.startsWith(), endsWith(), contains(), or compile and cache a Pattern
explicitly
+org.apache.calcite.sql.type.OperandTypes#LITERAL @ LITERAL type checker throws
when literals with CAST are passed. Use
org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker instead.
org.apache.commons.io.FileUtils#getTempDirectory() @ Use
org.junit.rules.TemporaryFolder for tests instead
org.apache.commons.io.FileUtils#deleteDirectory(java.io.File) @ Use
org.apache.druid.java.util.common.FileUtils#deleteDirectory()
org.apache.commons.io.FileUtils#forceMkdir(java.io.File) @ Use
org.apache.druid.java.util.common.FileUtils.mkdirp instead
diff --git
a/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestGenerateSketchSqlAggregator.java
b/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestGenerateSketchSqlAggregator.java
index ebb6c7f4b14..cb9b95423a6 100644
---
a/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestGenerateSketchSqlAggregator.java
+++
b/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestGenerateSketchSqlAggregator.java
@@ -25,10 +25,10 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.AggregatorFactory;
import
org.apache.druid.query.aggregation.tdigestsketch.TDigestSketchAggregatorFactory;
@@ -37,6 +37,7 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.Aggregations;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.rel.InputAccessor;
@@ -133,8 +134,6 @@ public class TDigestGenerateSketchSqlAggregator implements
SqlAggregator
private static class TDigestGenerateSketchSqlAggFunction extends
SqlAggFunction
{
- private static final String SIGNATURE_WITH_COMPRESSION = "'" + NAME +
"(column, compression)'";
-
TDigestGenerateSketchSqlAggFunction()
{
super(
@@ -143,16 +142,19 @@ public class TDigestGenerateSketchSqlAggregator
implements SqlAggregator
SqlKind.OTHER_FUNCTION,
ReturnTypes.explicit(SqlTypeName.OTHER),
null,
- OperandTypes.or(
- OperandTypes.ANY,
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE_WITH_COMPRESSION,
OperandTypes.ANY, OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
- )
- ),
+ // Validation for signatures like 'TDIGEST_GENERATE_SKETCH(column)'
and
+ // 'TDIGEST_GENERATE_SKETCH(column, compression)'
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("column", "compression")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
+ .requiredOperandCount(1)
+ .literalOperands(1)
+ .build(),
SqlFunctionCategory.USER_DEFINED_FUNCTION,
false,
- false
+ false,
+ Optionality.FORBIDDEN
);
}
}
diff --git
a/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchQuantileSqlAggregator.java
b/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchQuantileSqlAggregator.java
index ee63444f6d7..7cc0bdfe8c5 100644
---
a/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchQuantileSqlAggregator.java
+++
b/extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchQuantileSqlAggregator.java
@@ -26,10 +26,10 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.post.FieldAccessPostAggregator;
@@ -40,6 +40,7 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.Aggregations;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.rel.InputAccessor;
@@ -158,9 +159,6 @@ public class TDigestSketchQuantileSqlAggregator implements
SqlAggregator
private static class TDigestSketchQuantileSqlAggFunction extends
SqlAggFunction
{
- private static final String SIGNATURE1 = "'" + NAME + "(column,
quantile)'";
- private static final String SIGNATURE2 = "'" + NAME + "(column, quantile,
compression)'";
-
TDigestSketchQuantileSqlAggFunction()
{
super(
@@ -169,19 +167,18 @@ public class TDigestSketchQuantileSqlAggregator
implements SqlAggregator
SqlKind.OTHER_FUNCTION,
ReturnTypes.explicit(SqlTypeName.DOUBLE),
null,
- OperandTypes.or(
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE1, OperandTypes.ANY,
OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
- ),
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE2, OperandTypes.ANY,
OperandTypes.LITERAL, OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY,
SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC)
- )
- ),
+ // Accounts for both 'TDIGEST_QUANTILE(column, quantile)' and
'TDIGEST_QUANTILE(column, quantile, compression)'
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("column", "quantile", "compression")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC,
SqlTypeFamily.NUMERIC)
+ .literalOperands(1, 2)
+ .requiredOperandCount(2)
+ .build(),
SqlFunctionCategory.USER_DEFINED_FUNCTION,
false,
- false
+ false,
+ Optionality.FORBIDDEN
);
}
}
diff --git
a/extensions-contrib/tdigestsketch/src/test/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchSqlAggregatorTest.java
b/extensions-contrib/tdigestsketch/src/test/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchSqlAggregatorTest.java
index c9dba876276..6193b82cbdd 100644
---
a/extensions-contrib/tdigestsketch/src/test/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchSqlAggregatorTest.java
+++
b/extensions-contrib/tdigestsketch/src/test/java/org/apache/druid/query/aggregation/tdigestsketch/sql/TDigestSketchSqlAggregatorTest.java
@@ -138,6 +138,76 @@ public class TDigestSketchSqlAggregatorTest extends
BaseCalciteQueryTest
);
}
+ @Test
+ public void testCastedQuantileAndCompressionParamForTDigestQuantileAgg()
+ {
+ cannotVectorize();
+ testQuery(
+ "SELECT\n"
+ + "TDIGEST_QUANTILE(m1, CAST(0.0 AS DOUBLE)), "
+ + "TDIGEST_QUANTILE(m1, CAST(0.5 AS FLOAT), CAST(200 AS INTEGER)), "
+ + "TDIGEST_QUANTILE(m1, CAST(1.0 AS DOUBLE), 300)\n"
+ + "FROM foo",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(new
MultipleIntervalSegmentSpec(ImmutableList.of(Filtration.eternity())))
+ .granularity(Granularities.ALL)
+ .aggregators(ImmutableList.of(
+ new TDigestSketchAggregatorFactory("a0:agg", "m1",
+
TDigestSketchAggregatorFactory.DEFAULT_COMPRESSION
+ ),
+ new TDigestSketchAggregatorFactory("a1:agg", "m1",
+ 200
+ ),
+ new TDigestSketchAggregatorFactory("a2:agg", "m1",
+ 300
+ )
+ ))
+ .postAggregators(
+ new TDigestSketchToQuantilePostAggregator("a0",
makeFieldAccessPostAgg("a0:agg"), 0.0f),
+ new TDigestSketchToQuantilePostAggregator("a1",
makeFieldAccessPostAgg("a1:agg"), 0.5f),
+ new TDigestSketchToQuantilePostAggregator("a2",
makeFieldAccessPostAgg("a2:agg"), 1.0f)
+ )
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ResultMatchMode.EQUALS_EPS,
+ ImmutableList.of(
+ new Object[]{1.0, 3.5, 6.0}
+ )
+ );
+ }
+
+ @Test
+ public void
testComputingSketchOnNumericValuesWithCastedCompressionParameter()
+ {
+ cannotVectorize();
+
+ testQuery(
+ "SELECT\n"
+ + "TDIGEST_GENERATE_SKETCH(m1, CAST(200 AS INTEGER))"
+ + "FROM foo",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(new
MultipleIntervalSegmentSpec(ImmutableList.of(Filtration.eternity())))
+ .granularity(Granularities.ALL)
+ .aggregators(ImmutableList.of(
+ new TDigestSketchAggregatorFactory("a0:agg", "m1", 200)
+ ))
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ResultMatchMode.EQUALS_EPS,
+ ImmutableList.of(
+ new String[]{
+
"\"AAAAAT/wAAAAAAAAQBgAAAAAAABAaQAAAAAAAAAAAAY/8AAAAAAAAD/wAAAAAAAAP/AAAAAAAABAAAAAAAAAAD/wAAAAAAAAQAgAAAAAAAA/8AAAAAAAAEAQAAAAAAAAP/AAAAAAAABAFAAAAAAAAD/wAAAAAAAAQBgAAAAAAAA=\""
+ }
+ )
+ );
+ }
+
@Test
public void testComputingSketchOnCastedString()
{
diff --git
a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchListArgBaseOperatorConversion.java
b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchListArgBaseOperatorConversion.java
index a83d937f680..337586b1f94 100644
---
a/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchListArgBaseOperatorConversion.java
+++
b/extensions-core/datasketches/src/main/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchListArgBaseOperatorConversion.java
@@ -40,7 +40,6 @@ import org.apache.calcite.util.Static;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.PostAggregator;
import org.apache.druid.segment.column.RowSignature;
-import org.apache.druid.sql.calcite.expression.BasicOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.OperatorConversions;
import org.apache.druid.sql.calcite.expression.PostAggregatorVisitor;
@@ -143,8 +142,8 @@ public abstract class
DoublesSketchListArgBaseOperatorConversion implements SqlO
final RelDataType operandType =
callBinding.getValidator().deriveType(callBinding.getScope(), operand);
// Verify that 'operand' is a literal number.
- if (!SqlUtil.isLiteral(operand)) {
- return BasicOperandTypeChecker.throwOrReturn(
+ if (!SqlUtil.isLiteral(operand, true)) {
+ return OperatorConversions.throwOrReturn(
throwOnFailure,
callBinding,
cb -> cb.getValidator()
@@ -156,7 +155,7 @@ public abstract class
DoublesSketchListArgBaseOperatorConversion implements SqlO
}
if (!SqlTypeFamily.NUMERIC.contains(operandType)) {
- return BasicOperandTypeChecker.throwOrReturn(
+ return OperatorConversions.throwOrReturn(
throwOnFailure,
callBinding,
SqlCallBinding::newValidationSignatureError
diff --git
a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
index f85225d107d..b39d3441b65 100644
---
a/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
+++
b/extensions-core/datasketches/src/test/java/org/apache/druid/query/aggregation/datasketches/quantiles/sql/DoublesSketchSqlAggregatorTest.java
@@ -495,6 +495,7 @@ public class DoublesSketchSqlAggregatorTest extends
BaseCalciteQueryTest
+ " DS_GET_QUANTILE(DS_QUANTILES_SKETCH(cnt + 123), 0.5) + 1000,\n"
+ " ABS(DS_GET_QUANTILE(DS_QUANTILES_SKETCH(cnt), 0.5)),\n"
+ " DS_GET_QUANTILES(DS_QUANTILES_SKETCH(cnt), 0.5, 0.8),\n"
+ + " DS_GET_QUANTILES(DS_QUANTILES_SKETCH(cnt), CAST(0.5 AS DOUBLE),
CAST(0.8 AS DOUBLE)),\n"
+ " DS_HISTOGRAM(DS_QUANTILES_SKETCH(cnt), 0.2, 0.6),\n"
+ " DS_RANK(DS_QUANTILES_SKETCH(cnt), 3),\n"
+ " DS_CDF(DS_QUANTILES_SKETCH(cnt), 0.2, 0.6),\n"
@@ -588,41 +589,49 @@ public class DoublesSketchSqlAggregatorTest extends
BaseCalciteQueryTest
),
new double[]{0.5d, 0.8d}
),
- new DoublesSketchToHistogramPostAggregator(
+ new DoublesSketchToQuantilesPostAggregator(
"p13",
new FieldAccessPostAggregator(
"p12",
"a2:agg"
),
+ new double[]{0.5d, 0.8d}
+ ),
+ new DoublesSketchToHistogramPostAggregator(
+ "p15",
+ new FieldAccessPostAggregator(
+ "p14",
+ "a2:agg"
+ ),
new double[]{0.2d, 0.6d},
null
),
new DoublesSketchToRankPostAggregator(
- "p15",
+ "p17",
new FieldAccessPostAggregator(
- "p14",
+ "p16",
"a2:agg"
),
3.0d
),
new DoublesSketchToCDFPostAggregator(
- "p17",
+ "p19",
new FieldAccessPostAggregator(
- "p16",
+ "p18",
"a2:agg"
),
new double[]{0.2d, 0.6d}
),
new DoublesSketchToStringPostAggregator(
- "p19",
+ "p21",
new FieldAccessPostAggregator(
- "p18",
+ "p20",
"a2:agg"
)
),
new ExpressionPostAggregator(
- "p20",
-
"replace(replace(\"p19\",'HeapCompactDoublesSketch','HeapUpdateDoublesSketch'),"
+ "p22",
+
"replace(replace(\"p21\",'HeapCompactDoublesSketch','HeapUpdateDoublesSketch'),"
+ "'Combined Buffer Capacity : 6',"
+ "'Combined Buffer Capacity : 8')",
null,
@@ -640,6 +649,7 @@ public class DoublesSketchSqlAggregatorTest extends
BaseCalciteQueryTest
1124.0d,
1.0d,
"[1.0,1.0]",
+ "[1.0,1.0]",
"[0.0,0.0,6.0]",
1.0d,
"[0.0,0.0,1.0]",
diff --git
a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
index 6a1ca49067e..cb73d94ef7e 100644
---
a/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
+++
b/extensions-core/druid-bloom-filter/src/main/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregator.java
@@ -25,10 +25,10 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.bloom.BloomFilterAggregatorFactory;
@@ -38,6 +38,7 @@ import
org.apache.druid.query.dimension.ExtractionDimensionSpec;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
import org.apache.druid.sql.calcite.planner.Calcites;
@@ -168,8 +169,6 @@ public class BloomFilterSqlAggregator implements
SqlAggregator
private static class BloomFilterSqlAggFunction extends SqlAggFunction
{
- private static final String SIGNATURE1 = "'" + NAME + "(column,
maxNumEntries)'";
-
BloomFilterSqlAggFunction()
{
super(
@@ -178,13 +177,18 @@ public class BloomFilterSqlAggregator implements
SqlAggregator
SqlKind.OTHER_FUNCTION,
ReturnTypes.explicit(SqlTypeName.OTHER),
null,
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE1, OperandTypes.ANY,
OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
- ),
+ // Allow signatures like 'BLOOM_FILTER(column, maxNumEntries)'
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("column", "maxNumEntries")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
+ .literalOperands(1)
+ .requiredOperandCount(2)
+ .build(),
SqlFunctionCategory.USER_DEFINED_FUNCTION,
false,
- false
+ false,
+ Optionality.FORBIDDEN
);
}
}
diff --git
a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
index 1c77f0986e1..eff2039ffa8 100644
---
a/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
+++
b/extensions-core/druid-bloom-filter/src/test/java/org/apache/druid/query/aggregation/bloom/sql/BloomFilterSqlAggregatorTest.java
@@ -125,7 +125,8 @@ public class BloomFilterSqlAggregatorTest extends
BaseCalciteQueryTest
testQuery(
"SELECT\n"
- + "BLOOM_FILTER(dim1, 1000)\n"
+ + "BLOOM_FILTER(dim1, 1000),\n"
+ + "BLOOM_FILTER(dim1, CAST(1000 AS INTEGER))\n"
+ "FROM numfoo",
ImmutableList.of(
Druids.newTimeseriesQueryBuilder()
@@ -145,7 +146,10 @@ public class BloomFilterSqlAggregatorTest extends
BaseCalciteQueryTest
.build()
),
ImmutableList.of(
- new
Object[]{queryFramework().queryJsonMapper().writeValueAsString(expected1)}
+ new Object[]{
+
queryFramework().queryJsonMapper().writeValueAsString(expected1),
+
queryFramework().queryJsonMapper().writeValueAsString(expected1)
+ }
)
);
}
diff --git
a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
index fdc61796c4d..eceaa7b8ad2 100644
---
a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
+++
b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregator.java
@@ -26,10 +26,10 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.histogram.FixedBucketsHistogram;
@@ -39,6 +39,7 @@ import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.Aggregations;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.rel.InputAccessor;
@@ -221,15 +222,6 @@ public class FixedBucketsHistogramQuantileSqlAggregator
implements SqlAggregator
private static class FixedBucketsHistogramQuantileSqlAggFunction extends
SqlAggFunction
{
- private static final String SIGNATURE1 =
- "'"
- + NAME
- + "(column, probability, numBuckets, lowerLimit, upperLimit)'";
- private static final String SIGNATURE2 =
- "'"
- + NAME
- + "(column, probability, numBuckets, lowerLimit, upperLimit,
outlierHandlingMode)'";
-
FixedBucketsHistogramQuantileSqlAggFunction()
{
super(
@@ -238,47 +230,33 @@ public class FixedBucketsHistogramQuantileSqlAggregator
implements SqlAggregator
SqlKind.OTHER_FUNCTION,
ReturnTypes.explicit(SqlTypeName.DOUBLE),
null,
- OperandTypes.or(
- OperandTypes.and(
- OperandTypes.sequence(
- SIGNATURE1,
- OperandTypes.ANY,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL
- ),
- OperandTypes.family(
- SqlTypeFamily.ANY,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC
- )
- ),
- OperandTypes.and(
- OperandTypes.sequence(
- SIGNATURE2,
- OperandTypes.ANY,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL,
- OperandTypes.LITERAL
- ),
- OperandTypes.family(
- SqlTypeFamily.ANY,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.NUMERIC,
- SqlTypeFamily.STRING
- )
+ // Allows signatures like 'APPROX_QUANTILE_FIXED_BUCKETS(column,
probability, numBuckets, lowerLimit, upperLimit)'
+ // and 'APPROX_QUANTILE_FIXED_BUCKETS(column, probability,
numBuckets, lowerLimit, upperLimit, outlierHandlingMode)'
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames(
+ "column",
+ "probability",
+ "numBuckets",
+ "lowerLimit",
+ "upperLimit",
+ "outlierHandlingMode"
+ )
+ .operandTypes(
+ SqlTypeFamily.ANY,
+ SqlTypeFamily.NUMERIC,
+ SqlTypeFamily.NUMERIC,
+ SqlTypeFamily.NUMERIC,
+ SqlTypeFamily.NUMERIC,
+ SqlTypeFamily.STRING
)
- ),
+ .literalOperands(1, 2, 3, 4, 5)
+ .requiredOperandCount(5)
+ .build(),
SqlFunctionCategory.NUMERIC,
false,
- false
+ false,
+ Optionality.FORBIDDEN
);
}
}
diff --git
a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
index a3fe8dc5458..41df080147b 100644
---
a/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
+++
b/extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregator.java
@@ -26,10 +26,10 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.aggregation.AggregatorFactory;
import org.apache.druid.query.aggregation.histogram.ApproximateHistogram;
@@ -41,6 +41,7 @@ import org.apache.druid.segment.column.ValueType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.Aggregations;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.planner.PlannerContext;
import org.apache.druid.sql.calcite.rel.InputAccessor;
@@ -196,9 +197,6 @@ public class QuantileSqlAggregator implements SqlAggregator
private static class QuantileSqlAggFunction extends SqlAggFunction
{
- private static final String SIGNATURE1 = "'" + NAME + "(column,
probability)'";
- private static final String SIGNATURE2 = "'" + NAME + "(column,
probability, resolution)'";
-
QuantileSqlAggFunction()
{
super(
@@ -207,19 +205,19 @@ public class QuantileSqlAggregator implements
SqlAggregator
SqlKind.OTHER_FUNCTION,
ReturnTypes.explicit(SqlTypeName.DOUBLE),
null,
- OperandTypes.or(
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE1, OperandTypes.ANY,
OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
- ),
- OperandTypes.and(
- OperandTypes.sequence(SIGNATURE2, OperandTypes.ANY,
OperandTypes.LITERAL, OperandTypes.LITERAL),
- OperandTypes.family(SqlTypeFamily.ANY,
SqlTypeFamily.NUMERIC, SqlTypeFamily.EXACT_NUMERIC)
- )
- ),
+ // Checks for signatures like 'APPROX_QUANTILE(column, probability)'
and
+ // 'APPROX_QUANTILE(column, probability, resolution)'
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("column", "probability", "resolution")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC,
SqlTypeFamily.EXACT_NUMERIC)
+ .requiredOperandCount(2)
+ .literalOperands(1, 2)
+ .build(),
SqlFunctionCategory.NUMERIC,
false,
- false
+ false,
+ Optionality.FORBIDDEN
);
}
}
diff --git
a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
index d0a12ab2f2d..c54231c86d8 100644
---
a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
+++
b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/FixedBucketsHistogramQuantileSqlAggregatorTest.java
@@ -128,6 +128,9 @@ public class FixedBucketsHistogramQuantileSqlAggregatorTest
extends BaseCalciteQ
6.494999885559082,
5.497499942779541,
6.499499797821045,
+ 6.499499797821045,
+ 6.499499797821045,
+ 6.499499797821045,
1.25
}
);
@@ -142,6 +145,9 @@ public class FixedBucketsHistogramQuantileSqlAggregatorTest
extends BaseCalciteQ
+ "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.99, 20, 0.0, 10.0) FILTER(WHERE
dim1 = 'abc'),\n"
+ "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.999, 20, 0.0, 10.0)
FILTER(WHERE dim1 <> 'abc'),\n"
+ "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.999, 20, 0.0, 10.0)
FILTER(WHERE dim1 = 'abc'),\n"
+ + "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.999, 20, 0.0, 10.0, 'ignore')
FILTER(WHERE dim1 = 'abc'),\n"
+ + "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.999, 20, 0.0, 10.0, 'clip')
FILTER(WHERE dim1 = 'abc'),\n"
+ + "APPROX_QUANTILE_FIXED_BUCKETS(m1, 0.999, 20, 0.0, 10.0, 'overflow')
FILTER(WHERE dim1 = 'abc'),\n"
+ "APPROX_QUANTILE_FIXED_BUCKETS(cnt, 0.5, 20, 0.0, 10.0)\n"
+ "FROM foo",
ImmutableList.of(
@@ -200,8 +206,32 @@ public class
FixedBucketsHistogramQuantileSqlAggregatorTest extends BaseCalciteQ
),
not(equality("dim1", "abc", ColumnType.STRING))
),
+ new FilteredAggregatorFactory(
+ new FixedBucketsHistogramAggregatorFactory(
+ "a9:agg",
+ "m1",
+ 20,
+ 0.0d,
+ 10.0d,
+ FixedBucketsHistogram.OutlierHandlingMode.CLIP,
+ false
+ ),
+ equality("dim1", "abc", ColumnType.STRING)
+ ),
+ new FilteredAggregatorFactory(
+ new FixedBucketsHistogramAggregatorFactory(
+ "a10:agg",
+ "m1",
+ 20,
+ 0.0d,
+ 10.0d,
+
FixedBucketsHistogram.OutlierHandlingMode.OVERFLOW,
+ false
+ ),
+ equality("dim1", "abc", ColumnType.STRING)
+ ),
new FixedBucketsHistogramAggregatorFactory(
- "a8:agg",
+ "a11:agg",
"cnt",
20,
0.0d,
@@ -219,7 +249,55 @@ public class
FixedBucketsHistogramQuantileSqlAggregatorTest extends BaseCalciteQ
new QuantilePostAggregator("a5", "a5:agg", 0.99f),
new QuantilePostAggregator("a6", "a6:agg", 0.999f),
new QuantilePostAggregator("a7", "a5:agg", 0.999f),
- new QuantilePostAggregator("a8", "a8:agg", 0.50f)
+ new QuantilePostAggregator("a8", "a5:agg", 0.999f),
+ new QuantilePostAggregator("a9", "a9:agg", 0.999f),
+ new QuantilePostAggregator("a10", "a10:agg", 0.999f),
+ new QuantilePostAggregator("a11", "a11:agg", 0.50f)
+ )
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ expectedResults
+ );
+ }
+
+ @Test
+ public void testQuantileWithCastedLiteralArguments()
+ {
+ final List<Object[]> expectedResults = ImmutableList.of(new
Object[]{6.499499797821045});
+ testQuery(
+ "SELECT\n"
+ + "APPROX_QUANTILE_FIXED_BUCKETS("
+ + "m1, "
+ + "CAST(0.999 AS DOUBLE), "
+ + "CAST(20 AS INTEGER), "
+ + "CAST(0.0 AS DOUBLE), "
+ + "CAST(10.0 AS DOUBLE), "
+ + "CAST('overflow' AS VARCHAR)"
+ + ") "
+ + "FILTER(WHERE dim1 = 'abc')\n"
+ + "FROM foo",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(new
MultipleIntervalSegmentSpec(ImmutableList.of(Filtration.eternity())))
+ .granularity(Granularities.ALL)
+ .aggregators(ImmutableList.of(
+ new FilteredAggregatorFactory(
+ new FixedBucketsHistogramAggregatorFactory(
+ "a0:agg",
+ "m1",
+ 20,
+ 0.0d,
+ 10.0d,
+
FixedBucketsHistogram.OutlierHandlingMode.OVERFLOW,
+ false
+ ),
+ equality("dim1", "abc", ColumnType.STRING)
+ )
+ ))
+ .postAggregators(
+ new QuantilePostAggregator("a0", "a0:agg", 0.999f)
)
.context(QUERY_CONTEXT_DEFAULT)
.build()
diff --git
a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
index 9e36def53cb..6bcd26d23c1 100644
---
a/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
+++
b/extensions-core/histogram/src/test/java/org/apache/druid/query/aggregation/histogram/sql/QuantileSqlAggregatorTest.java
@@ -121,6 +121,7 @@ public class QuantileSqlAggregatorTest extends
BaseCalciteQueryTest
"SELECT\n"
+ "APPROX_QUANTILE(m1, 0.01),\n"
+ "APPROX_QUANTILE(m1, 0.5, 50),\n"
+ + "APPROX_QUANTILE(m1, CAST(0.5 AS DOUBLE), CAST(50 AS INTEGER)),\n"
+ "APPROX_QUANTILE(m1, 0.98, 200),\n"
+ "APPROX_QUANTILE(m1, 0.99),\n"
+ "APPROX_QUANTILE(m1 * 2, 0.97),\n"
@@ -144,28 +145,29 @@ public class QuantileSqlAggregatorTest extends
BaseCalciteQueryTest
)
.aggregators(ImmutableList.of(
new ApproximateHistogramAggregatorFactory("a0:agg",
"m1", null, null, null, null, false),
- new ApproximateHistogramAggregatorFactory("a2:agg",
"m1", 200, null, null, null, false),
- new ApproximateHistogramAggregatorFactory("a4:agg",
"v0", null, null, null, null, false),
+ new ApproximateHistogramAggregatorFactory("a3:agg",
"m1", 200, null, null, null, false),
+ new ApproximateHistogramAggregatorFactory("a5:agg",
"v0", null, null, null, null, false),
new FilteredAggregatorFactory(
- new ApproximateHistogramAggregatorFactory("a5:agg",
"m1", null, null, null, null, false),
+ new ApproximateHistogramAggregatorFactory("a6:agg",
"m1", null, null, null, null, false),
equality("dim1", "abc", ColumnType.STRING)
),
new FilteredAggregatorFactory(
- new ApproximateHistogramAggregatorFactory("a6:agg",
"m1", null, null, null, null, false),
+ new ApproximateHistogramAggregatorFactory("a7:agg",
"m1", null, null, null, null, false),
not(equality("dim1", "abc", ColumnType.STRING))
),
- new ApproximateHistogramAggregatorFactory("a8:agg",
"cnt", null, null, null, null, false)
+ new ApproximateHistogramAggregatorFactory("a9:agg",
"cnt", null, null, null, null, false)
))
.postAggregators(
new QuantilePostAggregator("a0", "a0:agg", 0.01f),
new QuantilePostAggregator("a1", "a0:agg", 0.50f),
- new QuantilePostAggregator("a2", "a2:agg", 0.98f),
- new QuantilePostAggregator("a3", "a0:agg", 0.99f),
- new QuantilePostAggregator("a4", "a4:agg", 0.97f),
- new QuantilePostAggregator("a5", "a5:agg", 0.99f),
- new QuantilePostAggregator("a6", "a6:agg", 0.999f),
- new QuantilePostAggregator("a7", "a5:agg", 0.999f),
- new QuantilePostAggregator("a8", "a8:agg", 0.50f)
+ new QuantilePostAggregator("a2", "a0:agg", 0.50f),
+ new QuantilePostAggregator("a3", "a3:agg", 0.98f),
+ new QuantilePostAggregator("a4", "a0:agg", 0.99f),
+ new QuantilePostAggregator("a5", "a5:agg", 0.97f),
+ new QuantilePostAggregator("a6", "a6:agg", 0.99f),
+ new QuantilePostAggregator("a7", "a7:agg", 0.999f),
+ new QuantilePostAggregator("a8", "a6:agg", 0.999f),
+ new QuantilePostAggregator("a9", "a9:agg", 0.50f)
)
.context(QUERY_CONTEXT_DEFAULT)
.build()
@@ -174,6 +176,7 @@ public class QuantileSqlAggregatorTest extends
BaseCalciteQueryTest
new Object[]{
1.0,
3.0,
+ 3.0,
5.880000114440918,
5.940000057220459,
11.640000343322754,
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestAnySqlAggregator.java
b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestAnySqlAggregator.java
index 2e031616027..efa3a9e7e32 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestAnySqlAggregator.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestAnySqlAggregator.java
@@ -33,8 +33,8 @@ import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperatorBinding;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.InferTypes;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.util.SqlVisitor;
@@ -61,6 +61,7 @@ import org.apache.druid.segment.column.ColumnHolder;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
import org.apache.druid.sql.calcite.planner.Calcites;
@@ -369,14 +370,13 @@ public class EarliestLatestAnySqlAggregator implements
SqlAggregator
SqlKind.OTHER_FUNCTION,
EARLIEST_LATEST_ARG0_RETURN_TYPE_INFERENCE,
InferTypes.RETURN_TYPE,
- OperandTypes.or(
- OperandTypes.ANY,
- OperandTypes.sequence(
- "'" + aggregatorType.name() + "(expr, maxBytesPerString)'",
- OperandTypes.ANY,
- OperandTypes.and(OperandTypes.NUMERIC, OperandTypes.LITERAL)
- )
- ),
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("expr", "maxBytesPerString")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.NUMERIC)
+ .requiredOperandCount(1)
+ .literalOperands(1)
+ .build(),
SqlFunctionCategory.USER_DEFINED_FUNCTION,
false,
false,
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestBySqlAggregator.java
b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestBySqlAggregator.java
index c12be459cf5..03e23503a81 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestBySqlAggregator.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/aggregation/builtin/EarliestLatestBySqlAggregator.java
@@ -26,7 +26,6 @@ import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.InferTypes;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.util.Optionality;
@@ -38,6 +37,7 @@ import
org.apache.druid.query.aggregation.post.FinalizingFieldAccessPostAggregat
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.sql.calcite.aggregation.Aggregation;
import org.apache.druid.sql.calcite.aggregation.SqlAggregator;
+import org.apache.druid.sql.calcite.expression.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
import org.apache.druid.sql.calcite.planner.Calcites;
@@ -176,19 +176,13 @@ public class EarliestLatestBySqlAggregator implements
SqlAggregator
SqlKind.OTHER_FUNCTION,
EARLIEST_LATEST_ARG0_RETURN_TYPE_INFERENCE,
InferTypes.RETURN_TYPE,
- OperandTypes.or(
- OperandTypes.sequence(
- "'" + StringUtils.format("%s_BY", aggregatorType.name()) +
"(expr, timeColumn)'",
- OperandTypes.ANY,
- OperandTypes.family(SqlTypeFamily.TIMESTAMP)
- ),
- OperandTypes.sequence(
- "'" + StringUtils.format("%s_BY", aggregatorType.name()) +
"(expr, timeColumn, maxBytesPerString)'",
- OperandTypes.ANY,
- OperandTypes.family(SqlTypeFamily.TIMESTAMP),
- OperandTypes.and(OperandTypes.NUMERIC, OperandTypes.LITERAL)
- )
- ),
+ DefaultOperandTypeChecker
+ .builder()
+ .operandNames("expr", "timeColumn", "maxBytesPerString")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.TIMESTAMP,
SqlTypeFamily.NUMERIC)
+ .requiredOperandCount(2)
+ .literalOperands(2)
+ .build(),
SqlFunctionCategory.USER_DEFINED_FUNCTION,
false,
false,
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/BasicOperandTypeChecker.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/DefaultOperandTypeChecker.java
similarity index 62%
rename from
sql/src/main/java/org/apache/druid/sql/calcite/expression/BasicOperandTypeChecker.java
rename to
sql/src/main/java/org/apache/druid/sql/calcite/expression/DefaultOperandTypeChecker.java
index 177d4447f4e..f43fde3a935 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/BasicOperandTypeChecker.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/DefaultOperandTypeChecker.java
@@ -24,7 +24,6 @@ import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperandCountRange;
@@ -35,35 +34,47 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.Static;
+import org.apache.druid.java.util.common.ISE;
import javax.annotation.Nullable;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import java.util.function.Function;
import java.util.stream.IntStream;
/**
- * Operand type checker that is used in simple situations: there are a
particular number of operands, with
+ * Operand type checker that is used in 'simple' situations: there are a
particular number of operands, with
* particular types, some of which may be optional or nullable, and some of
which may be required to be literals.
*/
-public class BasicOperandTypeChecker implements SqlOperandTypeChecker
+public class DefaultOperandTypeChecker implements SqlOperandTypeChecker
{
+ /**
+ * Operand names for {@link #getAllowedSignatures(SqlOperator, String)}. May
be empty, in which case the
+ * {@link #operandTypes} are used instead.
+ */
+ private final List<String> operandNames;
private final List<SqlTypeFamily> operandTypes;
private final int requiredOperands;
- private final IntSet nullOperands;
+ private final IntSet nullableOperands;
private final IntSet literalOperands;
- BasicOperandTypeChecker(
+ private DefaultOperandTypeChecker(
+ final List<String> operandNames,
final List<SqlTypeFamily> operandTypes,
final int requiredOperands,
- final IntSet nullOperands,
+ final IntSet nullableOperands,
@Nullable final int[] literalOperands
)
{
Preconditions.checkArgument(requiredOperands <= operandTypes.size() &&
requiredOperands >= 0);
+ this.operandNames = Preconditions.checkNotNull(operandNames,
"operandNames");
this.operandTypes = Preconditions.checkNotNull(operandTypes,
"operandTypes");
this.requiredOperands = requiredOperands;
- this.nullOperands = Preconditions.checkNotNull(nullOperands,
"nullOperands");
+ this.nullableOperands = Preconditions.checkNotNull(nullableOperands,
"nullableOperands");
+
+ if (!operandNames.isEmpty() && operandNames.size() != operandTypes.size())
{
+ throw new ISE("Operand name count[%s] and type count[%s] must match",
operandNames.size(), operandTypes.size());
+ }
if (literalOperands == null) {
this.literalOperands = IntSets.EMPTY_SET;
@@ -78,19 +89,6 @@ public class BasicOperandTypeChecker implements
SqlOperandTypeChecker
return new Builder();
}
- public static boolean throwOrReturn(
- final boolean throwOnFailure,
- final SqlCallBinding callBinding,
- final Function<SqlCallBinding, CalciteException> exceptionMapper
- )
- {
- if (throwOnFailure) {
- throw exceptionMapper.apply(callBinding);
- } else {
- return false;
- }
- }
-
@Override
public boolean checkOperandTypes(SqlCallBinding callBinding, boolean
throwOnFailure)
{
@@ -98,9 +96,9 @@ public class BasicOperandTypeChecker implements
SqlOperandTypeChecker
final SqlNode operand = callBinding.operands().get(i);
if (literalOperands.contains(i)) {
- // Verify that 'operand' is a literal.
- if (!SqlUtil.isLiteral(operand)) {
- return throwOrReturn(
+ // Verify that 'operand' is a literal. Allow CAST, since we can reduce
these away later.
+ if (!SqlUtil.isLiteral(operand, true)) {
+ return OperatorConversions.throwOrReturn(
throwOnFailure,
callBinding,
cb -> cb.getValidator()
@@ -121,15 +119,15 @@ public class BasicOperandTypeChecker implements
SqlOperandTypeChecker
// Operand came in with one of the expected types.
} else if (operandType.getSqlTypeName() == SqlTypeName.NULL ||
SqlUtil.isNullLiteral(operand, true)) {
// Null came in, check if operand is a nullable type.
- if (!nullOperands.contains(i)) {
- return throwOrReturn(
+ if (!nullableOperands.contains(i)) {
+ return OperatorConversions.throwOrReturn(
throwOnFailure,
callBinding,
cb -> cb.getValidator().newValidationError(operand,
Static.RESOURCE.nullIllegal())
);
}
} else {
- return throwOrReturn(
+ return OperatorConversions.throwOrReturn(
throwOnFailure,
callBinding,
SqlCallBinding::newValidationSignatureError
@@ -149,7 +147,25 @@ public class BasicOperandTypeChecker implements
SqlOperandTypeChecker
@Override
public String getAllowedSignatures(SqlOperator op, String opName)
{
- return SqlUtil.getAliasedSignature(op, opName, operandTypes);
+ final List<?> operands = !operandNames.isEmpty() ? operandNames :
operandTypes;
+ final StringBuilder ret = new StringBuilder();
+ ret.append("'");
+ ret.append(opName);
+ ret.append("(");
+ for (int i = 0; i < operands.size(); i++) {
+ if (i > 0) {
+ ret.append(", ");
+ }
+ if (i >= requiredOperands) {
+ ret.append("[");
+ }
+ ret.append("<").append(operands.get(i)).append(">");
+ }
+ for (int i = requiredOperands; i < operands.size(); i++) {
+ ret.append("]");
+ }
+ ret.append(")'");
+ return ret.toString();
}
@Override
@@ -166,64 +182,70 @@ public class BasicOperandTypeChecker implements
SqlOperandTypeChecker
public static class Builder
{
+ private List<String> operandNames = Collections.emptyList();
private List<SqlTypeFamily> operandTypes;
- private Integer requiredOperandCount = null;
- private int[] literalOperands = null;
- /**
- * Signifies that a function accepts operands of type family given by
{@param operandTypes}.
- */
+ @Nullable
+ private Integer requiredOperandCount;
+ private int[] literalOperands;
+
+ private Builder()
+ {
+ }
+
+ public Builder operandNames(final String... operandNames)
+ {
+ this.operandNames = Arrays.asList(operandNames);
+ return this;
+ }
+
+ public Builder operandNames(final List<String> operandNames)
+ {
+ this.operandNames = operandNames;
+ return this;
+ }
+
public Builder operandTypes(final SqlTypeFamily... operandTypes)
{
this.operandTypes = Arrays.asList(operandTypes);
return this;
}
- /**
- * Signifies that a function accepts operands of type family given by
{@param operandTypes}.
- */
public Builder operandTypes(final List<SqlTypeFamily> operandTypes)
{
this.operandTypes = operandTypes;
return this;
}
- /**
- * Signifies that the first {@code requiredOperands} operands are
required, and all later operands are optional.
- *
- * Required operands are not allowed to be null. Optional operands can
either be skipped or explicitly provided as
- * literal NULLs. For example, if {@code requiredOperands == 1}, then
{@code F(x, NULL)} and {@code F(x)} are both
- * accepted, and {@code x} must not be null.
- */
- public Builder requiredOperandCount(final int requiredOperandCount)
+ public Builder requiredOperandCount(Integer requiredOperandCount)
{
this.requiredOperandCount = requiredOperandCount;
return this;
}
- /**
- * Signifies that the operands at positions given by {@code
literalOperands} must be literals.
- */
public Builder literalOperands(final int... literalOperands)
{
this.literalOperands = literalOperands;
return this;
}
- public BasicOperandTypeChecker build()
+ public DefaultOperandTypeChecker build()
{
- // Create "nullableOperands" set including all optional arguments.
- final IntSet nullableOperands = new IntArraySet();
- if (requiredOperandCount != null) {
- IntStream.range(requiredOperandCount,
operandTypes.size()).forEach(nullableOperands::add);
- }
-
- return new BasicOperandTypeChecker(
+ int computedRequiredOperandCount = requiredOperandCount == null ?
operandTypes.size() : requiredOperandCount;
+ return new DefaultOperandTypeChecker(
+ operandNames,
operandTypes,
- requiredOperandCount == null ? operandTypes.size() :
requiredOperandCount,
- nullableOperands,
+ computedRequiredOperandCount,
+
DefaultOperandTypeChecker.buildNullableOperands(computedRequiredOperandCount,
operandTypes.size()),
literalOperands
);
}
}
+
+ public static IntSet buildNullableOperands(int requiredOperandCount, int
totalOperandCount)
+ {
+ final IntSet nullableOperands = new IntArraySet();
+ IntStream.range(requiredOperandCount,
totalOperandCount).forEach(nullableOperands::add);
+ return nullableOperands;
+ }
}
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
index e8a2b796a26..39137b77aeb 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/OperatorConversions.java
@@ -19,13 +19,11 @@
package org.apache.druid.sql.calcite.expression;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
-import it.unimi.dsi.fastutil.ints.IntSets;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexInputRef;
@@ -37,13 +35,9 @@ import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlOperandCountRange;
import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.type.BasicSqlType;
import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandCountRanges;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlOperandTypeInference;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
@@ -51,7 +45,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeTransforms;
import org.apache.calcite.util.Optionality;
-import org.apache.calcite.util.Static;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.java.util.common.StringUtils;
@@ -69,7 +63,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
-import java.util.stream.IntStream;
/**
* Utilities for assisting in writing {@link SqlOperatorConversion}
implementations.
@@ -334,6 +327,13 @@ public class OperatorConversions
return new AggregatorBuilder(name);
}
+ /**
+ * Helps in creating the operator builder along with validations and type
inference for simple operator conversions.
+ *
+ * The type checker for this operator conversion can be either supplied
manually, or an instance of
+ * {@link DefaultOperandTypeChecker} will be used if the user passes in
{@link #operandTypes} and other optional
+ * parameters. Exactly one of them must be supplied to the builder.
+ */
public static class OperatorBuilder<T extends SqlFunction>
{
protected final String name;
@@ -491,8 +491,8 @@ public class OperatorConversions
}
/**
- * Equivalent to calling {@link
BasicOperandTypeChecker.Builder#operandTypes(SqlTypeFamily...)}; leads to using
a
- * {@link BasicOperandTypeChecker} as our operand type checker.
+ * Equivalent to calling {@link
DefaultOperandTypeChecker.Builder#operandTypes(SqlTypeFamily...)}; leads to
using a
+ * {@link DefaultOperandTypeChecker} as our operand type checker.
*
* May be used in conjunction with {@link #requiredOperandCount(int)} and
{@link #literalOperands(int...)} in order
* to further refine operand checking logic.
@@ -506,12 +506,9 @@ public class OperatorConversions
}
/**
- * Equivalent to calling {@link
BasicOperandTypeChecker.Builder#requiredOperandCount(int)}; leads to using a
- * {@link BasicOperandTypeChecker} as our operand type checker.
- *
- * Not compatible with {@link #operandTypeChecker(SqlOperandTypeChecker)}.
+ * Equivalent to calling {@link
DefaultOperandTypeChecker.Builder#requiredOperandCount(Integer)}; leads to
using a
+ * {@link DefaultOperandTypeChecker} as our operand type checker.
*/
- @Deprecated
public OperatorBuilder<T> requiredOperandCount(final int
requiredOperandCount)
{
this.requiredOperandCount = requiredOperandCount;
@@ -531,8 +528,8 @@ public class OperatorConversions
}
/**
- * Equivalent to calling {@link
BasicOperandTypeChecker.Builder#literalOperands(int...)}; leads to using a
- * {@link BasicOperandTypeChecker} as our operand type checker.
+ * Equivalent to calling {@link
DefaultOperandTypeChecker.Builder#literalOperands(int...)}; leads to using a
+ * {@link DefaultOperandTypeChecker} as our operand type checker.
*
* Not compatible with {@link #operandTypeChecker(SqlOperandTypeChecker)}.
*/
@@ -554,37 +551,30 @@ public class OperatorConversions
@SuppressWarnings("unchecked")
public T build()
{
- final IntSet nullableOperands = buildNullableOperands();
return (T) new SqlFunction(
name,
kind,
Preconditions.checkNotNull(returnTypeInference,
"returnTypeInference"),
- buildOperandTypeInference(nullableOperands),
- buildOperandTypeChecker(nullableOperands),
+ buildOperandTypeInference(),
+ buildOperandTypeChecker(),
functionCategory
);
}
- protected IntSet buildNullableOperands()
- {
- // Create "nullableOperands" set including all optional arguments.
- final IntSet nullableOperands = new IntArraySet();
- if (requiredOperandCount != null) {
- IntStream.range(requiredOperandCount,
operandTypes.size()).forEach(nullableOperands::add);
- }
- return nullableOperands;
- }
-
- protected SqlOperandTypeChecker buildOperandTypeChecker(final IntSet
nullableOperands)
+ protected SqlOperandTypeChecker buildOperandTypeChecker()
{
if (operandTypeChecker == null) {
- return new DefaultOperandTypeChecker(
- operandNames,
- operandTypes,
- requiredOperandCount == null ? operandTypes.size() :
requiredOperandCount,
- nullableOperands,
- literalOperands
- );
+ if (operandTypes == null) {
+ throw DruidException.defensive(
+ "'operandTypes' must be non null if 'operandTypeChecker' is not
passed to the operator conversion.");
+ }
+ return DefaultOperandTypeChecker
+ .builder()
+ .operandNames(operandNames)
+ .operandTypes(operandTypes)
+ .requiredOperandCount(requiredOperandCount == null ?
operandTypes.size() : requiredOperandCount)
+ .literalOperands(literalOperands)
+ .build();
} else if (operandNames.isEmpty()
&& operandTypes == null
&& requiredOperandCount == null
@@ -598,8 +588,11 @@ public class OperatorConversions
}
}
- protected SqlOperandTypeInference buildOperandTypeInference(final IntSet
nullableOperands)
+ protected SqlOperandTypeInference buildOperandTypeInference()
{
+ final IntSet nullableOperands = requiredOperandCount == null
+ ? new IntArraySet()
+ :
DefaultOperandTypeChecker.buildNullableOperands(requiredOperandCount,
operandTypes.size());
if (operandTypeInference == null) {
SqlOperandTypeInference defaultInference = new
DefaultOperandTypeInference(operandTypes, nullableOperands);
return (callBinding, returnType, types) -> {
@@ -634,9 +627,8 @@ public class OperatorConversions
@Override
public SqlAggFunction build()
{
- final IntSet nullableOperands = buildNullableOperands();
- final SqlOperandTypeInference operandTypeInference =
buildOperandTypeInference(nullableOperands);
- final SqlOperandTypeChecker operandTypeChecker =
buildOperandTypeChecker(nullableOperands);
+ final SqlOperandTypeInference operandTypeInference =
buildOperandTypeInference();
+ final SqlOperandTypeChecker operandTypeChecker =
buildOperandTypeChecker();
class DruidSqlAggFunction extends SqlAggFunction
{
@@ -735,147 +727,6 @@ public class OperatorConversions
}
}
- /**
- * Operand type checker that is used in 'simple' situations: there are a
particular number of operands, with
- * particular types, some of which may be optional or nullable, and some of
which may be required to be literals.
- */
- @VisibleForTesting
- static class DefaultOperandTypeChecker implements SqlOperandTypeChecker
- {
- /**
- * Operand names for {@link #getAllowedSignatures(SqlOperator, String)}.
May be empty, in which case the
- * {@link #operandTypes} are used instead.
- */
- private final List<String> operandNames;
- private final List<SqlTypeFamily> operandTypes;
- private final int requiredOperands;
- private final IntSet nullableOperands;
- private final IntSet literalOperands;
-
- public int getNumberOfLiteralOperands()
- {
- return literalOperands.size();
- }
-
- @VisibleForTesting
- DefaultOperandTypeChecker(
- final List<String> operandNames,
- final List<SqlTypeFamily> operandTypes,
- final int requiredOperands,
- final IntSet nullableOperands,
- @Nullable final int[] literalOperands
- )
- {
- Preconditions.checkArgument(requiredOperands <= operandTypes.size() &&
requiredOperands >= 0);
- this.operandNames = Preconditions.checkNotNull(operandNames,
"operandNames");
- this.operandTypes = Preconditions.checkNotNull(operandTypes,
"operandTypes");
- this.requiredOperands = requiredOperands;
- this.nullableOperands = Preconditions.checkNotNull(nullableOperands,
"nullableOperands");
-
- if (!operandNames.isEmpty() && operandNames.size() !=
operandTypes.size()) {
- throw new ISE("Operand name count[%s] and type count[%s] must match",
operandNames.size(), operandTypes.size());
- }
-
- if (literalOperands == null) {
- this.literalOperands = IntSets.EMPTY_SET;
- } else {
- this.literalOperands = new IntArraySet();
- Arrays.stream(literalOperands).forEach(this.literalOperands::add);
- }
- }
-
- @Override
- public boolean checkOperandTypes(SqlCallBinding callBinding, boolean
throwOnFailure)
- {
- for (int i = 0; i < callBinding.operands().size(); i++) {
- final SqlNode operand = callBinding.operands().get(i);
-
- if (literalOperands.contains(i)) {
- // Verify that 'operand' is a literal. Allow CAST, since we can
reduce these away later.
- if (!SqlUtil.isLiteral(operand, true)) {
- return throwOrReturn(
- throwOnFailure,
- callBinding,
- cb -> cb.getValidator()
- .newValidationError(
- operand,
-
Static.RESOURCE.argumentMustBeLiteral(callBinding.getOperator().getName())
- )
- );
- }
- }
-
- final RelDataType operandType =
callBinding.getValidator().deriveType(callBinding.getScope(), operand);
- final SqlTypeFamily expectedFamily = operandTypes.get(i);
-
- if (expectedFamily == SqlTypeFamily.ANY) {
- // ANY matches anything. This operand is all good; do nothing.
- } else if
(expectedFamily.getTypeNames().contains(operandType.getSqlTypeName())) {
- // Operand came in with one of the expected types.
- } else if (operandType.getSqlTypeName() == SqlTypeName.NULL ||
SqlUtil.isNullLiteral(operand, true)) {
- // Null came in, check if operand is a nullable type.
- if (!nullableOperands.contains(i)) {
- return throwOrReturn(
- throwOnFailure,
- callBinding,
- cb -> cb.getValidator().newValidationError(operand,
Static.RESOURCE.nullIllegal())
- );
- }
- } else {
- return throwOrReturn(
- throwOnFailure,
- callBinding,
- SqlCallBinding::newValidationSignatureError
- );
- }
- }
-
- return true;
- }
-
- @Override
- public SqlOperandCountRange getOperandCountRange()
- {
- return SqlOperandCountRanges.between(requiredOperands,
operandTypes.size());
- }
-
- @Override
- public String getAllowedSignatures(SqlOperator op, String opName)
- {
- final List<?> operands = !operandNames.isEmpty() ? operandNames :
operandTypes;
- final StringBuilder ret = new StringBuilder();
- ret.append("'");
- ret.append(opName);
- ret.append("(");
- for (int i = 0; i < operands.size(); i++) {
- if (i > 0) {
- ret.append(", ");
- }
- if (i >= requiredOperands) {
- ret.append("[");
- }
- ret.append("<").append(operands.get(i)).append(">");
- }
- for (int i = requiredOperands; i < operands.size(); i++) {
- ret.append("]");
- }
- ret.append(")'");
- return ret.toString();
- }
-
- @Override
- public Consistency getConsistency()
- {
- return Consistency.NONE;
- }
-
- @Override
- public boolean isOptional(int i)
- {
- return i + 1 > requiredOperands;
- }
- }
-
public static boolean throwOrReturn(
final boolean throwOnFailure,
final SqlCallBinding callBinding,
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ComplexDecodeBase64OperatorConversion.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ComplexDecodeBase64OperatorConversion.java
index 94b90ed9af8..51bf88c363a 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ComplexDecodeBase64OperatorConversion.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/ComplexDecodeBase64OperatorConversion.java
@@ -23,7 +23,6 @@ import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlOperator;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.SqlReturnTypeInference;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.druid.java.util.common.StringUtils;
@@ -52,13 +51,10 @@ public class ComplexDecodeBase64OperatorConversion
implements SqlOperatorConvers
private static final SqlFunction SQL_FUNCTION = OperatorConversions
.operatorBuilder(StringUtils.toUpperCase(BuiltInExprMacros.ComplexDecodeBase64ExprMacro.NAME))
- .operandTypeChecker(
- OperandTypes.sequence(
- "'" +
StringUtils.toUpperCase(BuiltInExprMacros.ComplexDecodeBase64ExprMacro.NAME) +
"(typeName, base64)'",
- OperandTypes.and(OperandTypes.family(SqlTypeFamily.STRING),
OperandTypes.LITERAL),
- OperandTypes.ANY
- )
- )
+ .operandNames("typeName", "base64")
+ .operandTypes(SqlTypeFamily.STRING, SqlTypeFamily.ANY)
+ .requiredOperandCount(2)
+ .literalOperands(0)
.returnTypeInference(ARBITRARY_COMPLEX_RETURN_TYPE_INFERENCE)
.functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
.build();
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/NestedDataOperatorConversions.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/NestedDataOperatorConversions.java
index ffaa42b1b65..2ba07c126af 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/NestedDataOperatorConversions.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/NestedDataOperatorConversions.java
@@ -118,13 +118,10 @@ public class NestedDataOperatorConversions
{
private static final SqlFunction SQL_FUNCTION = OperatorConversions
.operatorBuilder("JSON_KEYS")
- .operandTypeChecker(
- OperandTypes.sequence(
- "'JSON_KEYS(expr, path)'",
- OperandTypes.ANY,
- OperandTypes.and(OperandTypes.family(SqlTypeFamily.STRING),
OperandTypes.LITERAL)
- )
- )
+ .operandNames("expr", "path")
+ .operandTypes(SqlTypeFamily.ANY, SqlTypeFamily.STRING)
+ .literalOperands(1)
+ .requiredOperandCount(2)
.functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
.returnTypeNullableArrayWithNullableElements(SqlTypeName.VARCHAR)
.build();
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java
index 21d4bea356c..4f266e7a0e2 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/QueryLookupOperatorConversion.java
@@ -31,7 +31,6 @@ import org.apache.druid.math.expr.Expr;
import org.apache.druid.query.lookup.LookupExtractorFactoryContainerProvider;
import org.apache.druid.query.lookup.RegisteredLookupExtractionFn;
import org.apache.druid.segment.column.RowSignature;
-import org.apache.druid.sql.calcite.expression.BasicOperandTypeChecker;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.OperatorConversions;
import org.apache.druid.sql.calcite.expression.SqlOperatorConversion;
@@ -43,16 +42,10 @@ public class QueryLookupOperatorConversion implements
SqlOperatorConversion
{
private static final SqlFunction SQL_FUNCTION = OperatorConversions
.operatorBuilder("LOOKUP")
- .operandTypeChecker(
- BasicOperandTypeChecker.builder()
- .operandTypes(
- SqlTypeFamily.CHARACTER,
- SqlTypeFamily.CHARACTER,
- SqlTypeFamily.CHARACTER
- )
- .requiredOperandCount(2)
- .literalOperands(2)
- .build())
+ .operandNames("expr", "lookupName", "replaceMissingValueWith")
+ .operandTypes(SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER,
SqlTypeFamily.CHARACTER)
+ .requiredOperandCount(2)
+ .literalOperands(2)
.returnTypeNullable(SqlTypeName.VARCHAR)
.functionCategory(SqlFunctionCategory.STRING)
.build();
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/convertlet/TimeInIntervalConvertletFactory.java
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/convertlet/TimeInIntervalConvertletFactory.java
index b99043e5fcb..e3dcf71879a 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/convertlet/TimeInIntervalConvertletFactory.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/convertlet/TimeInIntervalConvertletFactory.java
@@ -27,7 +27,6 @@ import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql2rel.SqlRexContext;
@@ -53,13 +52,10 @@ public class TimeInIntervalConvertletFactory implements
DruidConvertletFactory
private static final SqlOperator OPERATOR = OperatorConversions
.operatorBuilder(NAME)
- .operandTypeChecker(
- OperandTypes.sequence(
- "'" + NAME + "(<TIMESTAMP>, <LITERAL ISO8601 INTERVAL>)'",
- OperandTypes.family(SqlTypeFamily.TIMESTAMP),
- OperandTypes.and(OperandTypes.family(SqlTypeFamily.CHARACTER),
OperandTypes.LITERAL)
- )
- )
+ .operandNames("timestamp", "interval")
+ .operandTypes(SqlTypeFamily.TIMESTAMP, SqlTypeFamily.CHARACTER)
+ .requiredOperandCount(2)
+ .literalOperands(1)
.returnTypeNonNull(SqlTypeName.BOOLEAN)
.functionCategory(SqlFunctionCategory.TIMEDATE)
.build();
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
index 315d6da4fd9..f65c98bc1a1 100644
---
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
+++
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteNestedDataQueryTest.java
@@ -4454,6 +4454,48 @@ public class CalciteNestedDataQueryTest extends
BaseCalciteQueryTest
);
}
+ @Test
+ public void testGroupByCastedRootKeysJsonPath()
+ {
+ cannotVectorize();
+ testQuery(
+ "SELECT "
+ + "JSON_KEYS(nester, CAST('$.' AS VARCHAR)), "
+ + "SUM(cnt) "
+ + "FROM druid.nested GROUP BY 1",
+ ImmutableList.of(
+ GroupByQuery.builder()
+ .setDataSource(DATA_SOURCE)
+ .setInterval(querySegmentSpec(Filtration.eternity()))
+ .setGranularity(Granularities.ALL)
+ .setVirtualColumns(
+ new ExpressionVirtualColumn(
+ "v0",
+ "json_keys(\"nester\",'$.')",
+ ColumnType.STRING_ARRAY,
+ queryFramework().macroTable()
+ )
+ )
+ .setDimensions(
+ dimensions(
+ new DefaultDimensionSpec("v0", "d0",
ColumnType.STRING_ARRAY)
+ )
+ )
+ .setAggregatorSpecs(aggregators(new
LongSumAggregatorFactory("a0", "cnt")))
+ .setContext(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{null, 5L},
+ new Object[]{"[\"array\",\"n\"]", 2L}
+ ),
+ RowSignature.builder()
+ .add("EXPR$0", ColumnType.STRING_ARRAY)
+ .add("EXPR$1", ColumnType.LONG)
+ .build()
+ );
+ }
+
@Test
public void testGroupByAllPaths()
{
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index b67db5dce41..702d73d9f9d 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -642,10 +642,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
testQuery(
"SELECT "
- + "EARLIEST(cnt), EARLIEST(m1), EARLIEST(dim1, 10), "
+ + "EARLIEST(cnt), EARLIEST(m1), EARLIEST(dim1, 10), EARLIEST(dim1,
CAST(10 AS INTEGER)), "
+ "EARLIEST(cnt + 1), EARLIEST(m1 + 1), EARLIEST(dim1 || CAST(cnt AS
VARCHAR), 10), "
+ "EARLIEST_BY(cnt, MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(m1,
MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(dim1, MILLIS_TO_TIMESTAMP(l1), 10), "
- + "EARLIEST_BY(cnt + 1, MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(m1 + 1,
MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), 10) "
+ + "EARLIEST_BY(cnt + 1, MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(m1 + 1,
MILLIS_TO_TIMESTAMP(l1)), EARLIEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), 10), EARLIEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), 10) "
+ "FROM druid.numfoo",
ImmutableList.of(
Druids.newTimeseriesQueryBuilder()
@@ -677,7 +677,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.build()
),
ImmutableList.of(
- new Object[]{1L, 1.0f, "", 2L, 2.0f, "1", 1L, 3.0f, "2", 2L, 4.0f,
"21"}
+ new Object[]{1L, 1.0f, "", "", 2L, 2.0f, "1", 1L, 3.0f, "2", 2L,
4.0f, "21", "21"}
)
);
}
@@ -752,10 +752,10 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
testQuery(
"SELECT "
- + "LATEST(cnt), LATEST(m1), LATEST(dim1, 10), "
+ + "LATEST(cnt), LATEST(m1), LATEST(dim1, 10), LATEST(dim1, CAST(10 AS
INTEGER)), "
+ "LATEST(cnt + 1), LATEST(m1 + 1), LATEST(dim1 || CAST(cnt AS
VARCHAR), 10), "
+ "LATEST_BY(cnt, MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(m1,
MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(dim1, MILLIS_TO_TIMESTAMP(l1), 10), "
- + "LATEST_BY(cnt + 1, MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(m1 + 1,
MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), 10) "
+ + "LATEST_BY(cnt + 1, MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(m1 + 1,
MILLIS_TO_TIMESTAMP(l1)), LATEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), 10), LATEST_BY(dim1 || CAST(cnt AS VARCHAR),
MILLIS_TO_TIMESTAMP(l1), CAST(10 AS INTEGER)) "
+ "FROM druid.numfoo",
ImmutableList.of(
Druids.newTimeseriesQueryBuilder()
@@ -787,7 +787,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
.build()
),
ImmutableList.of(
- new Object[]{1L, 6.0f, "abc", 2L, 7.0f, "abc1", 1L, 2.0f, "10.1",
2L, 3.0f, "10.11"}
+ new Object[]{1L, 6.0f, "abc", "abc", 2L, 7.0f, "abc1", 1L, 2.0f,
"10.1", 2L, 3.0f, "10.11", "10.11"}
)
);
}
@@ -5612,6 +5612,28 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
);
}
+ @Test
+ public void testCountStarWithTimeInCastedIntervalFilter()
+ {
+ testQuery(
+ "SELECT COUNT(*) FROM druid.foo "
+ + "WHERE TIME_IN_INTERVAL(__time, CAST('2000-01-01/P1Y' AS VARCHAR)) "
+ + "AND TIME_IN_INTERVAL(CURRENT_TIMESTAMP, '2000/3000') -- Optimized
away: always true",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+
.intervals(querySegmentSpec(Intervals.of("2000-01-01/2001-01-01")))
+ .granularity(Granularities.ALL)
+ .aggregators(aggregators(new CountAggregatorFactory("a0")))
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{3L}
+ )
+ );
+ }
+
@Test
public void testCountStarWithTimeInIntervalFilterLosAngeles()
{
@@ -5661,7 +5683,7 @@ public class CalciteQueryTest extends BaseCalciteQueryTest
expected -> {
expected.expect(CoreMatchers.instanceOf(DruidException.class));
expected.expect(ThrowableMessageMatcher.hasMessage(CoreMatchers.containsString(
- "Argument to function 'TIME_IN_INTERVAL' must be a literal (line
[1], column [38])")));
+ "Argument to function 'TIME_IN_INTERVAL' must be a literal (line
[1], column [63])")));
}
);
}
@@ -14076,6 +14098,7 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
);
}
}
+
@Test
public void testComplexDecodeAgg()
{
@@ -14109,6 +14132,43 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
)
);
}
+
+ @Test
+ public void testComplexDecodeAggWithCastedTypeName()
+ {
+ msqIncompatible();
+ cannotVectorize();
+ testQuery(
+ "SELECT "
+ +
"APPROX_COUNT_DISTINCT_BUILTIN(COMPLEX_DECODE_BASE64(CAST('hyperUnique' AS
VARCHAR),PARSE_JSON(TO_JSON_STRING(unique_dim1)))) "
+ + "FROM druid.foo",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(querySegmentSpec(Filtration.eternity()))
+ .virtualColumns(
+ expressionVirtualColumn(
+ "v0",
+
"complex_decode_base64('hyperUnique',parse_json(to_json_string(\"unique_dim1\")))",
+ ColumnType.ofComplex("hyperUnique")
+ )
+ )
+ .aggregators(
+ new HyperUniquesAggregatorFactory(
+ "a0",
+ "v0",
+ false,
+ true
+ )
+ )
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{6L}
+ )
+ );
+ }
+
@NotYetSupported
@Test
public void testOrderByAlongWithInternalScanQuery()
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/expression/OperatorConversionsTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/expression/OperatorConversionsTest.java
index a240a2c2986..5a0c52514f3 100644
---
a/sql/src/test/java/org/apache/druid/sql/calcite/expression/OperatorConversionsTest.java
+++
b/sql/src/test/java/org/apache/druid/sql/calcite/expression/OperatorConversionsTest.java
@@ -20,7 +20,6 @@
package org.apache.druid.sql.calcite.expression;
import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.ints.IntSets;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.runtime.Resources.ExInst;
@@ -38,7 +37,6 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.druid.java.util.common.StringUtils;
-import
org.apache.druid.sql.calcite.expression.OperatorConversions.DefaultOperandTypeChecker;
import org.apache.druid.sql.calcite.planner.DruidTypeSystem;
import org.junit.Assert;
import org.junit.Rule;
@@ -51,7 +49,6 @@ import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
@RunWith(Enclosed.class)
@@ -65,13 +62,13 @@ public class OperatorConversionsTest
@Test
public void testGetOperandCountRange()
{
- SqlOperandTypeChecker typeChecker = new DefaultOperandTypeChecker(
- Collections.emptyList(),
- ImmutableList.of(SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER,
SqlTypeFamily.INTEGER),
- 2,
- IntSets.EMPTY_SET,
- null
- );
+ SqlOperandTypeChecker typeChecker = DefaultOperandTypeChecker
+ .builder()
+ .operandNames()
+ .operandTypes(SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER,
SqlTypeFamily.INTEGER)
+ .requiredOperandCount(2)
+ .literalOperands()
+ .build();
SqlOperandCountRange countRange = typeChecker.getOperandCountRange();
Assert.assertEquals(2, countRange.getMin());
Assert.assertEquals(3, countRange.getMax());
@@ -80,13 +77,13 @@ public class OperatorConversionsTest
@Test
public void testIsOptional()
{
- SqlOperandTypeChecker typeChecker = new DefaultOperandTypeChecker(
- Collections.emptyList(),
- ImmutableList.of(SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER,
SqlTypeFamily.INTEGER),
- 2,
- IntSets.EMPTY_SET,
- null
- );
+ SqlOperandTypeChecker typeChecker = DefaultOperandTypeChecker
+ .builder()
+ .operandNames()
+ .operandTypes(SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER,
SqlTypeFamily.INTEGER)
+ .requiredOperandCount(2)
+ .literalOperands()
+ .build();
Assert.assertFalse(typeChecker.isOptional(0));
Assert.assertFalse(typeChecker.isOptional(1));
Assert.assertTrue(typeChecker.isOptional(2));
@@ -121,7 +118,7 @@ public class OperatorConversionsTest
{
SqlFunction function = OperatorConversions
.operatorBuilder("testRequiredOperandsOnly")
-
.operandTypeChecker(BasicOperandTypeChecker.builder().operandTypes(SqlTypeFamily.INTEGER,
SqlTypeFamily.DATE).requiredOperandCount(1).build())
+
.operandTypeChecker(DefaultOperandTypeChecker.builder().operandTypes(SqlTypeFamily.INTEGER,
SqlTypeFamily.DATE).requiredOperandCount(1).build())
.returnTypeNonNull(SqlTypeName.CHAR)
.build();
SqlOperandTypeChecker typeChecker = function.getOperandTypeChecker();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]