Skip to content

implement:implements full support for DECIMAL(M, D) with M up to 65 #23916

Open
robll-v1 wants to merge 6 commits intomatrixorigin:mainfrom
robll-v1:issue/23910
Open

implement:implements full support for DECIMAL(M, D) with M up to 65 #23916
robll-v1 wants to merge 6 commits intomatrixorigin:mainfrom
robll-v1:issue/23910

Conversation

@robll-v1
Copy link
Collaborator

What type of PR is this?

  • API-change
  • BUG
  • Improvement
  • Documentation
  • Feature
  • Test and CI
  • Code Refactoring

Which issue(s) this PR fixes:

issue #23910

What this PR does / why we need it:

This PR implements full support for DECIMAL(M, D) with M up to 65 (backed by the decimal256 type), bringing it from a skeletal type stub to a production-usable numeric type.

What was added across two commits

Commit 1 — d9a6e16: Basic query path

  • Parser now accepts DECIMAL(M) up to M=65 and routes M>38 to T_decimal256
  • Decimal256 parse/format, float/string/int cast, encode/decode, compare, and vector infrastructure wired up
  • CAST('...' AS DECIMAL(65,30)), DDL, INSERT, SELECT, and frontend output working
  • BVT case added: test/distributed/cases/dtype/decimal_256.test

Commit 2 — fa7995f: Complete operator and function support

  • Arithmetic: +, -, *, /, %, div — scale-aware Add/Sub/Mul/Div/Mod methods added to Decimal256, wired into arithmetic.go and baseTemplate.go
  • Aggregation: SUM, AVG via new sumAvgDec256Exec exec path; MIN, MAX via newDecimal256MinMaxExec; return types updated in sumavg2.go
  • Math functions: ABS, SIGN, CEIL, FLOOR, ROUND, TRUNCATE — implementations in func_unary.go / func_binary.go, registered in list_builtIn.go
  • Predicates: =, !=, <, >, <=, >=, <=>, BETWEEN, IN, NOT IN — valueDec256Compare helper added, all comparison function bodies extended
  • Conditional expressions: CASE WHEN, COALESCE, IFF wired in operatorSet.go and list_operator.go
  • Sorting: ORDER BY supported via pkg/sort/sort.go
  • Type coercion: implicit cast rules in type_check.go extended with full T_decimal256 cross-type entries; list_operator.go retType functions updated for all arithmetic operators

Testing

  • Unit tests added/extended: decimal256_support_test.go, sumavg2_test.go, minmax2_test.go, decimal_test.go
  • BVT: decimal_256.test — 10/10 pass via mo-tester
  • go test passes for: pkg/container/types, pkg/sql/plan/function, pkg/sql/colexec/aggexec, pkg/sort
  • make build clean

Known Risks and Limitations

  1. ZoneMap (ZM) pruning is disabled for decimal256 columns

In pkg/vm/engine/tae/index/zm.go, the UpdateZM and BatchUpdateZM paths skip T_decimal256 entirely. The raw Decimal256 value is 32 bytes, which does not fit the current fixed-layout zonemap encoding. This means:

  • Correctness is not affected — queries return correct results
  • Performance is degraded on large tables: WHERE decimal256_col > x cannot use min/max block pruning and will fall back to full scan
  • This is intentional to avoid a flush-time panic; a proper ZM fix requires a layout change and is deferred
  1. No window function support

RANK, ROW_NUMBER, LAG, LEAD, FIRST_VALUE, LAST_VALUE etc. have not been extended for T_decimal256. Using decimal256 columns in window functions will return an unsupported-type error.

  1. No bitwise / BIT_AND / BIT_OR / BIT_XOR / OCT support

These are edge-case operations on decimal types in general and are not implemented for decimal256.

  1. No LEAST / GREATEST / SERIAL / SERIAL_EXTRACT support

These convenience functions are not wired for T_decimal256 and will return unsupported errors if called with decimal256 arguments.

  1. AVG precision cap

AVG return type for decimal256 is capped at scale = min(input_scale + 6, 30) consistent with the existing decimal128 policy. This is a known approximation, not a regression.

robll-v1 and others added 2 commits March 20, 2026 14:27
Extend the DECIMAL(65,30)/decimal256 type to cover all core numeric
operators and functions:

