Skip to content

fix(isthmus)!: derive SUM, AVG and decimal arithmetic types as the extensions declare - #1347

Open
alexandrefimov wants to merge 3 commits into
substrait-io:mainfrom
alexandrefimov:issue-1117-decimal-aggregate-types
Open

alexandrefimov wants to merge 3 commits into
substrait-io:mainfrom
alexandrefimov:issue-1117-decimal-aggregate-types

Conversation

@alexandrefimov

@alexandrefimov alexandrefimov commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Isthmus typed SUM and AVG as their argument, SUM0 as BIGINT, and used Calcite's decimal rules, so SUM over decimal(7,2) came out decimal(7,2) instead of the declared decimal(38,2), and SUM over i32 stayed i32 instead of i64.

SubstraitTypeSystem now derives SUM, AVG and decimal + - * / % as the extensions declare them, and isthmus's aggregates take their types from it. Nothing is cast back.

Part of #1117. Decimal arithmetic with an integer operand still differs; that goes with the operand casts, as agreed there.

BREAKING CHANGE: isthmus types SUM, SUM0, AVG and decimal + - * / % as the extensions declare them: an integer SUM or SUM0 is BIGINT, a floating-point one DOUBLE, and a decimal SUM, SUM0 or AVG is DECIMAL(38, s); STDDEV_* and VAR_* over a decimal become DECIMAL(38, s) as well. A decimal result above precision 38 now gives up scale, so DECIMAL(38,20) * DECIMAL(38,20) is DECIMAL(38,6) rather than DECIMAL(38,38). Cast the results where code relies on the previous types.

…tensions declare

Isthmus typed SUM and AVG as their argument and SUM0 as BIGINT, and left
decimal arithmetic to Calcite's defaults, so SUM over decimal(7,2) was
typed decimal(7,2) where sum:dec declares decimal(38,2), and SUM over i32
was typed i32 where the declaration is i64.

SubstraitTypeSystem now derives the sum, the average and decimal +, -, *
and / as the extensions declare them, including the scale a result above
precision 38 gives up, and isthmus's SUM, AVG and SUM0 take their types
from it. Nothing is cast back: the wider type is what Calcite derives.
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository: substrait-io/substrait-java/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 6839dbc6-164b-4244-b448-d42fba2dc6f8

📥 Commits

Reviewing files that changed from the base of the PR and between 0b63541 and c1c7363.

📒 Files selected for processing (1)
  • isthmus/src/test/java/io/substrait/isthmus/SubstraitTypeSystemTest.java
 ______________________________________________________________
< Ad Astra Per Codicem Fixis. To the stars through code fixes. >
 --------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: substrait-io/substrait-java/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 212c5f5e-caeb-456e-9044-29bb1fb40664

📥 Commits

Reviewing files that changed from the base of the PR and between 10deea6 and 0b63541.

📒 Files selected for processing (5)
  • isthmus/src/main/java/io/substrait/isthmus/AggregateFunctions.java
  • isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java
  • isthmus/src/test/java/io/substrait/isthmus/OutputNamesTest.java
  • isthmus/src/test/java/io/substrait/isthmus/SubstraitRelNodeConverterTest.java
  • isthmus/src/test/java/io/substrait/isthmus/SubstraitTypeSystemTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • isthmus/src/test/java/io/substrait/isthmus/OutputNamesTest.java

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.


📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Corrected inferred result types for SUM and AVG across integer, floating-point, and decimal inputs, including nullable results where applicable.
    • Decimal addition, subtraction, multiplication, division, and modulus now follow Substrait precision and scale rules, with a maximum precision of 38.
    • SUM results are nullable except for the empty-is-zero variant, which remains non-nullable.
    • Aggregate conversion now reports inferred output types consistently, including for decimal aggregates.

Walkthrough

This change adds Substrait-specific result-type derivation for aggregates and decimal arithmetic. SUM and AVG use derived types. Tests compare these types with extension declarations and converter expectations.

Changes

Type Inference

