You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by "yashmayya (via GitHub)" <gi...@apache.org> on 2026/10/19 20:47:11 UTC

[PR] Fold MSE literal function calls with runtime value types [pinot]

yashmayya opened a new pull request, #19948:
URL: https://github.com/apache/pinot/pull/19948

   When all arguments of a function call are literals, the multi-stage engine (MSE) evaluates the call at planning time (`PinotEvaluateLiteralRule`). The rule passed the values that Calcite stores: `BigDecimal` for INTEGER, BIGINT and DECIMAL literals, and `Long` millis for TIMESTAMP literals. The runtime passes `Integer`, `Long`, `Double` and `Timestamp`. `Object` and varargs parameters get the value as is, and some functions dispatch on its class. So the folded result was different from the runtime result. For example, `toThetaSketch(5)` sketched the string `"5"`, not the number 5.
   
   Found while reviewing #19757.
   
   ## Changes
   
   - `PinotEvaluateLiteralRule` converts each literal as the runtime does: `RexExpressionUtils.fromRexLiteral`, then `ColumnDataType.toExternal`, as in `FunctionOperand`. It converts the literals only after it finds the function, because some operators take literals that are not values (the Sarg of a SEARCH). Literals that the runtime cannot represent, for example INTERVAL, keep the Calcite value, as before.
   - `inIdSet` is deterministic again. #19757 set `isDeterministic = false` only because folding passed a `BigDecimal` for an INT literal.
   
   ## Which functions had the problem
   
   | Function | Result |
   |---|---|
   | `toThetaSketch`, `toHLL`, `toCpcSketch`, `toULL`, `toIntegerSumTupleSketch` | Yes, for INTEGER, BIGINT and DECIMAL literals. REAL literals already matched. |
   | `inIdSet` | Yes. Folding failed with "Cannot look up BIG_DECIMAL values". |
   | `isDistinctFrom`, `isNotDistinctFrom` | Yes (`isDistinctFrom(1.5, 1.50)` was `true`), now fixed by #19791, which compares numbers by value. |
   | `arrayValueConstructor` | Values were correct. Primitive array results are fixed by #19552. |
   | `nullIf` | No. `NULLIF` resolves to the Calcite operator, which becomes a CASE. |
   | `cast` | Not from this rule. Calcite folds `CAST(<literal> AS ...)` first, with SQL-standard text. That is a separate problem. |
   
   ## Behavior changes
   
   Only the broker folds. These results change, and now match the runtime and the single-stage engine (SSE):
   
   - Sketch functions on numeric literals return other bytes, for example `toThetaSketch(5)`.
   - A DECIMAL literal with a scale arrives as a `Double`, the type that the planner gives it. For example, `jsonFormat(1.50)` is now `'1.5'` (was `'1.50'`), and `bigDecimalToBytes(1.50)` returns other bytes.
   - A TIMESTAMP literal arrives as a `Timestamp`. Functions with `long` or `Timestamp` parameters do not change. `toThetaSketch(TIMESTAMP '...')` now fails at planning time with the runtime error. Before, it sketched the millis.
   - Both engines fold `IN_ID_SET` on literals, so a value type mismatch fails at planning time, not at runtime. `inIdSet` is now IMMUTABLE, so ingestion transforms accept it.
   
   ## Single-stage engine
   
   `CompileTimeFunctionsInvoker` needs no change. The SSE parser already gives literals the Java types of the SSE runtime (`Integer`, `Long`, `Double`, `String`, `Boolean`, `byte[]`). For example, SSE folds `toThetaSketch(5)` with an `Integer`, and gives the same bytes as MSE now. SSE has no TIMESTAMP literal type: the parser turns `TIMESTAMP '...'` into a STRING literal, and the SSE runtime gets the same string. A `Timestamp` at compile time only would make compile time and runtime disagree.
   
   ## Tests
   
   - `LiteralEvaluationPlans.json`: `IN_ID_SET` on INT and BIGINT literals, BIGINT, REAL, BOOLEAN and TIMESTAMP array literals, integer literals beyond BIGINT, a function on an INTERVAL literal, and a SEARCH on a folded literal.
   - `LiteralExpressions.json` (runtime, compared with H2, both planners): each sketch function on a literal against the same call on column values.
   - `IdSet.json`: `IN_ID_SET` on a BIGINT literal.
   - `IdSetQueriesTest`: SSE folds `IN_ID_SET` on literals.
   
   Without the change, the sketch and `IN_ID_SET` tests fail. Without the INTERVAL branch or the late conversion, the INTERVAL and SEARCH plan tests fail.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org


Re: [PR] Fold MSE literal function calls with runtime value types [pinot]

Posted by "yashmayya (via GitHub)" <gi...@apache.org>.
yashmayya merged PR #19948:
URL: https://github.com/apache/pinot/pull/19948


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org


Re: [PR] Fold MSE literal function calls with runtime value types [pinot]

Posted by "xiangfu0 (via GitHub)" <gi...@apache.org>.
xiangfu0 commented on code in PR #19948:
URL: https://github.com/apache/pinot/pull/19948#discussion_r4344123273


##########
pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java:
##########
@@ -386,22 +393,44 @@ private static boolean isUnsignedIntegerType(SqlTypeName sqlTypeName) {
     }
   }
 
+  /// Returns the value that the runtime passes to a function for the literal (see `FunctionOperand`): the value from
+  /// [RexExpressionUtils#fromRexLiteral], converted with [ColumnDataType#toExternal]. For example, an INTEGER literal
+  /// becomes an `Integer` (Calcite stores a `BigDecimal`), and a TIMESTAMP literal becomes a `Timestamp`.
   @Nullable
-  private static Object getLiteralValue(RexLiteral rexLiteral) {
-    Object value = rexLiteral.getValue();
-    if (value instanceof NlsString) {
-      // STRING
-      return ((NlsString) value).getValue();
-    } else if (value instanceof GregorianCalendar) {
-      // TIMESTAMP
-      return ((GregorianCalendar) value).getTimeInMillis();
-    } else if (value instanceof ByteString) {
-      // BYTES
-      return ((ByteString) value).getBytes();
-    } else if (value instanceof TimeUnitRange) {
-      return ((TimeUnitRange) value).name();
-    } else {
-      return value;
+  private static Object getArgumentValue(RexLiteral rexLiteral, ColumnDataType argumentType) {
+    if (argumentType == ColumnDataType.OBJECT && rexLiteral.getTypeName() != SqlTypeName.SYMBOL) {
+      // The runtime cannot represent this literal (for example an INTERVAL), so keep the value that Calcite stores
+      return rexLiteral.getValue();
+    }
+    if (canExceedArgumentType(rexLiteral, argumentType)) {
+      // Keep the exact value, which the conversion to INT or LONG can wrap around
+      return rexLiteral.getValue();
+    }
+    RexExpression.Literal literal = RexExpressionUtils.fromRexLiteral(rexLiteral);
+    Object value = literal.getValue();
+    return value != null ? literal.getDataType().toExternal(value) : null;
+  }
+
+  /// Returns whether the literal is a DECIMAL whose type can hold numbers beyond the range of its INT or LONG argument
+  /// type. The planner gives DECIMAL(p, 0) the type INT or LONG by its precision only, for example INT to
+  /// DECIMAL(10, 0), and LONG to an integer literal beyond the BIGINT range. The decision depends on the type, not on
+  /// the value, so that the literals of one type (for example the elements of an array) get the same Java class.
+  private static boolean canExceedArgumentType(RexLiteral rexLiteral, ColumnDataType argumentType) {

Review Comment:
   Minor / tradeoff worth a note: because this keeps the raw `BigDecimal` for any scale-0 DECIMAL whose *type* exceeds the target range, a value that actually fits still diverges from the runtime. E.g. `toThetaSketch(CAST(5 AS DECIMAL(10,0)))` folds the `BigDecimal` (sketched as the string "5"), while the same `5` as a plain INT literal sketches the number — the exact class of mismatch this PR fixes.
   
   I think keeping the exact value is the right choice (avoiding wraparound matters more, and a value-aware check would break the array-uniformity invariant you rely on), so no change needed — just flagging it so the narrow re-exposure is a conscious decision. A one-line comment here noting "value may still differ from runtime for a fitting value, but never wraps" would help the next reader.



##########
pinot-query-runtime/src/test/resources/queries/LiteralExpressions.json:
##########
@@ -36,5 +36,38 @@
         ]
       }
     ]
+  },
+  "literal_function_evaluation": {
+    "comments": "The planner evaluates a function call on literals. It must pass the literals as the Java values that the runtime passes for the same values, e.g. an Integer for an INT literal rather than the BigDecimal that Calcite stores, or functions that dispatch on the value class return other results. Each query compares a call on a literal with the same call on column values, which runs above the leaf stage because of the GROUP BY.",
+    "tables": {
+      "tbl": {
+        "schema": [
+          {"name": "intCol", "type": "INT"},
+          {"name": "longCol", "type": "LONG"},
+          {"name": "doubleCol", "type": "DOUBLE"}
+        ],
+        "inputs": [
+          [1, 10000000001, 1.25],
+          [5, 10000000005, 1.5]
+        ]
+      }
+    },
+    "queries": [
+      {
+        "description": "Sketches of an INT literal",
+        "sql": "SELECT intCol, toBase64(toThetaSketch(5)) = toBase64(toThetaSketch(intCol)), toBase64(toHLL(5)) = toBase64(toHLL(intCol)), toBase64(toCpcSketch(5)) = toBase64(toCpcSketch(intCol)), toBase64(toULL(5)) = toBase64(toULL(intCol)), toBase64(toIntegerSumTupleSketch(5, 1)) = toBase64(toIntegerSumTupleSketch(intCol, 1)) FROM (SELECT intCol FROM {tbl} GROUP BY intCol) t",
+        "h2Sql": "SELECT intCol, intCol = 5, intCol = 5, intCol = 5, intCol = 5, intCol = 5 FROM (SELECT intCol FROM {tbl} GROUP BY intCol) t"
+      },
+      {
+        "description": "Sketch of a BIGINT literal",
+        "sql": "SELECT longCol, toBase64(toThetaSketch(10000000005)) = toBase64(toThetaSketch(longCol)) FROM (SELECT longCol FROM {tbl} GROUP BY longCol) t",
+        "h2Sql": "SELECT longCol, longCol = 10000000005 FROM (SELECT longCol FROM {tbl} GROUP BY longCol) t"
+      },
+      {
+        "description": "Sketch of a DECIMAL literal, which the runtime reads as a DOUBLE",

Review Comment:
   Minor test-coverage gap: the PR description calls out two observable behavior changes that don't have a dedicated regression test — `jsonFormat(1.50)` now yielding `'1.5'` (scale stripped, since the arg arrives as a `Double`), and `toThetaSketch(TIMESTAMP '...')` now failing at plan time. The sketch-parity tests here are great, but a small case pinning the `1.50`->`1.5` output (and maybe asserting the TIMESTAMP-sketch error) would guard those documented changes against silent regressions.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org


Re: [PR] Fold MSE literal function calls with runtime value types [pinot]

Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #19948:
URL: https://github.com/apache/pinot/pull/19948#issuecomment-6279639624

   ## [Codecov](https://app.codecov.io/gh/apache/pinot/pull/19948?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   :x: Patch coverage is `91.66667%` with `2 lines` in your changes missing coverage. Please review.
   :white_check_mark: Project coverage is 58.94%. Comparing base ([`ad85048`](https://app.codecov.io/gh/apache/pinot/commit/ad850480ef2ea656a8c921b625578017b7d5e9ab?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`c5c0a6b`](https://app.codecov.io/gh/apache/pinot/commit/c5c0a6b09fcc3e1de3550171422abe91bc9a9c1e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
   
   | [Files with missing lines](https://app.codecov.io/gh/apache/pinot/pull/19948?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
   |---|---|---|
   | [...ot/calcite/rel/rules/PinotEvaluateLiteralRule.java](https://app.codecov.io/gh/apache/pinot/pull/19948?src=pr&el=tree&filepath=pinot-query-planner%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpinot%2Fcalcite%2Frel%2Frules%2FPinotEvaluateLiteralRule.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cGlub3QtcXVlcnktcGxhbm5lci9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcGlub3QvY2FsY2l0ZS9yZWwvcnVsZXMvUGlub3RFdmFsdWF0ZUxpdGVyYWxSdWxlLmphdmE=) | 91.66% | [0 Missing and 2 partials :warning: ](https://app.codecov.io/gh/apache/pinot/pull/19948?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   > :exclamation:  There is a different number of reports uploaded between BASE (ad85048) and HEAD (c5c0a6b). Click for more details.
   > 
   > <details><summary>HEAD has 8 uploads less than BASE</summary>
   >
   >| Flag | BASE (ad85048) | HEAD (c5c0a6b) |
   >|------|------|------|
   >|lane-a|1|0|
   >|integration|2|0|
   >|java-25|2|1|
   >|integration1|2|0|
   >|temurin|2|1|
   >|lane-b|1|0|
   ></details>
   
   <details><summary>Additional details and impacted files</summary>
   
   
   
   ```diff
   @@              Coverage Diff               @@
   ##              master   #19948       +/-   ##
   ==============================================
   - Coverage     100.00%   58.94%   -41.06%     
   + Complexity         6        1        -5     
   ==============================================
     Files              3     2729     +2726     
     Lines              6   171755   +171749     
     Branches           0    28309    +28309     
   ==============================================
   + Hits               6   101247   +101241     
   - Misses             0    61952    +61952     
   - Partials           0     8556     +8556     
   ```
   
   | [Flag](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [integration](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
   | [integration1](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
   | [java-25](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `58.94% <91.66%> (-41.06%)` | :arrow_down: |
   | [lane-a](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
   | [lane-b](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `?` | |
   | [temurin](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `58.94% <91.66%> (-41.06%)` | :arrow_down: |
   | [unittests](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `58.94% <91.66%> (?)` | |
   | [unittests1](https://app.codecov.io/gh/apache/pinot/pull/19948/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `58.94% <91.66%> (?)` | |
   
   Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
   </details>
   
   [:umbrella: View full report in Codecov by Harness](https://app.codecov.io/gh/apache/pinot/pull/19948?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).   
   :loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
   <details><summary> :rocket: New features to boost your workflow: </summary>
   
   - :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
   </details>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org


Re: [PR] Fold MSE literal function calls with runtime value types [pinot]

Posted by "yashmayya (via GitHub)" <gi...@apache.org>.
yashmayya commented on code in PR #19948:
URL: https://github.com/apache/pinot/pull/19948#discussion_r4350833090


##########
pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java:
##########
@@ -386,22 +393,44 @@ private static boolean isUnsignedIntegerType(SqlTypeName sqlTypeName) {
     }
   }
 
+  /// Returns the value that the runtime passes to a function for the literal (see `FunctionOperand`): the value from
+  /// [RexExpressionUtils#fromRexLiteral], converted with [ColumnDataType#toExternal]. For example, an INTEGER literal
+  /// becomes an `Integer` (Calcite stores a `BigDecimal`), and a TIMESTAMP literal becomes a `Timestamp`.
   @Nullable
-  private static Object getLiteralValue(RexLiteral rexLiteral) {
-    Object value = rexLiteral.getValue();
-    if (value instanceof NlsString) {
-      // STRING
-      return ((NlsString) value).getValue();
-    } else if (value instanceof GregorianCalendar) {
-      // TIMESTAMP
-      return ((GregorianCalendar) value).getTimeInMillis();
-    } else if (value instanceof ByteString) {
-      // BYTES
-      return ((ByteString) value).getBytes();
-    } else if (value instanceof TimeUnitRange) {
-      return ((TimeUnitRange) value).name();
-    } else {
-      return value;
+  private static Object getArgumentValue(RexLiteral rexLiteral, ColumnDataType argumentType) {
+    if (argumentType == ColumnDataType.OBJECT && rexLiteral.getTypeName() != SqlTypeName.SYMBOL) {
+      // The runtime cannot represent this literal (for example an INTERVAL), so keep the value that Calcite stores
+      return rexLiteral.getValue();
+    }
+    if (canExceedArgumentType(rexLiteral, argumentType)) {
+      // Keep the exact value, which the conversion to INT or LONG can wrap around
+      return rexLiteral.getValue();
+    }
+    RexExpression.Literal literal = RexExpressionUtils.fromRexLiteral(rexLiteral);
+    Object value = literal.getValue();
+    return value != null ? literal.getDataType().toExternal(value) : null;
+  }
+
+  /// Returns whether the literal is a DECIMAL whose type can hold numbers beyond the range of its INT or LONG argument
+  /// type. The planner gives DECIMAL(p, 0) the type INT or LONG by its precision only, for example INT to
+  /// DECIMAL(10, 0), and LONG to an integer literal beyond the BIGINT range. The decision depends on the type, not on
+  /// the value, so that the literals of one type (for example the elements of an array) get the same Java class.
+  private static boolean canExceedArgumentType(RexLiteral rexLiteral, ColumnDataType argumentType) {

Review Comment:
   Added a comment there in d28d8d2c3c: a fitting value of such a DECIMAL type stays a BigDecimal, unlike at runtime, but no value wraps around.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-query-runtime/src/test/resources/queries/LiteralExpressions.json:
##########
@@ -36,5 +36,38 @@
         ]
       }
     ]
+  },
+  "literal_function_evaluation": {
+    "comments": "The planner evaluates a function call on literals. It must pass the literals as the Java values that the runtime passes for the same values, e.g. an Integer for an INT literal rather than the BigDecimal that Calcite stores, or functions that dispatch on the value class return other results. Each query compares a call on a literal with the same call on column values, which runs above the leaf stage because of the GROUP BY.",
+    "tables": {
+      "tbl": {
+        "schema": [
+          {"name": "intCol", "type": "INT"},
+          {"name": "longCol", "type": "LONG"},
+          {"name": "doubleCol", "type": "DOUBLE"}
+        ],
+        "inputs": [
+          [1, 10000000001, 1.25],
+          [5, 10000000005, 1.5]
+        ]
+      }
+    },
+    "queries": [
+      {
+        "description": "Sketches of an INT literal",
+        "sql": "SELECT intCol, toBase64(toThetaSketch(5)) = toBase64(toThetaSketch(intCol)), toBase64(toHLL(5)) = toBase64(toHLL(intCol)), toBase64(toCpcSketch(5)) = toBase64(toCpcSketch(intCol)), toBase64(toULL(5)) = toBase64(toULL(intCol)), toBase64(toIntegerSumTupleSketch(5, 1)) = toBase64(toIntegerSumTupleSketch(intCol, 1)) FROM (SELECT intCol FROM {tbl} GROUP BY intCol) t",
+        "h2Sql": "SELECT intCol, intCol = 5, intCol = 5, intCol = 5, intCol = 5, intCol = 5 FROM (SELECT intCol FROM {tbl} GROUP BY intCol) t"
+      },
+      {
+        "description": "Sketch of a BIGINT literal",
+        "sql": "SELECT longCol, toBase64(toThetaSketch(10000000005)) = toBase64(toThetaSketch(longCol)) FROM (SELECT longCol FROM {tbl} GROUP BY longCol) t",
+        "h2Sql": "SELECT longCol, longCol = 10000000005 FROM (SELECT longCol FROM {tbl} GROUP BY longCol) t"
+      },
+      {
+        "description": "Sketch of a DECIMAL literal, which the runtime reads as a DOUBLE",

Review Comment:
   Added two plan tests in d28d8d2c3c (`LiteralEvaluationPlans.json`): `jsonFormat(1.50)` folds to '1.5', and `toThetaSketch(TIMESTAMP '...')` fails at planning with the runtime error. Both fail with the old rule.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org