[expression] Mixed signed/unsigned IN constants can make NOT IN delete protected rows
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Bug Report
### 1. Minimal reproduce step (Required)
**Environment**
TiDB ed2376acc6/master 05b396fb66; strict default; MDL ON; one TiDB/PD/TiKV; no injection, concurrency, retry, or fault
**Steps**
Create a BIGINT UNSIGNED table with ordinary IDs and two named 18446744073709551615 rows. Compare pairwise equality, IN lists in both orders, and EXPLAIN. Run matched DELETE statements using NOT IN, pairwise NOT(a=b OR a=c), and the reversed IN order. Verify affected rows, fresh named preimages, and ADMIN CHECK TABLE.
### 2. What did you expect to see? (Required)
IN agrees with pairwise OR and is invariant to constant order. Both reference DELETE statements remove only IDs 0 and 1 and preserve the two MaxUint64 preimages.
### 3. What did you see instead? (Required)
IN(-1,MaxUint64) returns false for an unsigned MaxUint64 probe while the reversed list returns true. EXPLAIN contains only -1. NOT IN deletes all four rows; the two references delete two and preserve both MaxUint64 rows.
### 4. What is your TiDB version? (Required)
TiDB ed2376acc6/master 05b396fb66; strict default; MDL ON; one TiDB/PD/TiKV; no injection, concurrency, retry, or fault
Likely root cause and fix direction
**Likely root cause**
builtinInIntSig stores constants in map[int64]bool and deduplicates solely by the int64 key. Signed -1 and unsigned MaxUint64 share key -1 but are not SQL-equal. The first signedness overwrites the semantic domain, and b.args truncation removes the second constant from serialized expressions.
**Fix direction**
Preserve all equality-relevant domain tags. Store a signed/unsigned bit mask per int64 representation, deduplicate by bits plus signedness, retain both arguments, and apply the same match rule in scalar, vectorized, and pushdown paths.
Contributor guide
Research direction
Start in the expression implementation around builtinInIntSig and trace how constants are represented in b.args. Reproduce the mixed signed/unsigned IN and NOT IN cases, then compare scalar, vectorized, and pushdown paths. Done means both constant orders agree, NOT IN preserves the MaxUint64 rows, and the reference checks still pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100