cockroachdb / cockroachdb/cockroach

sql: sum()/avg() over INT2/INT4 returns DECIMAL instead of BIGINT, diverging from Postgres

Open
#173,284 1 comment 0 reactions 0 assignees View on GitHub
A-sql-builtins A-sql-pgcompat A-sql-typing C-bug O-agent T-sql-queries
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Describe the problem**

In PostgreSQL, the return type of `sum` depends on the input integer width:

- `sum(smallint)` / `sum(int2)` → `bigint`
- `sum(integer)` / `sum(int4)` → `bigint`
- `sum(bigint)` / `sum(int8)` → `numeric`

CockroachDB instead returns `numeric` (aka `DECIMAL`) for `sum` over *every* integer width. This is correct for `int8` inputs (Postgres also promotes those to `numeric` to avoid int64 overflow), but diverges for `int2`/`int4`, which Postgres promotes only as far as `bigint`.

The root cause is that CockroachDB defines a single `sum(int) → decimal` overload rather than Postgres's three width-specific `sum` aggregates. Narrower widths widen into the single `int` overload:

https://github.com/cockroachdb/cockroach/blob/master/pkg/sql/sem/builtins/aggregate_builtins.go#L417-L419

```go
"sum": makeBuiltin(tree.FunctionProperties{},
makeImmutableAggOverload([]*types.T{types.Int}, types.Decimal, newIntSumAggregate,
"Calculates the sum of the selected values."),
...
```

**To Reproduce**

```sql
-- Postgres returns bigint for both; CRDB returns numeric for both.
SELECT pg_typeof(sum(x::INT2)) FROM generate_series(1, 10) g(x); -- numeric
SELECT pg_typeof(sum(x::INT4)) FROM generate_series(1, 10) g(x); -- numeric

-- This case IS compatible (Postgres also returns numeric):
SELECT pg_typeof(sum(x::INT8)) FROM generate_series(1, 10) g(x); -- numeric
```

**Expected behavior**

To match Postgres:

| input | Postgres | CRDB today | expected |
|---|---|---|---|
| `sum(int2)` | `bigint` | `numeric` | `bigint` |
| `sum(int4)` | `bigint` | `numeric` | `bigint` |
| `sum(int8)` | `numeric` | `numeric` | `numeric` (unchanged) |

Summing `int2`/`int4` into `int8` cannot overflow int64 for any realistic row count, so the overflow-avoidance rationale that justifies `sum(int8) → numeric` does not apply to the narrower widths. The likely fix is width-specific `sum` overloads (`int2`/`int4 → int8`), while keeping `int8 → decimal`.

`avg` almost certainly has the analogous divergence (Postgres: `avg(int2/int4/int8) → numeric`, so `avg` may be a separate question) — worth auditing the other int-input aggregates at the same time.

**Additional context**

- Related to #26925 (general integer-width Postgres compatibility, epic CRDB-60813); this issue is the concrete aggregate-return-type instance of that broader gap.
- Prior discussion: #7414 established the "return decimal to avoid overflow" decision for `sum(int)`; #12701 (closed/stale) considered overloading `sum` to return int. #38845 ("some aggregations should promote the return type") was closed by #49900, but that only taught the *vectorized* engine to handle the existing decimal return type — it did not change the user-visible return type.
- Impact: pg-compatibility for ORMs, drivers, and applications that inspect result column types; a column typed `bigint` in Postgres arrives as `numeric` in CRDB.

Epic CRDB-60813

Jira issue: CRDB-66606

Contributor guide

Open the contributing guide

Research direction

Start in pkg/sql/sem/builtins/aggregate_builtins.go at the sum overload around the linked lines, then run the SQL reproduction queries for INT2, INT4, and INT8. Done means sum over INT2 and INT4 reports bigint while INT8 remains numeric; audit the related integer-input aggregate behavior as the issue suggests.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.