- Arithmetic: +, -, *, /, %, div with scale-aware Decimal256.Add/Sub/Mul/Div/Mod methods
- Aggregation: SUM, AVG, MIN, MAX with dedicated decimal256 exec paths
- Math functions: ABS, SIGN, CEIL, FLOOR, ROUND, TRUNCATE
- Predicates: =, !=, <, >, <=, >=, <=>, BETWEEN, IN, NOT IN
- Conditional: CASE WHEN, COALESCE, IFF
- Sorting: ORDER BY via pkg/sort
- Implicit type coercion rules updated in type_check.go
- Return type inference for all arithmetic operators updated in list_operator.go

All affected unit tests pass. BVT cases updated and verified with mo-tester (10/10).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR upgrades DECIMAL support to fully handle DECIMAL(M,D) up to M=65 by introducing/activating decimal256 across parsing, planning, execution, vector ops, functions/operators, aggregation, sorting, and frontend formatting/export paths (issue #23910).

Changes:

  • Extend MySQL parser + planner/binder type selection to route DECIMAL(39..65, D) to T_decimal256.
  • Add decimal256 support across casts, comparisons, arithmetic/operators, math functions, SUM/AVG/MIN/MAX, sorting, and vector encode/decode/minmax.
  • Add/update unit tests and distributed test expectations for the new type and behaviors.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/distributed/cases/operator/is_operator.result Updates expected output (including new result formatting) and removes the previous parser error for DECIMAL(65,30).
test/distributed/cases/function/func_datetime_unixtime.result Adjusts expected behavior of FROM_UNIXTIME(huge_literal) to return null instead of an error.
test/distributed/cases/dtype/numeric.sql Updates “out of range” numeric tests to exceed new max precision (66/67).
test/distributed/cases/dtype/numeric.result Updates expected parser error messages to reflect new 0..65 range.
test/distributed/cases/dtype/decimal_256.test Adds distributed BVT coverage for decimal(65,30) DDL/DML/functions/aggregations.
test/distributed/cases/dtype/decimal_256.result Expected results for the new distributed decimal256 test.
test/distributed/cases/dtype/decimal.test Updates “out of range” decimal tests to exceed new max precision (66/67).
test/distributed/cases/dtype/decimal.result Updates expected parser error messages to reflect new 0..65 range.
pkg/vm/engine/tae/index/zm.go Skips zonemap maintenance for decimal256 to avoid layout/flush issues.
pkg/sql/plan/make.go Enables constant decimal expression typing to fall back to decimal256; adds default rewrite for empty decimal256 type.
pkg/sql/plan/function/type_check.go Extends type coercion / fixed cast rules to include decimal256 and scale propagation rules.
pkg/sql/plan/function/operator_in.go Adds decimal256 to generic IN/NOT IN operator type set.
pkg/sql/plan/function/operator_between.go Adds BETWEEN / in-range support using CompareDecimal256.
pkg/sql/plan/function/operatorSet.go Wires decimal256 into CASE/IFF/COALESCE and supported type sets; adds unary minus impl.
pkg/sql/plan/function/list_operator.go Registers operator overloads/return-type rules for decimal256 (IN/NOT IN, arithmetic, unary, coalesce).
pkg/sql/plan/function/list_builtIn.go Registers math built-ins and FROM_UNIXTIME overloads for decimal256.
pkg/sql/plan/function/list_agg.go Enables decimal256 for SUM and MIN/MAX supported types.
pkg/sql/plan/function/func_unary.go Implements ABS and SIGN for decimal256.
pkg/sql/plan/function/func_testcase.go Extends function test harness to compare/build vectors for decimal256.
pkg/sql/plan/function/func_compare.go Adds compare operator support and valueDec256Compare to handle scale alignment.
pkg/sql/plan/function/func_cast_test.go Adds unit tests for casting to decimal256 from string and int64.
pkg/sql/plan/function/func_cast.go Adds decimal256 casting support (to/from numeric/string/binary-ish types) and routing in NewCast.
pkg/sql/plan/function/func_binary_test.go Adds FROM_UNIXTIME(decimal256) unit test.
pkg/sql/plan/function/func_binary.go Adds decimal256 math funcs (ceil/floor/round/truncate) and FROM_UNIXTIME(decimal256) implementations.
pkg/sql/plan/function/decimal256_support_test.go Adds broad unit coverage for decimal256 operators, coercion, arithmetic, conditional ops, math, etc.
pkg/sql/plan/function/baseTemplate.go Extends decimal arithmetic template to include decimal256 and zero-checks.
pkg/sql/plan/function/arithmetic.go Adds decimal256 operator support (plus/minus/mul/div/mod/div) and result-type driven handling.
pkg/sql/plan/build_util.go Maps AST decimal types with precision > 38 to T_decimal256.
pkg/sql/plan/base_binder.go Updates numeric literal binding to fall back to decimal256 parsing when decimal128 parsing fails; generalizes decimal check for floats.
pkg/sql/parsers/dialect/mysql/mysql_sql_test.go Adds parser test for decimal(65,30).
pkg/sql/parsers/dialect/mysql/mysql_sql.y Extends grammar to accept decimal precision up to 65 and to build appropriate internal type.
pkg/sql/parsers/dialect/mysql/mysql_sql.go Regenerated parser output matching mysql_sql.y changes.
pkg/sql/colexec/aggexec/sumavg2_test.go Extends SUM/AVG tests to include decimal256 vectors and validation.
pkg/sql/colexec/aggexec/sumavg2.go Adds SUM/AVG execution support for decimal256 (including return type rules).
pkg/sql/colexec/aggexec/minmax2_test.go Adds unit tests for MIN/MAX over decimal256.
pkg/sql/colexec/aggexec/minmax2.go Adds MIN/MAX exec path for decimal256.
pkg/sort/sort_test.go Adds unit test for sorting decimal256.
pkg/sort/sort.go Adds decimal256 sort support.
pkg/frontend/util.go Enables scalar expr value extraction for decimal256.
pkg/frontend/output.go Adds decimal256 formatting for result output and column slice conversion.
pkg/frontend/mysql_cmd_executor.go Maps engine decimal256 to MySQL DECIMAL type for column metadata.
pkg/frontend/export_parquet.go Adds decimal256 conversion for Parquet export values.
pkg/frontend/export.go Adds decimal256 formatting for export and JSON conversion.
pkg/container/vector/vector.go Adds core vector operations for decimal256 (GetAny/Shrink/Shuffle/UnionAll/const set/String/minmax/sort/compact).
pkg/container/vector/tools.go Adds append-bytes function support for decimal256.
pkg/container/vector/functionTools.go Adds function result wrapper support for decimal256.
pkg/container/types/types.go Extends DecimalWithFormat to include Decimal256.
pkg/container/types/encoding.go Adds encode/decode and size constants for decimal256 values.
pkg/container/types/decimal_test.go Adds parse/format test for decimal256 and negative-scale float conversion test.
pkg/container/types/decimal.go Implements substantial decimal256 parsing/formatting, arithmetic helpers, trunc-scaling, float conversions, etc.
pkg/container/types/compare.go Adds decimal256 asc/desc compare helpers.
pkg/compare/compare.go Adds comparator construction + copy function for decimal256.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@robll-v1
Copy link
Collaborator Author

I fixed the clear issue-level gaps in this round:

  1. decimal256 cast support
    I added the missing decimal256 source casts for bit/int/uint, so decimal256 is no longer missing the basic
    integral cast paths that other decimal types already expose.

  2. silent scale-overflow handling in decimal256 comparison
    I changed the decimal256 compare path to return the scale overflow error explicitly instead of ignoring the
    Scale() error and continuing with a potentially wrong comparison result.

I also added focused tests for both changes.

ZoneMap bypass for decimal256 is a real performance limitation, but I did not address it in this patch. The
current zonemap raw layout cannot safely store 32-byte decimal256 values, so fixing it cleanly needs a
storage/index layout change rather than a small function-level patch.

The cast-gap report is only partially applicable now. I fixed the missing decimal256 integral cast paths
(bit/int/uint). I did not extend decimal256 to date/datetime/time/timestamp/year in this patch, because
that would widen the temporal cast surface beyond the scoped fix and is better handled as a follow-up
behavior discussion.

Decimal256 Format() using big.Int is a valid performance concern. I agree it is worth optimizing, but I
treated it as a follow-up perf improvement rather than a correctness blocker for issue 23910.

I do not see evidence that Decimal128.Mod128 behavior changed in this branch. The branch adds decimal256-
specific helpers, but Decimal128.Mod128 itself was not changed here.

FROM_UNIXTIME behavior did change for very large numeric literals after decimal256 widened the literal
typing path. I left that behavior as-is for now because it is a compatibility discussion, not a clear
correctness bug in the decimal256 execution path itself.

There is still duplicated decimal256 code across the decimal64/128/256 paths. That is real technical debt,
but I kept this patch scoped to correctness/compatibility fixes instead of refactoring the whole decimal
family.

Some result-file churn in this branch is broader than the core decimal256 behavior change. I agree that is
test hygiene noise, but it does not affect runtime correctness.

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

Labels

kind/feature size/XXL Denotes a PR that changes 2000+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants