Repository: calcite Updated Branches: refs/heads/master 6f07293a3 -> a377f8f28
[CALCITE-1765] Druid adapter: Gracefully handle granularity that cannot be pushed to extraction function (Slim Bouguerra) Close apache/calcite#437 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/a377f8f2 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/a377f8f2 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/a377f8f2 Branch: refs/heads/master Commit: a377f8f2826f51c77b639ee711e2bd578f4de822 Parents: 6f07293 Author: Slim Bouguerra <[email protected]> Authored: Thu Apr 27 11:54:14 2017 -0700 Committer: Julian Hyde <[email protected]> Committed: Fri Apr 28 09:22:26 2017 -0700 ---------------------------------------------------------------------- .../adapter/druid/DruidDateTimeUtils.java | 25 +++---- .../calcite/adapter/druid/DruidQuery.java | 53 +++++++-------- .../calcite/adapter/druid/DruidRules.java | 22 +++---- .../adapter/druid/ExtractionDimensionSpec.java | 8 ++- .../adapter/druid/ExtractionFunctionUtil.java | 68 -------------------- .../druid/TimeExtractionDimensionSpec.java | 24 ++----- .../adapter/druid/TimeExtractionFunction.java | 37 ++++++++++- .../org/apache/calcite/test/DruidAdapterIT.java | 41 ++++++++++++ 8 files changed, 136 insertions(+), 142 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java index e3e86c3..0e2b3d3 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidDateTimeUtils.java @@ -278,21 +278,24 @@ public class DruidDateTimeUtils { * It support {@code FLOOR(<time> TO <timeunit>)} and {@code EXTRACT(<timeunit> FROM <time>)}. * It returns null if it cannot be inferred. * - * @param call the function call + * @param node the Rex node * @return the granularity, or null if it cannot be inferred */ - public static Granularity extractGranularity(RexCall call) { - if ((call.getKind() != SqlKind.FLOOR && call.getKind() != SqlKind.EXTRACT) - || call.getOperands().size() != 2) { - return null; - } - int flagIndex; - if (call.getKind() == SqlKind.EXTRACT) { - // EXTRACT + public static Granularity extractGranularity(RexNode node) { + final int flagIndex; + switch (node.getKind()) { + case EXTRACT: flagIndex = 0; - } else { - // FLOOR + break; + case FLOOR: flagIndex = 1; + break; + default: + return null; + } + final RexCall call = (RexCall) node; + if (call.operands.size() != 2) { + return null; } final RexLiteral flag = (RexLiteral) call.operands.get(flagIndex); final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue(); http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java index b287f45..504e06d 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidQuery.java @@ -18,7 +18,6 @@ package org.apache.calcite.adapter.druid; import org.apache.calcite.DataContext; import org.apache.calcite.avatica.ColumnMetaData; -import org.apache.calcite.avatica.util.TimeUnitRange; import org.apache.calcite.config.CalciteConnectionConfig; import org.apache.calcite.config.CalciteConnectionProperty; import org.apache.calcite.interpreter.BindableRel; @@ -90,10 +89,6 @@ import static org.apache.calcite.sql.SqlKind.INPUT_REF; */ public class DruidQuery extends AbstractRelNode implements BindableRel { - private static final List<TimeUnitRange> LIST_OF_VALID_TIME_EXTRACT = ImmutableList.of( - TimeUnitRange.YEAR, - TimeUnitRange.MONTH, - TimeUnitRange.DAY); protected QuerySpec querySpec; final RelOptTable table; @@ -230,22 +225,12 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { case CAST: return isValidCast((RexCall) e, boundedComparator); case EXTRACT: - return isValidExtract((RexCall) e); + return TimeExtractionFunction.isValidTimeExtract((RexCall) e); default: return false; } } - private boolean isValidExtract(RexCall call) { - assert call.isA(SqlKind.EXTRACT); - final RexLiteral flag = (RexLiteral) call.operands.get(0); - final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue(); - if (timeUnit != null && LIST_OF_VALID_TIME_EXTRACT.contains(timeUnit)) { - return true; - } - return false; - } - private boolean areValidFilters(List<RexNode> es, boolean boundedComparator) { for (RexNode e : es) { if (!isValidFilter(e, boundedComparator)) { @@ -567,24 +552,29 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { final RexCall call = (RexCall) project; final Granularity funcGranularity = DruidDateTimeUtils.extractGranularity(call); if (funcGranularity != null) { - if (call.getKind().equals(SqlKind.EXTRACT)) { + final String extractColumnName; + switch (call.getKind()) { + case EXTRACT: // case extract field from time column finalGranularity = Granularity.ALL; - String extractColumnName = SqlValidatorUtil.uniquify(EXTRACT_COLUMN_NAME_PREFIX - + "_" + funcGranularity.value, usedFieldNames, SqlValidatorUtil.EXPR_SUGGESTER); - timeExtractionDimensionSpec = TimeExtractionDimensionSpec.makeExtract( + extractColumnName = SqlValidatorUtil.uniquify(EXTRACT_COLUMN_NAME_PREFIX + + "_" + funcGranularity.value, usedFieldNames, + SqlValidatorUtil.EXPR_SUGGESTER); + timeExtractionDimensionSpec = TimeExtractionDimensionSpec.makeTimeExtract( funcGranularity, extractColumnName); dimensions.add(timeExtractionDimensionSpec); builder.add(extractColumnName); - } else { + break; + case FLOOR: // case floor time column if (groupSet.cardinality() > 1) { // case we have more than 1 group by key -> then will have druid group by - String extractColumnName = SqlValidatorUtil.uniquify(FLOOR_COLUMN_NAME_PREFIX - + "_" + funcGranularity.value, usedFieldNames, SqlValidatorUtil - .EXPR_SUGGESTER); + extractColumnName = SqlValidatorUtil.uniquify(FLOOR_COLUMN_NAME_PREFIX + + "_" + funcGranularity.value, usedFieldNames, + SqlValidatorUtil.EXPR_SUGGESTER); dimensions.add( - TimeExtractionDimensionSpec.makeFloor(funcGranularity, extractColumnName)); + TimeExtractionDimensionSpec.makeTimeFloor(funcGranularity, + extractColumnName)); finalGranularity = Granularity.ALL; builder.add(extractColumnName); } else { @@ -594,6 +584,9 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { } assert timePositionIdx == -1; timePositionIdx = groupKey; + break; + default: + throw new AssertionError(); } } else { @@ -1014,9 +1007,13 @@ public class DruidQuery extends AbstractRelNode implements BindableRel { final boolean numeric = call.getOperands().get(posRef).getType().getFamily() == SqlTypeFamily.NUMERIC; - - final ExtractionFunction extractionFunction = ExtractionFunctionUtil.buildExtraction(call - .getOperands().get(posRef)); + final Granularity granularity = DruidDateTimeUtils.extractGranularity(call.getOperands() + .get(posRef)); + // in case no extraction the field will be omitted from the serialization + ExtractionFunction extractionFunction = null; + if (granularity != null) { + extractionFunction = TimeExtractionFunction.createExtractFromGranularity(granularity); + } String dimName = tr(e, posRef); if (dimName.equals(DruidConnectionImpl.DEFAULT_RESPONSE_TIMESTAMP_COLUMN)) { // We need to use Druid default column name to refer to the time dimension in a filter http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java index 8a02fd9..bbc8b4c 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java @@ -467,27 +467,25 @@ public class DruidRules { // Already one usage of timestamp column return -1; } - if (call.getKind() == SqlKind.FLOOR) { + switch (call.getKind()) { + case FLOOR: hasFloor = true; if (!(call.getOperands().get(0) instanceof RexInputRef)) { return -1; } final RexInputRef ref = (RexInputRef) call.getOperands().get(0); if (!(checkTimestampRefOnQuery(ImmutableBitSet.of(ref.getIndex()), - query.getTopNode(), query))) { + query.getTopNode(), + query))) { return -1; } idxTimestamp = i; - } else { - RexInputRef ref; - // Case extract from Calcite EXTRACT_DATE(FLAG(DAY), /INT(Reinterpret($0),86400000)) - if (call.getOperands().get(1) instanceof RexCall) { - RexCall refCall = (RexCall) call.getOperands().get(1); - ref = (RexInputRef) ((RexCall) refCall.getOperands().get(0)).getOperands().get(0); - } else { - ref = (RexInputRef) call.getOperands().get(1); - } - idxTimestamp = ref.getIndex(); + break; + case EXTRACT: + idxTimestamp = RelOptUtil.InputFinder.bits(call).asList().get(0); + break; + default: + throw new AssertionError(); } continue; } http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java b/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java index 25057c9..601fc89 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionDimensionSpec.java @@ -17,9 +17,11 @@ package org.apache.calcite.adapter.druid; import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.base.Preconditions; import java.io.IOException; +import static org.apache.calcite.adapter.druid.DruidQuery.writeField; import static org.apache.calcite.adapter.druid.DruidQuery.writeFieldIf; /** @@ -35,8 +37,8 @@ public class ExtractionDimensionSpec implements DimensionSpec { public ExtractionDimensionSpec(String dimension, ExtractionFunction extractionFunction, String outputName) { - this.dimension = dimension; - this.extractionFunction = extractionFunction; + this.dimension = Preconditions.checkNotNull(dimension); + this.extractionFunction = Preconditions.checkNotNull(extractionFunction); this.outputName = outputName; } @@ -49,7 +51,7 @@ public class ExtractionDimensionSpec implements DimensionSpec { generator.writeStringField("type", "extraction"); generator.writeStringField("dimension", dimension); writeFieldIf(generator, "outputName", outputName); - writeFieldIf(generator, "extractionFn", extractionFunction); + writeField(generator, "extractionFn", extractionFunction); generator.writeEndObject(); } http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionFunctionUtil.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionFunctionUtil.java b/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionFunctionUtil.java deleted file mode 100644 index b7cf372..0000000 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/ExtractionFunctionUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.calcite.adapter.druid; - - -import org.apache.calcite.avatica.util.TimeUnitRange; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.rex.RexLiteral; -import org.apache.calcite.rex.RexNode; -import org.apache.calcite.sql.SqlKind; - - -/** - * Utility class for extraction function mapping between SQL and Druid. - */ -public final class ExtractionFunctionUtil { - - private ExtractionFunctionUtil() { - } - - //~ Methods ---------------------------------------------------------------- - - /** - * This method will be used to build a Druid extraction function out of a SQL EXTRACT rexNode. - * - * @param rexNode node that might contain an extraction function on time - * @return the correspondent Druid extraction function or null if it is not recognisable - */ - public static ExtractionFunction buildExtraction(RexNode rexNode) { - if (rexNode instanceof RexCall) { - RexCall call = (RexCall) rexNode; - if (call.getKind().equals(SqlKind.EXTRACT)) { - final RexLiteral flag = (RexLiteral) call.operands.get(0); - final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue(); - if (timeUnit == null) { - return null; - } - switch (timeUnit) { - case YEAR: - return TimeExtractionFunction.createExtractFromGranularity(Granularity.YEAR); - case MONTH: - return TimeExtractionFunction.createExtractFromGranularity(Granularity.MONTH); - case DAY: - return TimeExtractionFunction.createExtractFromGranularity(Granularity.DAY); - default: - return null; - } - } - } - return null; - } -} - -// End ExtractionFunctionUtil.java http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java index 8f38720..656ee77 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionDimensionSpec.java @@ -42,7 +42,7 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec { /** * Creates a time extraction DimensionSpec that formats the '__time' column * according to the given granularity and outputs the column with the given - * name. Only YEAR, MONTH, and DAY granularity are supported. + * name. See {@link TimeExtractionFunction#VALID_TIME_EXTRACT} for set of valid extract * * @param granularity granularity to apply to the column * @param outputName name of the output column @@ -50,25 +50,12 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec { * @return time field extraction DimensionSpec instance or null if granularity * is not supported */ - public static TimeExtractionDimensionSpec makeExtract( + public static TimeExtractionDimensionSpec makeTimeExtract( Granularity granularity, String outputName) { - switch (granularity) { - case YEAR: - return new TimeExtractionDimensionSpec( - TimeExtractionFunction.createExtractFromGranularity(granularity), outputName); - case MONTH: - return new TimeExtractionDimensionSpec( - TimeExtractionFunction.createExtractFromGranularity(granularity), outputName); - case DAY: - return new TimeExtractionDimensionSpec( - TimeExtractionFunction.createExtractFromGranularity(granularity), outputName); - // TODO: Support other granularities - default: - return null; - } + return new TimeExtractionDimensionSpec( + TimeExtractionFunction.createExtractFromGranularity(granularity), outputName); } - /** * Creates floor time extraction dimension spec from Granularity with a given output name * @param granularity granularity to apply to the time column @@ -76,7 +63,8 @@ public class TimeExtractionDimensionSpec extends ExtractionDimensionSpec { * * @return floor time extraction DimensionSpec instance. */ - public static TimeExtractionDimensionSpec makeFloor(Granularity granularity, String outputName) { + public static TimeExtractionDimensionSpec makeTimeFloor(Granularity granularity, + String outputName) { ExtractionFunction fn = TimeExtractionFunction.createFloorFromGranularity(granularity); return new TimeExtractionDimensionSpec(fn, outputName); } http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java ---------------------------------------------------------------------- diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java index ff1f1cb..22733be 100644 --- a/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java +++ b/druid/src/main/java/org/apache/calcite/adapter/druid/TimeExtractionFunction.java @@ -16,7 +16,14 @@ */ package org.apache.calcite.adapter.druid; +import org.apache.calcite.avatica.util.TimeUnitRange; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.sql.SqlKind; + import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import java.io.IOException; import java.util.Locale; @@ -34,6 +41,12 @@ import static org.apache.calcite.adapter.druid.DruidQuery.writeFieldIf; */ public class TimeExtractionFunction implements ExtractionFunction { + private static final ImmutableSet<TimeUnitRange> VALID_TIME_EXTRACT = Sets.immutableEnumSet( + TimeUnitRange.YEAR, + TimeUnitRange.MONTH, + TimeUnitRange.DAY, + TimeUnitRange.WEEK); + private static final String ISO_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; private final String format; private final String granularity; @@ -71,7 +84,8 @@ public class TimeExtractionFunction implements ExtractionFunction { * Only YEAR, MONTH, and DAY granularity are supported. * * @param granularity granularity to apply to the column - * @return the time extraction function or null if granularity is not supported + * @return the time extraction function corresponding to the granularity input unit + * {@link TimeExtractionFunction#VALID_TIME_EXTRACT} for supported granularity */ public static TimeExtractionFunction createExtractFromGranularity(Granularity granularity) { switch (granularity) { @@ -81,8 +95,10 @@ public class TimeExtractionFunction implements ExtractionFunction { return new TimeExtractionFunction("M", null, "UTC", Locale.getDefault().toLanguageTag()); case YEAR: return new TimeExtractionFunction("yyyy", null, "UTC", Locale.getDefault().toLanguageTag()); + case WEEK: + return new TimeExtractionFunction("w", null, "UTC", Locale.getDefault().toLanguageTag()); default: - throw new AssertionError("Extraction " + granularity.value + " is not valid"); + throw new IllegalArgumentException("Granularity [" + granularity + "] is not supported"); } } @@ -96,6 +112,23 @@ public class TimeExtractionFunction implements ExtractionFunction { return new TimeExtractionFunction(ISO_TIME_FORMAT, granularity.value, "UTC", Locale .getDefault().toLanguageTag()); } + + /** + * Returns whether the RexCall contains a valid extract unit that we can + * serialize to Druid. + * + * @param call Extract expression + * + * @return true if the extract unit is valid + */ + public static boolean isValidTimeExtract(RexCall call) { + if (call.getKind() != SqlKind.EXTRACT) { + return false; + } + final RexLiteral flag = (RexLiteral) call.operands.get(0); + final TimeUnitRange timeUnit = (TimeUnitRange) flag.getValue(); + return timeUnit != null && VALID_TIME_EXTRACT.contains(timeUnit); + } } // End TimeExtractionFunction.java http://git-wip-us.apache.org/repos/asf/calcite/blob/a377f8f2/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java ---------------------------------------------------------------------- diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java index ced8fa8..b903fbb 100644 --- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java +++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java @@ -2047,6 +2047,47 @@ public class DruidAdapterIT { + "S=7270\nbrand_name=Toucan; C=123; S=380").queryContains(druidChecker(druidSubQuery)); } + + @Test public void testGroupByWeekExtract() { + final String sql = "SELECT extract(week from \"timestamp\") from \"foodmart\" where " + + "\"product_id\" = 1558 and extract(week from \"timestamp\") IN (10, 11)group by extract" + + "(week from \"timestamp\")"; + + final String druidQuery = "{'queryType':'groupBy','dataSource':'foodmart'," + + "'granularity':'all','dimensions':[{'type':'extraction'," + + "'dimension':'__time','outputName':'extract_week'," + + "'extractionFn':{'type':'timeFormat','format':'w','timeZone':'UTC'," + + "'locale':'en-US'}}],'limitSpec':{'type':'default'}," + + "'filter':{'type':'and','fields':[{'type':'selector'," + + "'dimension':'product_id','value':'1558'},{'type':'or'," + + "'fields':[{'type':'selector','dimension':'__time','value':'10'," + + "'extractionFn':{'type':'timeFormat','format':'w','timeZone':'UTC'," + + "'locale':'en-US'}},{'type':'selector','dimension':'__time'," + + "'value':'11','extractionFn':{'type':'timeFormat','format':'w'," + + "'timeZone':'UTC','locale':'en-US'}}]}]}," + + "'aggregations':[{'type':'longSum','name':'dummy_agg'," + + "'fieldName':'dummy_agg'}]," + + "'intervals':['1900-01-09T00:00:00.000/2992-01-10T00:00:00.000']}"; + sql(sql).returnsOrdered("EXPR$0=10\nEXPR$0=11").queryContains(druidChecker(druidQuery)); + } + + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1765">[CALCITE-1765] + * Druid adapter: Gracefully handle granularity that cannot be pushed to + * extraction function</a>. */ + @Test public void testTimeExtractThatCannotBePushed() { + final String sql = "SELECT extract(CENTURY from \"timestamp\") from \"foodmart\" where " + + "\"product_id\" = 1558 group by extract(CENTURY from \"timestamp\")"; + final String plan = "PLAN=EnumerableInterpreter\n" + + " BindableAggregate(group=[{0}])\n" + + " BindableProject(EXPR$0=[/INT(EXTRACT_DATE(FLAG(YEAR), /INT(Reinterpret($0), " + + "86400000)), 100)])\n" + + " DruidQuery(table=[[foodmart, foodmart]], " + + "intervals=[[1900-01-09T00:00:00.000/2992-01-10T00:00:00.000]], filter=[=($1, 1558)], " + + "projects=[[$0]])"; + sql(sql).explainContains(plan).queryContains(druidChecker("'queryType':'select'")) + .returnsUnordered("EXPR$0=19"); + } } // End DruidAdapterIT.java
