This is an automated email from the ASF dual-hosted git repository.
guohongyu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 6ba31302be [CALCITE-6257] StarRocks dialect implementation
6ba31302be is described below
commit 6ba31302be35f19e0ccf289a1c6705a79bf87587
Author: YiwenWu <[email protected]>
AuthorDate: Thu Feb 22 19:29:37 2024 +0800
[CALCITE-6257] StarRocks dialect implementation
---
.../java/org/apache/calcite/sql/SqlDialect.java | 1 +
.../apache/calcite/sql/SqlDialectFactoryImpl.java | 3 +
.../calcite/sql/dialect/StarRocksSqlDialect.java | 133 ++++++++++
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 271 ++++++++++++++++++---
4 files changed, 380 insertions(+), 28 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index 03602a5486..30c912dcb8 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -1393,6 +1393,7 @@ public class SqlDialect {
VERTICA("Vertica", "\"", NullCollation.HIGH),
SQLSTREAM("SQLstream", "\"", NullCollation.HIGH),
SPARK("Spark", null, NullCollation.LOW),
+ STARROCKS("StarRocks", "`", NullCollation.LOW),
/** Paraccel, now called Actian Matrix. Redshift is based on this, so
* presumably the dialect capabilities are similar. */
diff --git
a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
index e44b6f0435..b22f8fb466 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
@@ -47,6 +47,7 @@ import org.apache.calcite.sql.dialect.PrestoSqlDialect;
import org.apache.calcite.sql.dialect.RedshiftSqlDialect;
import org.apache.calcite.sql.dialect.SnowflakeSqlDialect;
import org.apache.calcite.sql.dialect.SparkSqlDialect;
+import org.apache.calcite.sql.dialect.StarRocksSqlDialect;
import org.apache.calcite.sql.dialect.SybaseSqlDialect;
import org.apache.calcite.sql.dialect.TeradataSqlDialect;
import org.apache.calcite.sql.dialect.VerticaSqlDialect;
@@ -230,6 +231,8 @@ public class SqlDialectFactoryImpl implements
SqlDialectFactory {
return SnowflakeSqlDialect.DEFAULT;
case SPARK:
return SparkSqlDialect.DEFAULT;
+ case STARROCKS:
+ return StarRocksSqlDialect.DEFAULT;
case SYBASE:
return SybaseSqlDialect.DEFAULT;
case TERADATA:
diff --git
a/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
new file mode 100644
index 0000000000..8e9950da7a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.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.calcite.sql.dialect;
+
+import org.apache.calcite.avatica.util.TimeUnitRange;
+import org.apache.calcite.config.NullCollation;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.SqlAbstractDateTimeLiteral;
+import org.apache.calcite.sql.SqlAlienSystemTypeNameSpec;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.fun.SqlFloorFunction;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import static org.apache.calcite.util.RelToSqlConverterUtil.unparseHiveTrim;
+
+/**
+ * A <code>SqlDialect</code> implementation for the StarRocks database.
+ */
+public class StarRocksSqlDialect extends MysqlSqlDialect {
+
+ public static final SqlDialect.Context DEFAULT_CONTEXT =
SqlDialect.EMPTY_CONTEXT
+ .withDatabaseProduct(SqlDialect.DatabaseProduct.STARROCKS)
+ .withIdentifierQuoteString("`")
+ .withNullCollation(NullCollation.LOW);
+
+ public static final SqlDialect DEFAULT = new
StarRocksSqlDialect(DEFAULT_CONTEXT);
+
+ /**
+ * Creates a StarRocksSqlDialect.
+ */
+ public StarRocksSqlDialect(Context context) {
+ super(context);
+ }
+
+ @Override public boolean supportsGroupByWithRollup() {
+ return false;
+ }
+
+ @Override public boolean supportsTimestampPrecision() {
+ return false;
+ }
+
+ @Override public boolean supportsApproxCountDistinct() {
+ return true;
+ }
+
+ @Override public void unparseCall(SqlWriter writer, SqlCall call, int
leftPrec, int rightPrec) {
+ switch (call.getKind()) {
+ case ARRAY_VALUE_CONSTRUCTOR:
+ final SqlWriter.Frame arrayFrame = writer.startList("[", "]");
+ for (SqlNode operand : call.getOperandList()) {
+ writer.sep(",");
+ operand.unparse(writer, leftPrec, rightPrec);
+ }
+ writer.endList(arrayFrame);
+ break;
+ case MAP_VALUE_CONSTRUCTOR:
+ writer.keyword(call.getOperator().getName());
+ final SqlWriter.Frame mapFrame = writer.startList("{", "}");
+ for (int i = 0; i < call.operandCount(); i++) {
+ String sep = i % 2 == 0 ? "," : ":";
+ writer.sep(sep);
+ call.operand(i).unparse(writer, leftPrec, rightPrec);
+ }
+ writer.endList(mapFrame);
+ break;
+ case TRIM:
+ unparseHiveTrim(writer, call, leftPrec, rightPrec);
+ break;
+ case FLOOR:
+ if (call.operandCount() != 2) {
+ super.unparseCall(writer, call, leftPrec, rightPrec);
+ return;
+ }
+ final SqlLiteral timeUnitNode = call.operand(1);
+ final TimeUnitRange timeUnit =
timeUnitNode.getValueAs(TimeUnitRange.class);
+ SqlCall newCall =
+ SqlFloorFunction.replaceTimeUnitOperand(call, timeUnit.name(),
+ timeUnitNode.getParserPosition());
+ SqlFloorFunction.unparseDatetimeFunction(writer, newCall, "DATE_TRUNC",
false);
+ break;
+ default:
+ super.unparseCall(writer, call, leftPrec, rightPrec);
+ break;
+ }
+ }
+
+ @Override public @Nullable SqlNode getCastSpec(RelDataType type) {
+ switch (type.getSqlTypeName()) {
+ case TIMESTAMP:
+ return new SqlDataTypeSpec(
+ new SqlAlienSystemTypeNameSpec(
+ "DATETIME",
+ type.getSqlTypeName(),
+ SqlParserPos.ZERO),
+ SqlParserPos.ZERO);
+ default:
+ return super.getCastSpec(type);
+ }
+ }
+
+ @Override public void unparseDateTimeLiteral(SqlWriter writer,
+ SqlAbstractDateTimeLiteral literal, int leftPrec, int rightPrec) {
+ if (literal.getTypeName() == SqlTypeName.TIMESTAMP) {
+ writer.literal("DATETIME '" + literal.toFormattedString() + "'");
+ } else {
+ super.unparseDateTimeLiteral(writer, literal, leftPrec, rightPrec);
+ }
+ }
+
+}
diff --git
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 394aac0780..1de16cb69e 100644
---
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -189,6 +189,7 @@ class RelToSqlConverterTest {
.put(DatabaseProduct.ORACLE.getDialect(), DatabaseProduct.ORACLE)
.put(DatabaseProduct.POSTGRESQL.getDialect(),
DatabaseProduct.POSTGRESQL)
.put(DatabaseProduct.PRESTO.getDialect(), DatabaseProduct.PRESTO)
+ .put(DatabaseProduct.STARROCKS.getDialect(), DatabaseProduct.STARROCKS)
.build();
}
@@ -542,14 +543,18 @@ class RelToSqlConverterTest {
+ "FROM `foodmart`.`product`";
final String expectedPresto = "SELECT COUNT(*)\n"
+ "FROM \"foodmart\".\"product\"";
+ final String expectedStarRocks = "SELECT COUNT(*)\n"
+ + "FROM `foodmart`.`product`";
sql(sql0)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
sql(sql1)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testSelectQueryWithGroupByEmpty2() {
@@ -854,10 +859,16 @@ class RelToSqlConverterTest {
+ "FROM `foodmart`.`product`\n"
+ "GROUP BY ROLLUP(`product_class_id`, `brand_name`)\n"
+ "ORDER BY `product_class_id` NULLS LAST, `brand_name` NULLS LAST";
+ final String expectedStarRocks = "SELECT `product_class_id`,
`brand_name`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`, `brand_name`)\n"
+ + "ORDER BY `product_class_id` IS NULL, `product_class_id`,
`brand_name` IS NULL, "
+ + "`brand_name`";
sql(query)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withMysql8().ok(expectedMysql8);
+ .withMysql8().ok(expectedMysql8)
+ .withStarRocks().ok(expectedStarRocks);
}
/** As {@link #testSelectQueryWithGroupByRollup()},
@@ -876,9 +887,15 @@ class RelToSqlConverterTest {
+ "FROM `foodmart`.`product`\n"
+ "GROUP BY `product_class_id`, `brand_name` WITH ROLLUP) AS `t0`\n"
+ "ORDER BY `brand_name`, `product_class_id`";
+ final String expectedStarRocks = "SELECT `product_class_id`,
`brand_name`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`, `brand_name`)\n"
+ + "ORDER BY `brand_name` IS NULL, `brand_name`, `product_class_id` IS
NULL, "
+ + "`product_class_id`";
sql(query)
.ok(expected)
- .withMysql().ok(expectedMysql);
+ .withMysql().ok(expectedMysql)
+ .withStarRocks().ok(expectedStarRocks);
}
/** Test case for
@@ -974,10 +991,15 @@ class RelToSqlConverterTest {
+ "FROM \"foodmart\".\"product\"\n"
+ "GROUP BY ROLLUP(\"product_class_id\")\n"
+ "ORDER BY \"product_class_id\", 2";
+ final String expectedStarRocks = "SELECT `product_class_id`, COUNT(*) AS
`C`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`)\n"
+ + "ORDER BY `product_class_id` IS NULL, `product_class_id`, COUNT(*)
IS NULL, 2";
sql(query)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
/** As {@link #testSelectQueryWithSingletonCube()}, but no ORDER BY
@@ -995,10 +1017,14 @@ class RelToSqlConverterTest {
final String expectedPresto = "SELECT \"product_class_id\", COUNT(*) AS
\"C\"\n"
+ "FROM \"foodmart\".\"product\"\n"
+ "GROUP BY ROLLUP(\"product_class_id\")";
+ final String expectedStarRocks = "SELECT `product_class_id`, COUNT(*) AS
`C`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`)";
sql(query)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
/** Cannot rewrite if ORDER BY contains a column not in GROUP BY (in this
@@ -1021,9 +1047,15 @@ class RelToSqlConverterTest {
+ "ORDER BY `product_class_id` IS NULL, `product_class_id`,"
+ " `brand_name` IS NULL, `brand_name`,"
+ " COUNT(*) IS NULL, 3";
+ final String expectedStarRocks = "SELECT `product_class_id`, `brand_name`,
COUNT(*) AS `C`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`, `brand_name`)\n"
+ + "ORDER BY `product_class_id` IS NULL, `product_class_id`,
`brand_name` IS NULL, "
+ + "`brand_name`, COUNT(*) IS NULL, 3";
sql(query)
.ok(expected)
- .withMysql().ok(expectedMysql);
+ .withMysql().ok(expectedMysql)
+ .withStarRocks().ok(expectedStarRocks);
}
/** As {@link #testSelectQueryWithSingletonCube()}, but with LIMIT. */
@@ -1046,10 +1078,15 @@ class RelToSqlConverterTest {
+ "FROM \"foodmart\".\"product\"\n"
+ "GROUP BY ROLLUP(\"product_class_id\")\n"
+ "LIMIT 5";
+ final String expectedStarRocks = "SELECT `product_class_id`, COUNT(*) AS
`C`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`)\n"
+ + "LIMIT 5";
sql(query)
.ok(expected)
.withMysql().ok(expectedMysql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
/**
@@ -1705,6 +1742,10 @@ class RelToSqlConverterTest {
+ "GROUP BY product_id) t1";
final String expectedSpark = expectedHive;
final String expectedExasol = expectedBigQuery;
+ final String expectedStarRocks = "SELECT SUM(`net_weight1`) AS
`net_weight_converted`\n"
+ + "FROM (SELECT SUM(`net_weight`) AS `net_weight1`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY `product_id`) AS `t1`";
sql(query)
.withBigQuery().ok(expectedBigQuery)
.withExasol().ok(expectedExasol)
@@ -1713,7 +1754,8 @@ class RelToSqlConverterTest {
.withOracle().ok(expectedOracle)
.withPostgresql().ok(expectedPostgresql)
.withSpark().ok(expectedSpark)
- .withVertica().ok(expectedVertica);
+ .withVertica().ok(expectedVertica)
+ .withStarRocks().ok(expectedStarRocks);
}
/** Test case for
@@ -2544,9 +2586,13 @@ class RelToSqlConverterTest {
final String expectedPresto = "SELECT *\n"
+ "FROM \"foodmart\".\"employee\"\n"
+ "WHERE (\"hire_date\" - INTERVAL '19800' SECOND) >
CAST(\"hire_date\" AS TIMESTAMP)";
+ final String expectedStarRocks = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` - INTERVAL '19800' SECOND) > CAST(`hire_date` AS
DATETIME)";
sql(query)
.withSpark().ok(expectedSpark)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testExasolCastToTimestamp() {
@@ -2652,7 +2698,12 @@ class RelToSqlConverterTest {
+ "FROM foodmart.product\n"
+ "LIMIT 100\n"
+ "OFFSET 10";
- sql(query).withHive().ok(expected);
+ final String expectedStarRocks = "SELECT `product_id`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "LIMIT 100\n"
+ + "OFFSET 10";
+ sql(query).withHive().ok(expected)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testPositionFunctionForHive() {
@@ -3405,17 +3456,19 @@ class RelToSqlConverterTest {
final String expectedClickHouse = "SELECT `product_id`\n"
+ "FROM `foodmart`.`product`\n"
+ "LIMIT 10, 100";
- sql(query)
- .ok(expected)
- .withClickHouse().ok(expectedClickHouse);
-
final String expectedPresto = "SELECT \"product_id\"\n"
+ "FROM \"foodmart\".\"product\"\n"
+ "OFFSET 10\n"
+ "LIMIT 100";
+ final String expectedStarRocks = "SELECT `product_id`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "LIMIT 100\n"
+ + "OFFSET 10";
sql(query)
.ok(expected)
- .withPresto().ok(expectedPresto);
+ .withClickHouse().ok(expectedClickHouse)
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testSelectQueryWithLimitOffsetClause() {
@@ -3432,8 +3485,14 @@ class RelToSqlConverterTest {
+ "ORDER BY net_weight NULLS LAST\n"
+ "LIMIT 100\n"
+ "OFFSET 10";
+ final String expectedStarRocks = "SELECT `product_id`\n"
+ + "FROM `foodmart`.`product`\n"
+ + "ORDER BY `net_weight` IS NULL, `net_weight`\n"
+ + "LIMIT 100\n"
+ + "OFFSET 10";
sql(query).ok(expected)
- .withBigQuery().ok(expectedBigQuery);
+ .withBigQuery().ok(expectedBigQuery)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testSelectQueryWithParameters() {
@@ -4506,13 +4565,16 @@ class RelToSqlConverterTest {
String expectedPresto = "SELECT DATE_TRUNC('MINUTE', \"hire_date\")\n"
+ "FROM \"foodmart\".\"employee\"";
String expectedFirebolt = expectedPostgresql;
+ String expectedStarRocks = "SELECT DATE_TRUNC('MINUTE', `hire_date`)\n"
+ + "FROM `foodmart`.`employee`";
sql(query)
.withClickHouse().ok(expectedClickHouse)
.withFirebolt().ok(expectedFirebolt)
.withHsqldb().ok(expectedHsqldb)
.withOracle().ok(expectedOracle)
.withPostgresql().ok(expectedPostgresql)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testFetchMssql() {
@@ -4523,21 +4585,26 @@ class RelToSqlConverterTest {
}
@Test void testFetchOffset() {
- String query = "SELECT * FROM \"employee\" LIMIT 1 OFFSET 1";
- String expectedMssql = "SELECT *\n"
+ final String query = "SELECT * FROM \"employee\" LIMIT 1 OFFSET 1";
+ final String expectedMssql = "SELECT *\n"
+ "FROM [foodmart].[employee]\n"
+ "OFFSET 1 ROWS\n"
+ "FETCH NEXT 1 ROWS ONLY";
- String expectedSybase = "SELECT TOP (1) START AT 1 *\n"
+ final String expectedSybase = "SELECT TOP (1) START AT 1 *\n"
+ "FROM foodmart.employee";
final String expectedPresto = "SELECT *\n"
+ "FROM \"foodmart\".\"employee\"\n"
+ "OFFSET 1\n"
+ "LIMIT 1";
+ final String expectedStarRocks = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "LIMIT 1\n"
+ + "OFFSET 1";
sql(query)
.withMssql().ok(expectedMssql)
.withSybase().ok(expectedSybase)
- .withPresto().ok(expectedPresto);
+ .withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testFloorMssqlMonth() {
@@ -4803,6 +4870,8 @@ class RelToSqlConverterTest {
final String expectedFirebolt = expectedPresto;
final String expectedMysql = "SELECT SUBSTRING(`brand_name`, 2)\n"
+ "FROM `foodmart`.`product`";
+ final String expectedStarRocks = "SELECT SUBSTRING(`brand_name`, 2)\n"
+ + "FROM `foodmart`.`product`";
sql(query)
.withBigQuery().ok(expectedBigQuery)
.withClickHouse().ok(expectedClickHouse)
@@ -4815,7 +4884,8 @@ class RelToSqlConverterTest {
.withPostgresql().ok(expectedPostgresql)
.withPresto().ok(expectedPresto)
.withRedshift().ok(expectedRedshift)
- .withSnowflake().ok(expectedSnowflake);
+ .withSnowflake().ok(expectedSnowflake)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testSubstringWithFor() {
@@ -4838,6 +4908,8 @@ class RelToSqlConverterTest {
+ "FROM `foodmart`.`product`";
final String expectedMssql = "SELECT SUBSTRING([brand_name], 2, 3)\n"
+ "FROM [foodmart].[product]";
+ final String expectedStarRocks = "SELECT SUBSTRING(`brand_name`, 2, 3)\n"
+ + "FROM `foodmart`.`product`";
sql(query)
.withBigQuery().ok(expectedBigQuery)
.withClickHouse().ok(expectedClickHouse)
@@ -4848,7 +4920,8 @@ class RelToSqlConverterTest {
.withPostgresql().ok(expectedPostgresql)
.withPresto().ok(expectedPresto)
.withRedshift().ok(expectedRedshift)
- .withSnowflake().ok(expectedSnowflake);
+ .withSnowflake().ok(expectedSnowflake)
+ .withStarRocks().ok(expectedStarRocks);
}
/** Test case for
@@ -6638,9 +6711,14 @@ class RelToSqlConverterTest {
+ "FROM foodmart.store\n"
+ "CROSS JOIN foodmart.employee\n"
+ "CROSS JOIN foodmart.department";
+ final String expectedStarRocks = "SELECT *\n"
+ + "FROM `foodmart`.`store`,\n"
+ + "`foodmart`.`employee`,\n"
+ + "`foodmart`.`department`";
sql(sql)
.withMysql().ok(expectedMysql)
- .withSpark().ok(expectedSpark);
+ .withSpark().ok(expectedSpark)
+ .withStarRocks().ok(expectedStarRocks);
}
/** As {@link #testCommaCrossJoin3way()}, but shows that if there is a
@@ -6855,10 +6933,14 @@ class RelToSqlConverterTest {
final String expectedSpark = "SELECT COUNT(*)\n"
+ "FROM foodmart.product\n"
+ "GROUP BY ROLLUP(product_id, product_class_id)";
+ final String expectedStarRocks = "SELECT COUNT(*)\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_id`, `product_class_id`)";
sql(query)
.ok(expected)
.withPresto().ok(expected)
- .withSpark().ok(expectedSpark);
+ .withSpark().ok(expectedSpark)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testJsonType() {
@@ -7266,13 +7348,16 @@ class RelToSqlConverterTest {
+ "FROM \"foodmart\".\"product\"";
final String expectedPrestoSql = "SELECT APPROX_DISTINCT(\"product_id\")\n"
+ "FROM \"foodmart\".\"product\"";
+ final String expectedStarRocksSql = "SELECT
APPROX_COUNT_DISTINCT(`product_id`)\n"
+ + "FROM `foodmart`.`product`";
sql(query).ok(expectedExact)
.withHive().ok(expectedApprox)
.withSpark().ok(expectedApprox)
.withBigQuery().ok(expectedApprox)
.withOracle().ok(expectedApproxQuota)
.withSnowflake().ok(expectedApproxQuota)
- .withPresto().ok(expectedPrestoSql);
+ .withPresto().ok(expectedPrestoSql)
+ .withStarRocks().ok(expectedStarRocksSql);
}
@Test void testRowValueExpression() {
@@ -7883,7 +7968,9 @@ class RelToSqlConverterTest {
final String query = "SELECT MAP['k1', 'v1', 'k2', 'v2']";
final String expectedPresto = "SELECT MAP (ARRAY['k1', 'k2'], ARRAY['v1',
'v2'])\n"
+ "FROM (VALUES (0)) AS \"t\" (\"ZERO\")";
- sql(query).withPresto().ok(expectedPresto);
+ final String expectedStarRocks = "SELECT MAP { 'k1' : 'v1', 'k2' : 'v2' }";
+ sql(query).withPresto().ok(expectedPresto)
+ .withStarRocks().ok(expectedStarRocks);
}
@Test void testMapValueConstructorWithArray() {
@@ -7893,6 +7980,130 @@ class RelToSqlConverterTest {
sql(query).withPresto().ok(expectedPresto);
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-6257">[CALCITE-6257]
+ * StarRocks dialect implementation </a>.
+ */
+ @Test void testCastToTimestamp() {
+ final String query = "select * from \"employee\" where \"hire_date\" - "
+ + "INTERVAL '19800' SECOND(5) > cast(\"hire_date\" as TIMESTAMP) ";
+ final String expectedStarRocks = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` - INTERVAL '19800' SECOND) > CAST(`hire_date` AS
DATETIME)";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testArrayValueConstructor() {
+ final String query = "SELECT ARRAY[1, 2, 3]";
+ final String expectedStarRocks = "SELECT[1, 2, 3]";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithBothSpecialCharacter() {
+ final String query = "SELECT TRIM(BOTH '$@*A' from
'$@*AABC$@*AADCAA$@*A')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT
REGEXP_REPLACE('$@*AABC$@*AADCAA$@*A',"
+ + " '^(\\$\\@\\*A)*|(\\$\\@\\*A)*$', '')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testUnparseSqlIntervalQualifier() {
+ final String sql0 = "select * from \"employee\" where \"hire_date\" - "
+ + "INTERVAL '19800' SECOND(5) > TIMESTAMP '2005-10-17 00:00:00' ";
+ final String expect0 = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` - INTERVAL '19800' SECOND) > DATETIME
'2005-10-17 00:00:00'";
+ sql(sql0).withStarRocks().ok(expect0);
+
+ final String sql1 = "select * from \"employee\" where \"hire_date\" + "
+ + "INTERVAL '10' HOUR > TIMESTAMP '2005-10-17 00:00:00' ";
+ final String expect1 = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` + INTERVAL '10' HOUR) > DATETIME '2005-10-17
00:00:00'";
+ sql(sql1).withStarRocks().ok(expect1);
+
+ final String sql2 = "select * from \"employee\" where \"hire_date\" + "
+ + "INTERVAL '1' YEAR > TIMESTAMP '2005-10-17 00:00:00' ";
+ final String expect2 = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` + INTERVAL '1' YEAR) > DATETIME '2005-10-17
00:00:00'";
+ sql(sql2).withStarRocks().ok(expect2);
+
+ final String sql3 = "select * from \"employee\" "
+ + "where \"hire_date\" + INTERVAL '39' MINUTE"
+ + " > TIMESTAMP '2005-10-17 00:00:00' ";
+ final String expect3 = "SELECT *\n"
+ + "FROM `foodmart`.`employee`\n"
+ + "WHERE (`hire_date` + INTERVAL '39' MINUTE) > DATETIME '2005-10-17
00:00:00'";
+ sql(sql3).withStarRocks().ok(expect3);
+ }
+
+ @Test void testTrim() {
+ final String query = "SELECT TRIM(' str ')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT TRIM(' str ')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithBoth() {
+ final String query = "SELECT TRIM(both ' ' from ' str ')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT TRIM(' str ')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithLeading() {
+ final String query = "SELECT TRIM(LEADING ' ' from ' str ')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT LTRIM(' str ')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithTailing() {
+ final String query = "SELECT TRIM(TRAILING ' ' from ' str ')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT RTRIM(' str ')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithBothChar() {
+ final String query = "SELECT TRIM(both 'a' from 'abcda')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT REGEXP_REPLACE('abcda',
'^(a)*|(a)*$', '')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithTailingChar() {
+ final String query = "SELECT TRIM(TRAILING 'a' from 'abcd')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT REGEXP_REPLACE('abcd', '(a)*$',
'')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testTrimWithLeadingChar() {
+ final String query = "SELECT TRIM(LEADING 'a' from 'abcd')\n"
+ + "from \"foodmart\".\"reserve_employee\"";
+ final String expectedStarRocks = "SELECT REGEXP_REPLACE('abcd', '^(a)*',
'')\n"
+ + "FROM `foodmart`.`reserve_employee`";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
+ @Test void testSelectQueryWithRollup() {
+ final String query = "select \"product_class_id\", \"product_id\",
count(*) "
+ + "from \"product\" group by rollup(\"product_class_id\",
\"product_id\")";
+ final String expectedStarRocks = "SELECT `product_class_id`, `product_id`,
COUNT(*)\n"
+ + "FROM `foodmart`.`product`\n"
+ + "GROUP BY ROLLUP(`product_class_id`, `product_id`)";
+ sql(query).withStarRocks().ok(expectedStarRocks);
+ }
+
/** Fluid interface to run tests. */
static class Sql {
private final CalciteAssert.SchemaSpec schemaSpec;
@@ -8041,6 +8252,10 @@ class RelToSqlConverterTest {
return dialect(DatabaseProduct.SPARK.getDialect());
}
+ Sql withStarRocks() {
+ return dialect(DatabaseProduct.STARROCKS.getDialect());
+ }
+
Sql withPostgresqlModifiedTypeSystem() {
// Postgresql dialect with max length for varchar set to 256
final PostgresqlSqlDialect postgresqlSqlDialect =