This is an automated email from the ASF dual-hosted git repository.
FrankChen021 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new edea417869a feat: accept LONG as an alias for BIGINT in CAST
expressions (#20402)
edea417869a is described below
commit edea417869a2f2fcacd3d1f81b7ef3776908092e
Author: Nanjesh Ramesh <[email protected]>
AuthorDate: Sat Sep 26 19:53:18 2026 -0700
feat: accept LONG as an alias for BIGINT in CAST expressions (#20402)
* feat: accept LONG as a non-reserved alias for BIGINT in CAST expressions
Users of the native engine write CAST(x AS LONG) because LONG is the name
Druid's native engine uses for its 64-bit integer type, but the SQL parser
only recognized BIGINT, so this failed with a parse error.
Add LONG to the SqlTypeName1 production alongside BIGINT so both parse to
SqlTypeName.BIGINT, and add it to nonReservedKeywordsToAdd so it stays
usable
unquoted as an identifier (a column or alias named "long" is unaffected).
Closes #17425.
* fix: scope the LONG/BIGINT CAST alias to CAST, not all of DataType()
SqlTypeName1 is reached from DataType(), which also serves DDL column
types, EXTEND column lists, RowTypeName, MapTypeName, and JSON_VALUE
RETURNING, not just CAST. Adding LONG there made every one of those
positions parse LONG as BIGINT, which broke
DruidSqlDdlParserTest#testColumnNamedProjection: CREATE TABLE t
(projection LONG) is a deliberate example of a column typed with an
arbitrary native/user-defined type name, and expects to keep rendering
as "projection `LONG`", not silently become BIGINT.
Revert the SqlTypeName1 change and instead recognize LONG as an
alternative to DataType() only inside the CAST/SAFE_CAST/TRY_CAST
production, constructing the same SqlDataTypeSpec/BIGINT node
SqlTypeName1 would have. LONG stays in nonReservedKeywordsToAdd so it
remains usable unquoted as an identifier everywhere else, unchanged
from before this PR.
Thanks to kdelay and FrankChen021 for catching this in review.
* docs: simplify the LONG alias note in SQL data types
Druid has no CREATE TABLE, so drop that example and say only that LONG is
supported as a CAST alias for BIGINT.
---
docs/querying/sql-data-types.md | 2 +-
sql/src/main/codegen/config.fmpp | 2 +
sql/src/main/codegen/templates/Parser.jj | 12 ++++++
.../apache/druid/sql/calcite/CalciteQueryTest.java | 45 ++++++++++++++++++++++
4 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/docs/querying/sql-data-types.md b/docs/querying/sql-data-types.md
index c7288802e46..8d95ae1d0ca 100644
--- a/docs/querying/sql-data-types.md
+++ b/docs/querying/sql-data-types.md
@@ -60,7 +60,7 @@ The following table describes how Druid maps SQL types onto
native types when ru
|TINYINT|LONG|`0`||
|SMALLINT|LONG|`0`||
|INTEGER|LONG|`0`||
-|BIGINT|LONG|`0`|Druid LONG columns (except `__time`) are reported as BIGINT|
+|BIGINT|LONG|`0`|Druid LONG columns (except `__time`) are reported as BIGINT.
`LONG` is supported only as a `CAST` alias for `BIGINT`, for example `CAST(x AS
LONG)`, matching the name Druid's native engine uses for this type.|
|TIMESTAMP|LONG|`0`, meaning 1970-01-01 00:00:00 UTC|Druid's `__time` column
is reported as TIMESTAMP. Casts between string and timestamp types assume
standard SQL formatting, such as `2000-01-02 03:04:05`, not ISO 8601
formatting. For handling other formats, use one of the [time
functions](sql-scalar.md#date-and-time-functions).|
|DATE|LONG|`0`, meaning 1970-01-01|Casting TIMESTAMP to DATE rounds down the
timestamp to the nearest day. Casts between string and date types assume
standard SQL formatting—for example, `2000-01-02`. For handling other
formats, use one of the [time
functions](sql-scalar.md#date-and-time-functions).|
|ARRAY|ARRAY|`NULL`|Druid native array types work as SQL arrays, and
multi-value strings can be converted to arrays. See [Arrays](#arrays) for more
information.|
diff --git a/sql/src/main/codegen/config.fmpp b/sql/src/main/codegen/config.fmpp
index f4f61f5e332..dd6533ad201 100644
--- a/sql/src/main/codegen/config.fmpp
+++ b/sql/src/main/codegen/config.fmpp
@@ -84,6 +84,7 @@ data: {
"PROPERTIES"
"PROJECTION"
"SEALED"
+ "LONG"
]
nonReservedKeywordsToAdd: [
@@ -94,6 +95,7 @@ data: {
"PROPERTIES"
"PROJECTION"
"SEALED"
+ "LONG"
]
# List of methods for parsing custom SQL statements.
diff --git a/sql/src/main/codegen/templates/Parser.jj
b/sql/src/main/codegen/templates/Parser.jj
index 100b955f264..d19a3dece71 100644
--- a/sql/src/main/codegen/templates/Parser.jj
+++ b/sql/src/main/codegen/templates/Parser.jj
@@ -6708,6 +6708,18 @@ SqlNode BuiltinFunctionCall() :
<LPAREN> AddExpression(args, ExprContext.ACCEPT_SUB_QUERY)
<AS>
(
+ // LONG is the name Druid's native engine uses for its 64-bit
integer type, so
+ // CAST(x AS LONG) is accepted as an alias for CAST(x AS BIGINT).
This is scoped to
+ // CAST rather than added to DataType() itself, so a column or
table named LONG
+ // (a DDL type name, an EXTEND column type, etc.) is unaffected.
+ LOOKAHEAD(2)
+ <LONG> {
+ dt = new SqlDataTypeSpec(
+ new SqlBasicTypeNameSpec(SqlTypeName.BIGINT, getPos()),
+ getPos());
+ args.add(dt);
+ }
+ |
dt = DataType() { args.add(dt); }
|
LOOKAHEAD(2)
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
index ac412d23feb..9d92bcc2b9f 100644
--- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
+++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java
@@ -6201,6 +6201,51 @@ public class CalciteQueryTest extends
BaseCalciteQueryTest
);
}
+ @Test
+ public void testCountStarWithFilterOnCastAsLong()
+ {
+ // LONG is a non-reserved alias for BIGINT, so this must plan identically
to
+ // testCountStarWithFilterOnCastedString() above.
+ testQuery(
+ "SELECT COUNT(*) FROM druid.foo WHERE CAST(dim1 AS long) = 2",
+ ImmutableList.of(
+ Druids.newTimeseriesQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(querySegmentSpec(Filtration.eternity()))
+ .granularity(Granularities.ALL)
+ .filters(equality("dim1", 2L, ColumnType.LONG))
+ .aggregators(aggregators(new CountAggregatorFactory("a0")))
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{1L}
+ )
+ );
+ }
+
+ @Test
+ public void testLongIsUsableAsAnIdentifier()
+ {
+ // LONG is added to nonReservedKeywordsToAdd, so it must remain usable
unquoted as a column alias.
+ testQuery(
+ "SELECT dim1 AS long FROM druid.foo LIMIT 1",
+ ImmutableList.of(
+ newScanQueryBuilder()
+ .dataSource(CalciteTests.DATASOURCE1)
+ .intervals(querySegmentSpec(Filtration.eternity()))
+ .columns("dim1")
+ .columnTypes(ColumnType.STRING)
+ .limit(1)
+ .context(QUERY_CONTEXT_DEFAULT)
+ .build()
+ ),
+ ImmutableList.of(
+ new Object[]{""}
+ )
+ );
+ }
+
@Test
public void testCountStarWithTimeFilter()
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]