Layer / File(s) Summary
Aggregate and decimal type derivation
isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java, isthmus/src/test/java/io/substrait/isthmus/SubstraitTypeSystemTest.java
SUM derives BIGINT for integer inputs and DOUBLE for floating-point inputs. Decimal SUM and AVG derive precision-38 DECIMAL results. Decimal arithmetic derives results using operation-specific precision and scale rules, capped at 38. Tests compare derived types with extension declarations.
Aggregate function integration and inference expectations
isthmus/src/main/java/io/substrait/isthmus/AggregateFunctions.java, isthmus/src/test/java/io/substrait/isthmus/SubstraitRelNodeConverterTest.java, isthmus/src/test/java/io/substrait/isthmus/OutputNamesTest.java
SUM and AVG use types derived by SubstraitTypeSystem and force nullable results. SUM0 no longer overrides Calcite return-type inference. Converter and output-name tests update aggregate type expectations.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Suggested reviewers: nielspardon

Merge Risk: ⚪ Minimal · up to 0b635

The numeric result-type changes appear ready to merge after normal checks. They intentionally widen aggregate outputs and change decimal arithmetic types, so consumers relying on previous types must account for that breaking change.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 0b635

This intentionally changes result widths and decimal scales, so callers relying on previous schemas may need explicit casts. The reviewed paths retain aggregate binding and validation behavior; no introduced security issue was established. Application-level exposure remains unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The established impact concerns numeric schemas used by applications consuming Isthmus conversions. The evidence does not establish a deployed endpoint, tenant scope, credential authority, or independently attackable production environment.

Trust Boundaries and Controls

  • observed — Declared plan output preservation and extension-declaration validation are independent policies. The default preserves declared output without asserting specification compliance; strict validation is opt-in. This policy predates the numeric inference change in the inspected comparisons and is not an observed new validation bypass.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise, specific, and clearly describes the primary change to SUM, AVG, and decimal arithmetic type derivation. It also follows the required Conventional Commit format with a breaking-ch…
Description check ✅ Passed The description explains the previous behavior, the implemented changes, the deferred integer-operand limitation, the related issue, and the breaking-change impact with concrete examples.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nielspardon

Copy link
Copy Markdown
Member

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

@nielspardon nielspardon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deriving these in SubstraitTypeSystem is the right shape, and the dec×dec formulas match the extension. The BIGINT operand regression inline is the one to fix first; the rest are smaller.

Please also rework the BREAKING CHANGE footer: not every column widens (a result above precision 38 loses scale), STDDEV/VAR over decimals change too, and the footer should say what consumers do. Something like:

BREAKING CHANGE: isthmus types SUM, SUM0, AVG and decimal + - * / as the extensions declare them: an integer SUM or SUM0 is BIGINT, a floating-point one DOUBLE, and a decimal SUM, SUM0 or AVG is DECIMAL(38, s); STDDEV_* and VAR_* over a decimal become DECIMAL(38, s) as well. A decimal result above precision 38 now gives up scale, so DECIMAL(38,20) * DECIMAL(38,20) is DECIMAL(38,6) rather than DECIMAL(38,38). Cast the results where code relies on the previous types.

SUM, AVG and SUM0 now follow the type system of a custom typeFactory(...), so the validator/cluster split in #1220 fails with type mismatch on the SQL path for them. Nothing to change here, but it makes #1220 more pressing.

Comment thread isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java Outdated
Comment thread isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java Outdated
Comment thread isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java
Comment thread isthmus/src/main/java/io/substrait/isthmus/SubstraitTypeSystem.java
Comment thread isthmus/src/main/java/io/substrait/isthmus/AggregateFunctions.java Outdated
Comment thread isthmus/src/main/java/io/substrait/isthmus/AggregateFunctions.java Outdated
Comment thread isthmus/src/test/java/io/substrait/isthmus/OutputNamesTest.java Outdated
@nielspardon

Copy link
Copy Markdown
Member

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
✅ Action performed

Reviews resumed and review finished.

… modulus

Under this type system decimalOf(BIGINT) is DECIMAL(38,0), so a BIGINT
operand made d + b a decimal<38,2> where add:dec_dec declares
decimal<22,2>. Only a Java type goes through decimalOf now. Decimal
modulus follows modulus:dec_dec too, a decimal SUM keeps Calcite's own
DECIMAL(38, s), and SUM0 relies on its superclass, which already infers
the sum's type made NOT NULL.
Comment on lines +69 to +70
* An integer operand counts as the decimal that holds its type, the one isthmus casts it to:
* {@code decimal(10,0)} for an INTEGER and {@code decimal(19,0)} for a BIGINT, whatever the type

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop "the one isthmus casts it to": SELECT d * b casts b to decimal(21,2), not decimal(19,0), and emits decimal<27,2> where multiply:dec_dec declares decimal<29,4> for those arguments. That's the integer-operand gap the description already defers, so only the wording needs to change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants