matrixorigin / matrixorigin/matrixone

[Bug]: FLOAT and DOUBLE secondary-index equality omits SQL-equal signed zero

Open
#28,695 1 comment 0 reactions 1 assignee Claimed by @XuPeng-SH View on GitHub
kind/bug needs-triage
Dominant language
Go
Stars
1.9k
Forks
311
Avg merge
1d 3h
Merged PRs (30d)
768

Description

## 问题

FLOAT/DOUBLE 二级索引把 `-0.0` 与 `+0.0` 编码为不同的索引键,但 SQL 比较、分组和唯一键均把它们视为相等。因此等值谓词使用二级索引时漏行:结果随访问路径变化。

## 环境

* MatrixOne `main`:`7013fbbd6e152ba31da2d3dbba7012ac7f8b8462`,新启动的 2 CN 环境。
* 对照:MySQL 8.3。

## 复现

```sql
create table d(id int primary key, v double, key idx_v(v));
insert into d values
(1, cast('-0.0' as double)),
(2, cast('0.0' as double)),
(3, 1.0);

select group_concat(id order by id) from d
where v = cast('-0.0' as double);
-- MO: 1

select group_concat(id order by id) from d
where v = cast('0.0' as double);
-- MO: 2

select group_concat(id order by id) from d ignore index(idx_v)
where v = cast('-0.0' as double);
-- MO: 1,2

explain select id from d where v = cast('-0.0' as double);
-- Index Table Scan on d.idx_v
```

同样的 FLOAT 表得到相同行为。

MO 中 `-0.0 = +0.0` 和 `-0.0 <=> +0.0` 都为 true;`GROUP BY v` 合并为一组,且向 DOUBLE UNIQUE KEY 插入 `-0.0` 后插入 `+0.0` 会正确报重复键。因此索引谓词的结果与同一实例的 SQL 相等语义不一致。

在 MySQL 8.3,DOUBLE/FLOAT 的索引访问和 `IGNORE INDEX` 扫描对正、负零谓词均返回 `1,2`。

## 稳定性与范围

* DOUBLE 使用三个独立表重复三轮,每轮 `-0.0` 索引谓词返回 `1`、`+0.0` 返回 `2`,对应扫描均为 `1,2`。
* FLOAT 使用相同数据与二级索引,索引和扫描差分相同。
* SQL `PREPARE/EXECUTE` 复用这一索引路径时也只返回一行;无索引表返回两行。

## 代码线索

`pkg/container/types/compare.go` 已将 signed zero 定义为 SQL ORDER BY peer;`pkg/common/hashmap/keycodec/keycodec.go` 的 `CanonicalFloat32Bits` / `CanonicalFloat64Bits` 也会将两种零归一化。相对地,二级索引的物理元组键保留浮点原始位表示(`Float32TupleAscCompare` / `Float64TupleAscCompare` 的注释明确说明会区分 signed zero)。当前 `prefix_eq(__mo_index_idx_col)` 没有将 SQL 的零等值谓词扩展到两个物理键,导致漏行。

## 预期

二级索引只能作为访问路径,不能改变 SQL 等值语义。FLOAT/DOUBLE 对 `-0.0` 或 `+0.0` 的 `=`, `<=>`, `IN` 等谓词走索引时应覆盖两种物理零表示,或索引键应在写入/查找时按 SQL 等值规则归一化。

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.