blob: 7888ec8090bab2acb2aa0dfacddf0950e53ecfa4 [file] [log] [blame]
/*
* 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.druid.sql.calcite.planner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.ArraySqlType;
import org.apache.calcite.sql.type.BasicSqlType;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.column.RowSignature;
import org.apache.druid.server.security.AuthConfig;
import org.apache.druid.sql.calcite.expression.DirectOperatorConversion;
import org.apache.druid.sql.calcite.expression.DruidExpression;
import org.apache.druid.sql.calcite.expression.Expressions;
import org.apache.druid.sql.calcite.expression.OperatorConversions;
import org.apache.druid.sql.calcite.expression.builtin.MultiValueStringOperatorConversions;
import org.apache.druid.sql.calcite.expression.builtin.TimeParseOperatorConversion;
import org.apache.druid.sql.calcite.schema.DruidSchema;
import org.apache.druid.sql.calcite.schema.DruidSchemaCatalog;
import org.apache.druid.sql.calcite.schema.NamedDruidSchema;
import org.apache.druid.sql.calcite.schema.NamedViewSchema;
import org.apache.druid.sql.calcite.schema.ViewSchema;
import org.apache.druid.sql.calcite.table.RowSignatures;
import org.apache.druid.sql.calcite.util.CalciteTestBase;
import org.apache.druid.sql.calcite.util.CalciteTests;
import org.apache.druid.sql.hook.DruidHookDispatcher;
import org.apache.druid.testing.InitializedNullHandlingTest;
import org.easymock.EasyMock;
import org.joda.time.DateTimeZone;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DruidRexExecutorTest extends InitializedNullHandlingTest
{
private static final SqlOperator OPERATOR = OperatorConversions
.operatorBuilder(StringUtils.toUpperCase("hyper_unique"))
.operandTypes(SqlTypeFamily.ANY)
.requiredOperandCount(0)
.returnTypeInference(
opBinding -> RowSignatures.makeComplexType(
opBinding.getTypeFactory(),
ColumnType.ofComplex("hyperUnique"),
true
)
)
.functionCategory(SqlFunctionCategory.USER_DEFINED_FUNCTION)
.build();
private static final PlannerToolbox PLANNER_TOOLBOX = new PlannerToolbox(
new DruidOperatorTable(
Collections.emptySet(),
ImmutableSet.of(new DirectOperatorConversion(OPERATOR, "hyper_unique"))
),
CalciteTests.createExprMacroTable(),
CalciteTests.getJsonMapper(),
new PlannerConfig(),
new DruidSchemaCatalog(
EasyMock.createMock(SchemaPlus.class),
ImmutableMap.of(
"druid", new NamedDruidSchema(EasyMock.createMock(DruidSchema.class), "druid"),
NamedViewSchema.NAME, new NamedViewSchema(EasyMock.createMock(ViewSchema.class))
)
),
CalciteTests.createJoinableFactoryWrapper(),
CatalogResolver.NULL_RESOLVER,
"druid",
new CalciteRulesManager(ImmutableSet.of()),
CalciteTests.TEST_AUTHORIZER_MAPPER,
AuthConfig.newBuilder().build(),
new DruidHookDispatcher()
);
private static final PlannerContext PLANNER_CONTEXT = PlannerContext.create(
PLANNER_TOOLBOX,
"SELECT 1", // The actual query isn't important for this test
null, /* Don't need an engine */
Collections.emptyMap(),
null
);
private final RexBuilder rexBuilder = new RexBuilder(new JavaTypeFactoryImpl());
private final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(DruidTypeSystem.INSTANCE);
@Test
public void testLongsReduced()
{
RexNode call = rexBuilder.makeCall(
SqlStdOperatorTable.MULTIPLY,
rexBuilder.makeLiteral(
new BigDecimal(10L),
typeFactory.createSqlType(SqlTypeName.BIGINT), true
),
rexBuilder.makeLiteral(
new BigDecimal(3L),
typeFactory.createSqlType(SqlTypeName.BIGINT), true
)
);
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.LITERAL, reduced.get(0).getKind());
Assert.assertEquals(new BigDecimal(30L), ((RexLiteral) reduced.get(0)).getValue());
}
@Test
public void testCastDateReduced()
{
// CAST('2010-01-01' AS DATE)
RexNode call = rexBuilder.makeCall(
rexBuilder.getTypeFactory().createSqlType(SqlTypeName.DATE),
SqlStdOperatorTable.CAST,
Collections.singletonList(rexBuilder.makeLiteral("2010-01-01"))
);
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.LITERAL, reduced.get(0).getKind());
Assert.assertEquals(
rexBuilder.makeDateLiteral(
Calcites.jodaToCalciteDateString(
DateTimes.of("2010-01-01"),
DateTimeZone.UTC
)
),
reduced.get(0)
);
}
@Test
public void testTimeParseReduced()
{
// TIME_PARSE('2010-01-01T02:03:04Z')
RexNode call = rexBuilder.makeCall(
new TimeParseOperatorConversion().calciteOperator(),
rexBuilder.makeLiteral("2010-01-01T02:03:04Z")
);
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.LITERAL, reduced.get(0).getKind());
Assert.assertEquals(
Calcites.jodaToCalciteTimestampLiteral(
rexBuilder,
DateTimes.of("2010-01-01T02:03:04Z"),
DateTimeZone.UTC,
DruidTypeSystem.DEFAULT_TIMESTAMP_PRECISION
),
reduced.get(0)
);
}
@Test
public void testTimeParseUnparseableReduced()
{
// TIME_PARSE('not a timestamp')
RexNode call = rexBuilder.makeCall(
new TimeParseOperatorConversion().calciteOperator(),
rexBuilder.makeLiteral("not a timestamp")
);
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.LITERAL, reduced.get(0).getKind());
Assert.assertTrue(RexLiteral.isNullLiteral(reduced.get(0)));
}
@Test
public void testComplexNotReduced()
{
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
RexNode call = rexBuilder.makeCall(OPERATOR);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.OTHER_FUNCTION, reduced.get(0).getKind());
Assert.assertEquals(
CalciteTestBase.makeExpression(ColumnType.ofComplex("hyperUnique"), "hyper_unique()"),
Expressions.toDruidExpression(
PLANNER_CONTEXT,
RowSignature.builder().build(),
reduced.get(0)
)
);
}
@Test
public void testArrayOfDoublesReduction()
{
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
BasicSqlType basicSqlType = new BasicSqlType(DruidTypeSystem.INSTANCE, SqlTypeName.DECIMAL);
ArraySqlType arraySqlType = new ArraySqlType(basicSqlType, false);
List<BigDecimal> elements = ImmutableList.of(BigDecimal.valueOf(50.12), BigDecimal.valueOf(12.1));
RexNode literal = rexBuilder.makeLiteral(elements, arraySqlType, true);
rexy.reduce(rexBuilder, ImmutableList.of(literal), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(
DruidExpression.ofExpression(
ColumnType.DOUBLE_ARRAY,
DruidExpression.functionCall("array"),
ImmutableList.of(
DruidExpression.ofLiteral(ColumnType.DOUBLE, "50.12"),
DruidExpression.ofLiteral(ColumnType.DOUBLE, "12.1")
)
),
Expressions.toDruidExpression(
PLANNER_CONTEXT,
RowSignature.empty(),
reduced.get(0)
)
);
}
@Test
public void testArrayOfLongsReduction()
{
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
List<RexNode> reduced = new ArrayList<>();
BasicSqlType basicSqlType = new BasicSqlType(DruidTypeSystem.INSTANCE, SqlTypeName.INTEGER);
ArraySqlType arraySqlType = new ArraySqlType(basicSqlType, false);
List<BigDecimal> elements = ImmutableList.of(BigDecimal.valueOf(50), BigDecimal.valueOf(12));
RexNode literal = rexBuilder.makeLiteral(elements, arraySqlType, true);
rexy.reduce(rexBuilder, ImmutableList.of(literal), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(
DruidExpression.ofExpression(
ColumnType.LONG_ARRAY,
DruidExpression.functionCall("array"),
ImmutableList.of(
DruidExpression.ofLiteral(ColumnType.LONG, "50"),
DruidExpression.ofLiteral(ColumnType.LONG, "12")
)
),
Expressions.toDruidExpression(
PLANNER_CONTEXT,
RowSignature.empty(),
reduced.get(0)
)
);
}
@Test
public void testMultiValueStringNotReduced()
{
DruidRexExecutor rexy = new DruidRexExecutor(PLANNER_CONTEXT);
RexNode call = rexBuilder.makeCall(
MultiValueStringOperatorConversions.StringToMultiString.SQL_FUNCTION,
rexBuilder.makeLiteral("a,b,c"),
rexBuilder.makeLiteral(",")
);
List<RexNode> reduced = new ArrayList<>();
rexy.reduce(rexBuilder, ImmutableList.of(call), reduced);
Assert.assertEquals(1, reduced.size());
Assert.assertEquals(SqlKind.OTHER_FUNCTION, reduced.get(0).getKind());
Assert.assertEquals(
DruidExpression.ofExpression(
ColumnType.STRING,
DruidExpression.functionCall("string_to_array"),
ImmutableList.of(
DruidExpression.ofStringLiteral("a,b,c"),
DruidExpression.ofStringLiteral(",")
)
),
Expressions.toDruidExpression(
PLANNER_CONTEXT,
RowSignature.builder().build(),
reduced.get(0)
)
);
}
}