This is an automated email from the ASF dual-hosted git repository.
xiangfu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 316842f66e [multistage] Support array type for select query (#10434)
316842f66e is described below
commit 316842f66e8470ba03042bd5e4d41e09e1d73492
Author: Xiang Fu <[email protected]>
AuthorDate: Fri Mar 24 02:29:12 2023 -0700
[multistage] Support array type for select query (#10434)
* [multistage] Support array type for select query
* Adding more tests
* upgrade h2 version
---
.github/workflows/pinot_compatibility_tests.yml | 2 +-
.github/workflows/pinot_tests.yml | 10 +-
...ultiStageEngineCustomTenantIntegrationTest.java | 2 +-
.../tests/MultiStageEngineIntegrationTest.java | 15 ++-
.../query/planner/logical/RelToStageConverter.java | 24 ++--
.../org/apache/pinot/query/type/TypeFactory.java | 27 +++--
.../planner/logical/RelToStageConverterTest.java | 133 +++++++++++++++++++++
.../apache/pinot/query/type/TypeFactoryTest.java | 105 ++++++++++++++++
.../pinot/query/runtime/QueryRunnerTestBase.java | 114 ++++++++++++------
.../src/test/resources/queries/Aggregates.json | 3 +-
.../src/test/resources/queries/BooleanLogic.json | 9 +-
.../test/resources/queries/SelectExpressions.json | 12 +-
12 files changed, 387 insertions(+), 69 deletions(-)
diff --git a/.github/workflows/pinot_compatibility_tests.yml
b/.github/workflows/pinot_compatibility_tests.yml
index f02304dc85..4059852163 100644
--- a/.github/workflows/pinot_compatibility_tests.yml
+++ b/.github/workflows/pinot_compatibility_tests.yml
@@ -36,7 +36,7 @@ jobs:
test_suite: [ "compatibility-verifier/sample-test-suite" ]
name: Pinot Compatibility Regression Testing against ${{
github.event.inputs.oldCommit }} and ${{ github.event.inputs.newCommit }} on
${{ matrix.test_suite }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
diff --git a/.github/workflows/pinot_tests.yml
b/.github/workflows/pinot_tests.yml
index 97d917b168..f817c3b854 100644
--- a/.github/workflows/pinot_tests.yml
+++ b/.github/workflows/pinot_tests.yml
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
name: Pinot Linter Test Set
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
@@ -76,7 +76,7 @@ jobs:
testset: [ 1, 2 ]
name: Pinot Unit Test Set ${{ matrix.testset }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
@@ -122,7 +122,7 @@ jobs:
testset: [ 1, 2 ]
name: Pinot Integration Test Set ${{ matrix.testset }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
@@ -223,7 +223,7 @@ jobs:
java: [ 8, 11, 15 ]
name: Pinot Quickstart on JDK ${{ matrix.java }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
with:
@@ -247,7 +247,7 @@ jobs:
runs-on: ubuntu-latest
name: Build Presto Pinot Driver
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
diff --git
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineCustomTenantIntegrationTest.java
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineCustomTenantIntegrationTest.java
index b2336b3c80..c77d6c4cd1 100644
---
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineCustomTenantIntegrationTest.java
+++
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineCustomTenantIntegrationTest.java
@@ -42,7 +42,7 @@ import org.testng.annotations.Test;
public class MultiStageEngineCustomTenantIntegrationTest extends
MultiStageEngineIntegrationTest {
private static final String SCHEMA_FILE_NAME =
-
"On_Time_On_Time_Performance_2014_100k_subset_nonulls_single_value_columns.schema";
+ "On_Time_On_Time_Performance_2014_100k_subset_nonulls.schema";
private static final String TEST_TENANT = "TestTenant";
@Override
diff --git
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineIntegrationTest.java
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineIntegrationTest.java
index 651a8da1bd..6dabf814d8 100644
---
a/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineIntegrationTest.java
+++
b/pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiStageEngineIntegrationTest.java
@@ -39,8 +39,7 @@ import org.testng.annotations.Test;
public class MultiStageEngineIntegrationTest extends
BaseClusterIntegrationTestSet {
- private static final String SCHEMA_FILE_NAME =
-
"On_Time_On_Time_Performance_2014_100k_subset_nonulls_single_value_columns.schema";
+ private static final String SCHEMA_FILE_NAME =
"On_Time_On_Time_Performance_2014_100k_subset_nonulls.schema";
@Override
protected String getSchemaFileName() {
@@ -108,6 +107,18 @@ public class MultiStageEngineIntegrationTest extends
BaseClusterIntegrationTestS
h2Query, getH2Connection(), null, ImmutableMap.of("queryOptions",
"useMultistageEngine=true"));
}
+ @Test
+ public void testMultiValueColumnSelectionQuery()
+ throws Exception {
+ String pinotQuery =
+ "SELECT DivAirportIDs, DivAirports FROM mytable WHERE
DATE_TIME_CONVERT(DaysSinceEpoch, '1:DAYS:EPOCH', "
+ + "'1:DAYS:SIMPLE_DATE_FORMAT:yyyy-MM-dd''T''HH:mm:ss.SSS''Z''',
'1:DAYS') = '2014-09-05T00:00:00.000Z'";
+ String h2Query =
+ "SELECT DivAirportIDs__MV0, DivAirports__MV0 FROM mytable WHERE
DaysSinceEpoch = 16318 LIMIT 10000";
+ ClusterIntegrationTestUtils.testQueryWithMatchingRowCount(pinotQuery,
_brokerBaseApiUrl, getPinotConnection(),
+ h2Query, getH2Connection(), null, ImmutableMap.of("queryOptions",
"useMultistageEngine=true"));
+ }
+
@Override
protected Connection getPinotConnection() {
Properties properties = new Properties();
diff --git
a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java
b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java
index 2bc1ebdae0..4b8c07ed1a 100644
---
a/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java
+++
b/pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToStageConverter.java
@@ -34,6 +34,7 @@ import org.apache.calcite.rel.logical.LogicalWindow;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelRecordType;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.pinot.common.utils.DataSchema;
import org.apache.pinot.common.utils.PinotDataType;
import org.apache.pinot.query.planner.partitioning.FieldSelectionKeySelector;
@@ -151,34 +152,39 @@ public final class RelToStageConverter {
}
public static DataSchema.ColumnDataType convertToColumnDataType(RelDataType
relDataType) {
- switch (relDataType.getSqlTypeName()) {
+ SqlTypeName sqlTypeName = relDataType.getSqlTypeName();
+ boolean isArray = sqlTypeName == SqlTypeName.ARRAY;
+ if (isArray) {
+ sqlTypeName = relDataType.getComponentType().getSqlTypeName();
+ }
+ switch (sqlTypeName) {
case BOOLEAN:
- return DataSchema.ColumnDataType.BOOLEAN;
+ return isArray ? DataSchema.ColumnDataType.BOOLEAN_ARRAY :
DataSchema.ColumnDataType.BOOLEAN;
case TINYINT:
case SMALLINT:
case INTEGER:
- return DataSchema.ColumnDataType.INT;
+ return isArray ? DataSchema.ColumnDataType.INT_ARRAY :
DataSchema.ColumnDataType.INT;
case BIGINT:
- return DataSchema.ColumnDataType.LONG;
+ return isArray ? DataSchema.ColumnDataType.LONG_ARRAY :
DataSchema.ColumnDataType.LONG;
case DECIMAL:
return resolveDecimal(relDataType);
case FLOAT:
- return DataSchema.ColumnDataType.FLOAT;
+ return isArray ? DataSchema.ColumnDataType.FLOAT_ARRAY :
DataSchema.ColumnDataType.FLOAT;
case REAL:
case DOUBLE:
- return DataSchema.ColumnDataType.DOUBLE;
+ return isArray ? DataSchema.ColumnDataType.DOUBLE_ARRAY :
DataSchema.ColumnDataType.DOUBLE;
case DATE:
case TIME:
case TIMESTAMP:
- return DataSchema.ColumnDataType.TIMESTAMP;
+ return isArray ? DataSchema.ColumnDataType.TIMESTAMP_ARRAY :
DataSchema.ColumnDataType.TIMESTAMP;
case CHAR:
case VARCHAR:
- return DataSchema.ColumnDataType.STRING;
+ return isArray ? DataSchema.ColumnDataType.STRING_ARRAY :
DataSchema.ColumnDataType.STRING;
case OTHER:
return DataSchema.ColumnDataType.OBJECT;
case BINARY:
case VARBINARY:
- return DataSchema.ColumnDataType.BYTES;
+ return isArray ? DataSchema.ColumnDataType.BYTES_ARRAY :
DataSchema.ColumnDataType.BYTES;
default:
return DataSchema.ColumnDataType.BYTES;
}
diff --git
a/pinot-query-planner/src/main/java/org/apache/pinot/query/type/TypeFactory.java
b/pinot-query-planner/src/main/java/org/apache/pinot/query/type/TypeFactory.java
index 0e1814e8c9..1877f7ec9c 100644
---
a/pinot-query-planner/src/main/java/org/apache/pinot/query/type/TypeFactory.java
+++
b/pinot-query-planner/src/main/java/org/apache/pinot/query/type/TypeFactory.java
@@ -54,23 +54,32 @@ public class TypeFactory extends JavaTypeFactoryImpl {
private RelDataType toRelDataType(FieldSpec fieldSpec) {
switch (fieldSpec.getDataType()) {
case INT:
- return createSqlType(SqlTypeName.INTEGER);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.INTEGER)
+ : createArrayType(createSqlType(SqlTypeName.INTEGER), -1);
case LONG:
- return createSqlType(SqlTypeName.BIGINT);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.BIGINT)
+ : createArrayType(createSqlType(SqlTypeName.BIGINT), -1);
case FLOAT:
- return createSqlType(SqlTypeName.FLOAT);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.FLOAT)
+ : createArrayType(createSqlType(SqlTypeName.FLOAT), -1);
case DOUBLE:
- return createSqlType(SqlTypeName.DOUBLE);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.DOUBLE)
+ : createArrayType(createSqlType(SqlTypeName.DOUBLE), -1);
case BOOLEAN:
- return createSqlType(SqlTypeName.BOOLEAN);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.BOOLEAN)
+ : createArrayType(createSqlType(SqlTypeName.BOOLEAN), -1);
case TIMESTAMP:
- return createSqlType(SqlTypeName.TIMESTAMP);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.TIMESTAMP)
+ : createArrayType(createSqlType(SqlTypeName.TIMESTAMP), -1);
case STRING:
- return createSqlType(SqlTypeName.VARCHAR);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.VARCHAR)
+ : createArrayType(createSqlType(SqlTypeName.VARCHAR), -1);
case BYTES:
- return createSqlType(SqlTypeName.VARBINARY);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.VARBINARY)
+ : createArrayType(createSqlType(SqlTypeName.VARBINARY), -1);
case BIG_DECIMAL:
- return createSqlType(SqlTypeName.DECIMAL);
+ return fieldSpec.isSingleValueField() ?
createSqlType(SqlTypeName.DECIMAL)
+ : createArrayType(createSqlType(SqlTypeName.DECIMAL), -1);
case JSON:
// TODO: support JSON, JSON should be supported using a special
RelDataType as it is not a simple String,
// nor can it be easily parsed as a STRUCT.
diff --git
a/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/logical/RelToStageConverterTest.java
b/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/logical/RelToStageConverterTest.java
new file mode 100644
index 0000000000..32a27c979b
--- /dev/null
+++
b/pinot-query-planner/src/test/java/org/apache/pinot/query/planner/logical/RelToStageConverterTest.java
@@ -0,0 +1,133 @@
+/**
+ * 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.pinot.query.planner.logical;
+
+import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.type.ArraySqlType;
+import org.apache.calcite.sql.type.BasicSqlType;
+import org.apache.calcite.sql.type.ObjectSqlType;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.pinot.common.utils.DataSchema;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+
+public class RelToStageConverterTest {
+
+ @Test
+ public void testConvertToColumnDataTypeForObjectTypes() {
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.BOOLEAN, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.BOOLEAN);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.TINYINT, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.INT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.SMALLINT, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.INT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.INTEGER, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.INT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.BIGINT, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.LONG);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.FLOAT, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.FLOAT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.DOUBLE, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.DOUBLE);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.TIMESTAMP, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.TIMESTAMP);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.CHAR, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.VARCHAR, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.STRING);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.VARBINARY, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.BYTES);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ObjectSqlType(SqlTypeName.OTHER, SqlIdentifier.STAR, true,
null, null)),
+ DataSchema.ColumnDataType.OBJECT);
+ }
+
+ @Test
+ public void testBigDecimal() {
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
10)),
+ DataSchema.ColumnDataType.INT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
38)),
+ DataSchema.ColumnDataType.LONG);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
39)),
+ DataSchema.ColumnDataType.BIG_DECIMAL);
+
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
14, 10)),
+ DataSchema.ColumnDataType.FLOAT);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
30, 10)),
+ DataSchema.ColumnDataType.DOUBLE);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new BasicSqlType(RelDataTypeSystem.DEFAULT, SqlTypeName.DECIMAL,
31, 10)),
+ DataSchema.ColumnDataType.BIG_DECIMAL);
+ }
+
+ @Test
+ public void testConvertToColumnDataTypeForArray() {
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.BOOLEAN,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.BOOLEAN_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.TINYINT,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.INT_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.SMALLINT,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.INT_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.INTEGER,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.INT_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.BIGINT,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.LONG_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.FLOAT,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.FLOAT_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.DOUBLE,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.DOUBLE_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.TIMESTAMP,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.TIMESTAMP_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.CHAR,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.STRING_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.VARCHAR,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.STRING_ARRAY);
+ Assert.assertEquals(RelToStageConverter.convertToColumnDataType(
+ new ArraySqlType(new ObjectSqlType(SqlTypeName.VARBINARY,
SqlIdentifier.STAR, true, null, null), true)),
+ DataSchema.ColumnDataType.BYTES_ARRAY);
+ }
+}
diff --git
a/pinot-query-planner/src/test/java/org/apache/pinot/query/type/TypeFactoryTest.java
b/pinot-query-planner/src/test/java/org/apache/pinot/query/type/TypeFactoryTest.java
new file mode 100644
index 0000000000..0e1d55d646
--- /dev/null
+++
b/pinot-query-planner/src/test/java/org/apache/pinot/query/type/TypeFactoryTest.java
@@ -0,0 +1,105 @@
+/**
+ * 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.pinot.query.type;
+
+import java.util.List;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.sql.type.ArraySqlType;
+import org.apache.calcite.sql.type.BasicSqlType;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.pinot.spi.data.FieldSpec;
+import org.apache.pinot.spi.data.Schema;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+
+public class TypeFactoryTest {
+ private static final TypeSystem TYPE_SYSTEM = new TypeSystem() {
+ };
+
+ @Test
+ public void testRelDataTypeConversion() {
+ TypeFactory typeFactory = new TypeFactory(TYPE_SYSTEM);
+ Schema testSchema = new
Schema.SchemaBuilder().addSingleValueDimension("INT_COL",
FieldSpec.DataType.INT)
+ .addSingleValueDimension("LONG_COL", FieldSpec.DataType.LONG)
+ .addSingleValueDimension("FLOAT_COL", FieldSpec.DataType.FLOAT)
+ .addSingleValueDimension("DOUBLE_COL", FieldSpec.DataType.DOUBLE)
+ .addSingleValueDimension("STRING_COL", FieldSpec.DataType.STRING)
+ .addSingleValueDimension("BYTES_COL", FieldSpec.DataType.BYTES)
+ .addMultiValueDimension("INT_ARRAY_COL", FieldSpec.DataType.INT)
+ .addMultiValueDimension("LONG_ARRAY_COL", FieldSpec.DataType.LONG)
+ .addMultiValueDimension("FLOAT_ARRAY_COL", FieldSpec.DataType.FLOAT)
+ .addMultiValueDimension("DOUBLE_ARRAY_COL", FieldSpec.DataType.DOUBLE)
+ .addMultiValueDimension("STRING_ARRAY_COL", FieldSpec.DataType.STRING)
+ .addMultiValueDimension("BYTES_ARRAY_COL", FieldSpec.DataType.BYTES)
+ .build();
+ RelDataType relDataTypeFromSchema =
typeFactory.createRelDataTypeFromSchema(testSchema);
+ List<RelDataTypeField> fieldList = relDataTypeFromSchema.getFieldList();
+ for (RelDataTypeField field : fieldList) {
+ switch (field.getName()) {
+ case "INT_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.INTEGER));
+ break;
+ case "LONG_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.BIGINT));
+ break;
+ case "FLOAT_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.FLOAT));
+ break;
+ case "DOUBLE_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.DOUBLE));
+ break;
+ case "STRING_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.VARCHAR));
+ break;
+ case "BYTES_COL":
+ Assert.assertEquals(field.getType(), new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.VARBINARY));
+ break;
+ case "INT_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.INTEGER), false));
+ break;
+ case "LONG_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.BIGINT), false));
+ break;
+ case "FLOAT_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.FLOAT), false));
+ break;
+ case "DOUBLE_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.DOUBLE), false));
+ break;
+ case "STRING_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.VARCHAR), false));
+ break;
+ case "BYTES_ARRAY_COL":
+ Assert.assertEquals(field.getType(),
+ new ArraySqlType(new BasicSqlType(TYPE_SYSTEM,
SqlTypeName.VARBINARY), false));
+ break;
+ default:
+ Assert.fail("Unexpected column name: " + field.getName());
+ break;
+ }
+ }
+ }
+}
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/QueryRunnerTestBase.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/QueryRunnerTestBase.java
index e083948758..06379ac21d 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/QueryRunnerTestBase.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/QueryRunnerTestBase.java
@@ -61,6 +61,7 @@ import org.apache.pinot.spi.data.readers.GenericRow;
import org.apache.pinot.spi.utils.ByteArray;
import org.apache.pinot.spi.utils.CommonConstants;
import org.apache.pinot.spi.utils.StringUtil;
+import org.h2.jdbc.JdbcArray;
import org.testng.Assert;
@@ -193,6 +194,35 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
return ((ByteArray) l).compareTo(new ByteArray((byte[]) r));
} else if (l instanceof Timestamp) {
return ((Timestamp) l).compareTo((Timestamp) r);
+ } else if (l instanceof int[]) {
+ int[] larray = (int[]) l;
+ Object[] rarray;
+ try {
+ rarray = (Object[]) ((JdbcArray) r).getArray();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ for (int idx = 0; idx < larray.length; idx++) {
+ Number relement = (Number) rarray[idx];
+ if (larray[idx] != relement.intValue()) {
+ return -1;
+ }
+ }
+ return 0;
+ } else if (l instanceof String[]) {
+ String[] larray = (String[]) l;
+ Object[] rarray;
+ try {
+ rarray = (Object[]) ((JdbcArray) r).getArray();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ for (int idx = 0; idx < larray.length; idx++) {
+ if (!larray[idx].equals(rarray[idx])) {
+ return -1;
+ }
+ }
+ return 0;
} else {
throw new RuntimeException("non supported type " + l.getClass());
}
@@ -231,7 +261,11 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
protected Schema constructSchema(String schemaName,
List<QueryTestCase.ColumnAndType> columnAndTypes) {
Schema.SchemaBuilder builder = new Schema.SchemaBuilder();
for (QueryTestCase.ColumnAndType columnAndType : columnAndTypes) {
- builder.addSingleValueDimension(columnAndType._name,
FieldSpec.DataType.valueOf(columnAndType._type));
+ if (columnAndType._isSingleValue) {
+ builder.addSingleValueDimension(columnAndType._name,
FieldSpec.DataType.valueOf(columnAndType._type));
+ } else {
+ builder.addMultiValueDimension(columnAndType._name,
FieldSpec.DataType.valueOf(columnAndType._type));
+ }
}
// TODO: ts is built-in, but we should allow user overwrite
builder.addDateTime("ts", FieldSpec.DataType.LONG, "1:MILLISECONDS:EPOCH",
"1:SECONDS");
@@ -299,13 +333,19 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
int h2Index = 1;
for (String fieldName : schema.getColumnNames()) {
Object value = row.getValue(fieldName);
- switch (schema.getFieldSpecFor(fieldName).getDataType()) {
- case BYTES:
- h2Statement.setBytes(h2Index++, Hex.decodeHex((String) value));
- break;
- default:
- h2Statement.setObject(h2Index++, value);
- break;
+ if (value instanceof List) {
+ h2Statement.setArray(h2Index++,
+
_h2Connection.createArrayOf(getH2FieldType(schema.getFieldSpecFor(fieldName).getDataType()),
+ ((List) value).toArray()));
+ } else {
+ switch (schema.getFieldSpecFor(fieldName).getDataType()) {
+ case BYTES:
+ h2Statement.setBytes(h2Index++, Hex.decodeHex((String) value));
+ break;
+ default:
+ h2Statement.setObject(h2Index++, value);
+ break;
+ }
}
}
h2Statement.execute();
@@ -317,41 +357,39 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
List<String> fieldNamesAndTypes = new ArrayList<>(pinotSchema.size());
for (String fieldName : pinotSchema.getColumnNames()) {
FieldSpec.DataType dataType =
pinotSchema.getFieldSpecFor(fieldName).getDataType();
- String fieldType;
- switch (dataType) {
- case INT:
- case LONG:
- fieldType = "bigint";
- break;
- case STRING:
- fieldType = "varchar(128)";
- break;
- case FLOAT:
- fieldType = "real";
- break;
- case DOUBLE:
- fieldType = "double";
- break;
- case BOOLEAN:
- fieldType = "BOOLEAN";
- break;
- case BIG_DECIMAL:
- fieldType = "NUMERIC(65535, 32767)";
- break;
- case BYTES:
- fieldType = "BYTEA";
- break;
- case TIMESTAMP:
- fieldType = "TIMESTAMP";
- break;
- default:
- throw new UnsupportedOperationException("Unsupported type conversion
to h2 type: " + dataType);
+ String fieldType = getH2FieldType(dataType);
+ if (!pinotSchema.getFieldSpecFor(fieldName).isSingleValueField()) {
+ fieldType += " ARRAY";
}
fieldNamesAndTypes.add(fieldName + " " + fieldType);
}
return fieldNamesAndTypes;
}
+ private static String getH2FieldType(FieldSpec.DataType dataType) {
+ switch (dataType) {
+ case INT:
+ case LONG:
+ return "bigint";
+ case STRING:
+ return "varchar(128)";
+ case FLOAT:
+ return "real";
+ case DOUBLE:
+ return "double";
+ case BOOLEAN:
+ return "BOOLEAN";
+ case BIG_DECIMAL:
+ return "NUMERIC(65535, 32767)";
+ case BYTES:
+ return "BYTEA";
+ case TIMESTAMP:
+ return "TIMESTAMP";
+ default:
+ throw new UnsupportedOperationException("Unsupported type conversion
to h2 type: " + dataType);
+ }
+ }
+
@JsonIgnoreProperties(ignoreUnknown = true)
public static class QueryTestCase {
public static final String BLOCK_SIZE_KEY = "blockSize";
@@ -400,6 +438,8 @@ public abstract class QueryRunnerTestBase extends
QueryTestSet {
String _name;
@JsonProperty("type")
String _type;
+ @JsonProperty("isSingleValue")
+ boolean _isSingleValue = true;
}
}
}
diff --git a/pinot-query-runtime/src/test/resources/queries/Aggregates.json
b/pinot-query-runtime/src/test/resources/queries/Aggregates.json
index ade91b5ee1..aaafa39651 100644
--- a/pinot-query-runtime/src/test/resources/queries/Aggregates.json
+++ b/pinot-query-runtime/src/test/resources/queries/Aggregates.json
@@ -195,7 +195,8 @@
{
"psql": "4.2.7",
"description": "aggregate int column and filter by int column",
- "sql": "SELECT sum(1 / int_col) FROM {tbl} WHERE int_col > 0"
+ "sql": "SELECT sum(1 / int_col) FROM {tbl} WHERE int_col > 0",
+ "h2Sql": "SELECT sum(1.0 / int_col) FROM {tbl} WHERE int_col > 0"
},
{
"psql": "4.2.7",
diff --git a/pinot-query-runtime/src/test/resources/queries/BooleanLogic.json
b/pinot-query-runtime/src/test/resources/queries/BooleanLogic.json
index 0a599e6caa..4473c4704a 100644
--- a/pinot-query-runtime/src/test/resources/queries/BooleanLogic.json
+++ b/pinot-query-runtime/src/test/resources/queries/BooleanLogic.json
@@ -58,13 +58,15 @@
"ignored": true,
"note": "H2 doesn't support this",
"description": "check implicit cast between boolean and int",
- "sql": "select b = i FROM {tbl}"
+ "sql": "select b = i FROM {tbl}",
+ "expectedException": "Values of types .* and .* are not comparable"
},
{
"ignored": true,
"comment": "H2 doesn't support this",
"description": "check implicit cast between boolean and double",
- "sql": "select b = d FROM {tbl}"
+ "sql": "select b = d FROM {tbl}",
+ "expectedException": "Values of types .* and .* are not comparable"
},
{
"ignored": true,
@@ -76,7 +78,8 @@
"ignored": true,
"comment": "H2 doesn't support this",
"description": "check implicit cast between boolean and numeric",
- "sql": "select b = n FROM {tbl}"
+ "sql": "select b = n FROM {tbl}",
+ "expectedException": "Values of types .* and .* are not comparable"
},
{
"description": "implicit cast should fail between boolean and
timestamp",
diff --git
a/pinot-query-runtime/src/test/resources/queries/SelectExpressions.json
b/pinot-query-runtime/src/test/resources/queries/SelectExpressions.json
index 7445bcf982..e0e4fb8b23 100644
--- a/pinot-query-runtime/src/test/resources/queries/SelectExpressions.json
+++ b/pinot-query-runtime/src/test/resources/queries/SelectExpressions.json
@@ -27,6 +27,15 @@
[1, "foo"],
[2, "bar"]
]
+ },
+ "tbl3": {
+ "schema":[
+ {"name": "intArrayCol", "type": "INT", "isSingleValue": false},
+ {"name": "strArrayCol", "type": "STRING", "isSingleValue": false}
+ ],
+ "inputs": [
+ [[1, 10], ["foo1", "foo2"]]
+ ]
}
},
"queries": [
@@ -53,7 +62,8 @@
{ "sql": "SELECT intCol, intCol, doubleCol, strCol, strCol FROM {tbl1}"
},
{ "sql": "SELECT {tbl1}.intCol, {tbl1}.intCol, {tbl1}.doubleCol,
{tbl2}.strCol, {tbl2}.strCol FROM {tbl1}, {tbl2} WHERE {tbl1}.intCol =
{tbl2}.intCol" },
{ "sql": "SELECT {tbl2}.intCol, {tbl2}.intCol FROM {tbl1}, {tbl2} WHERE
{tbl1}.intCol = {tbl2}.intCol AND {tbl1}.intCol < 100 ORDER BY
{tbl1}.doubleCol" },
- { "sql": "SELECT intCol, intCol FROM {tbl1} WHERE intCol < 100"}
+ { "sql": "SELECT intCol, intCol FROM {tbl1} WHERE intCol < 100"},
+ { "sql": "SELECT intArrayCol, strArrayCol FROM {tbl3}"}
]
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]