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&mdash;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]

Reply via email to