pingcap / pingcap/tidb

TiDB changes numeric values when a heterogeneous UNION crosses a relational boundary

Open
#70,636 3 comments 0 reactions 0 assignees View on GitHub
affects-25.10 affects-26.3 affects-7.5 affects-8.1 affects-8.5 contribution severity/critical sig/execution type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

TiDB returns different numeric values for a heterogeneous `UNION` depending
only on whether the `UNION` is executed directly or selected through a derived
table/CTE. In the flat form, a `DOUBLE` expression is rounded according to a
`FLOAT(M,D)` branch. Selecting the same `UNION` through a relational boundary
returns the full `DOUBLE` value instead.

The result metadata reported through the MySQL protocol is identical in both
forms, so this is a value-level wrong result rather than a client-side metadata
display difference.

This was independently exposed by VECT in `logic_bug.txt` as round 5 pair 286
and round 7 pair 83. Both candidates change transcendental-function results
such as `LN()` from a scale-limited value to a full-precision value when the
same `UNION` is placed behind a CTE or derived table.

### 1. Minimal reproduce step (Required)

Create a table whose type participates in `UNION` type resolution. The table
is deliberately empty in the first reproducer, so the only returned value is
`LN(45)`:

```sql
DROP DATABASE IF EXISTS tidb_union_numeric_repro;
CREATE DATABASE tidb_union_numeric_repro;
USE tidb_union_numeric_repro;

CREATE TABLE f(v FLOAT(8,2));
```

Run the flat form:

```sql
SELECT 1 AS v WHERE FALSE
UNION
SELECT LN(45)
UNION ALL
SELECT v FROM f WHERE FALSE;
```

Observed result:

```text
3.81
```

Now select the identical `UNION` through a derived table:

```sql
SELECT v
FROM (
SELECT 1 AS v WHERE FALSE
UNION
SELECT LN(45)
UNION ALL
SELECT v FROM f WHERE FALSE
) AS d;
```

Observed result:

```text
3.8066624897703196
```

The CTE form returns the same full-precision value as the derived-table form:

```sql
WITH c AS (
SELECT 1 AS v WHERE FALSE
UNION
SELECT LN(45)
UNION ALL
SELECT v FROM f WHERE FALSE
)
SELECT v FROM c;
```

Observed result:

```text
3.8066624897703196
```

In all three cases, the MySQL protocol metadata observed with PyMySQL was:

```text
type_code=5 (DOUBLE), internal_size=26, precision=26, scale=2
```

The discrepancy also occurs when the `FLOAT(8,2)` branch returns a row:

```sql
INSERT INTO f VALUES (12.34);

SELECT 1 AS v WHERE FALSE
UNION
SELECT LN(45)
UNION ALL
SELECT v FROM f;
```

Flat result:

```text
3.81
12.34
```

Derived-table result:

```sql
SELECT v
FROM (
SELECT 1 AS v WHERE FALSE
UNION
SELECT LN(45)
UNION ALL
SELECT v FROM f
) AS d;
```

```text
3.8066624897703196
12.34000015258789
```

### 2. What did you expect to see? (Required)
Adding a derived-table or CTE boundary around a query must not change its row
values. The flat, derived-table, and CTE forms should apply one consistent
numeric type-resolution and conversion policy and return the same values.

### 3. What did you see instead (Required)

### 4. What is your TiDB version? (Required)

```text
Release Version: v8.5.7
Edition: Community
Git Commit Hash: 202b7f47286a1109b5c957401d34c9358d130ae0
Git Branch: HEAD
UTC Build Time: 2026-07-15 02:06:00
GoVersion: go1.25.10
Race Enabled: false
Check Table Before Drop: false
Store: unistore
```

Contributor guide

Open the contributing guide

Research direction

The report does not identify a source file or test entry point. Start by running the supplied SQL reproducer on TiDB v8.5.7 and compare the flat, derived-table, and CTE forms; done means all forms return consistent numeric values while preserving the reported metadata behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.