yashmayya commented on code in PR #19790:
URL: https://github.com/apache/pinot/pull/19790#discussion_r4253696300


##########
pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToPlanNodeConverter.java:
##########
@@ -1115,9 +1114,12 @@ private static ColumnDataType resolveDecimal(RelDataType 
relDataType, boolean is
     int precision = relDataType.getPrecision();
     int scale = relDataType.getScale();
     if (scale == 0) {
-      if (precision <= 10) {
+      // Pick the type by the precision, so that it holds every value of the 
DECIMAL type: INT holds every number with
+      // up to 9 digits, and LONG every number with up to 18 digits. For 
example, Calcite gives an integer literal
+      // beyond the BIGINT range the type DECIMAL(p, 0) with p >= 19, which 
LONG would wrap around.
+      if (precision <= 9) {
         return isArray ? ColumnDataType.INT_ARRAY : ColumnDataType.INT;
-      } else if (precision <= 38) {
+      } else if (precision <= 18) {

Review Comment:
   Thanks, confirmed. `ARRAY_AGG(CAST(longCol AS DECIMAL(19, 0)), 'LONG')` and 
the `DECIMAL(10, 0)` / `'INT'` variant fail with this change and work on 
master. The root cause is older: `ARRAY_AGG(intCol, 'LONG')` and 
`ARRAY_AGG(doubleCol, 'FLOAT')` already fail on master. The plan type follows 
the value, but the accumulator follows the data type argument.
   
   I could not read the literal in the return type inference. Calcite infers 
aggregate types again through `AggCallBinding` (for example in the 
`typeMatchesInferred` assertion of the `Aggregate` constructor), and that 
binding cannot read literal operands. So `ArrayAggOperandTypeChecker` now casts 
the value to the explicit data type during validation, when the types differ. 
The existing inference then returns `ARRAY<data type>` in all places. The fix 
is in the planner only, so it covers both planners and older servers.
   
   One visible change: when the value has another type that already worked, the 
result now has the data type, like the single-stage engine. For example, 
`ARRAY_AGG(longCol, 'TIMESTAMP')` returns timestamps instead of longs. I added 
this to the PR description.
   
   Tests cover the two DECIMAL cases, the two cases that fail on master, the 
values of `'FLOAT'` and `'BIG_DECIMAL'` arrays, and a plan that shows the cast.
   



##########
pinot-query-planner/src/main/java/org/apache/pinot/query/planner/logical/RelToPlanNodeConverter.java:
##########
@@ -1115,9 +1114,12 @@ private static ColumnDataType resolveDecimal(RelDataType 
relDataType, boolean is
     int precision = relDataType.getPrecision();
     int scale = relDataType.getScale();
     if (scale == 0) {
-      if (precision <= 10) {
+      // Pick the type by the precision, so that it holds every value of the 
DECIMAL type: INT holds every number with
+      // up to 9 digits, and LONG every number with up to 18 digits. For 
example, Calcite gives an integer literal
+      // beyond the BIGINT range the type DECIMAL(p, 0) with p >= 19, which 
LONG would wrap around.
+      if (precision <= 9) {
         return isArray ? ColumnDataType.INT_ARRAY : ColumnDataType.INT;
-      } else if (precision <= 38) {
+      } else if (precision <= 18) {

Review Comment:
   Thanks, confirmed. This query returns false with this change and true on 
master. The same problem already exists on master for BIG_DECIMAL columns: 
`SUM(bigDecimalCol) IN (1, 2)` returns false when the sum is 1.
   
   `FilterOperand.In` now compares BigDecimal values with `compareTo`, like the 
comparison operators and like the single-stage `IN`, which uses a `TreeSet` for 
this reason. This also fixes the BIG_DECIMAL column case. Tests cover `IN` in a 
projection and in `HAVING`, and `NOT IN`, for `DECIMAL(19, 0)` and `DECIMAL(40, 
0)`.
   
   During a rolling upgrade, servers that are not upgraded yet still compare 
with `equals`.
   



##########
pinot-query-planner/src/main/java/org/apache/pinot/query/planner/physical/v2/PRelToPlanNodeConverter.java:
##########
@@ -375,9 +374,12 @@ private static ColumnDataType resolveDecimal(RelDataType 
relDataType, boolean is
     int precision = relDataType.getPrecision();
     int scale = relDataType.getScale();
     if (scale == 0) {
-      if (precision <= 10) {
+      // Pick the type by the precision, so that it holds every value of the 
DECIMAL type: INT holds every number with
+      // up to 9 digits, and LONG every number with up to 18 digits. For 
example, Calcite gives an integer literal
+      // beyond the BIGINT range the type DECIMAL(p, 0) with p >= 19, which 
LONG would wrap around.
+      if (precision <= 9) {
         return isArray ? ColumnDataType.INT_ARRAY : ColumnDataType.INT;
-      } else if (precision <= 38) {
+      } else if (precision <= 18) {

Review Comment:
   Thanks, confirmed. `CAST(doubleCol AS DECIMAL(19, 0))` returns 1.2 with this 
change and 1 on master, in the leaf stage and in intermediate stages. Master 
already keeps the fraction for `DECIMAL(p, 0)` with p > 38.
   
   To round a BIG_DECIMAL to the requested scale exactly, servers need more 
than the target type name that they get now, and older servers do not support 
it. So a CAST to `DECIMAL(p, 0)` with 19 <= p <= 38 from a non-integer type now 
converts through LONG first, as on master. It drops the fraction (truncation, 
like the LONG cast) and returns a BIG_DECIMAL with scale 0. Integer inputs 
still cast directly, so their values beyond the LONG range stay exact. For 
non-integer inputs, the old LONG range limit stays: a DOUBLE saturates and a 
BIG_DECIMAL wraps, as on master. The planning-time fold in 
`PinotEvaluateLiteralRule` does the same.
   
   Tests cover DOUBLE and BIG_DECIMAL inputs in the leaf stage, in an 
intermediate stage, and in a call that the planner evaluates.
   



-- 
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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to