ALTER TABLE ADD COLUMN silently drops an inline column-level CHECK constraint
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Bug Report
### 1. Minimal reproduce step (Required)
`ALTER TABLE ... ADD COLUMN` with an inline column-level `CHECK` constraint succeeds and emits no warning, but the `CHECK` constraint is never published or enforced. A later row that should be rejected is silently accepted.
```sql
-- CHECK constraints must be enabled; a session reads this at connect time, so reconnect after:
SET GLOBAL tidb_enable_check_constraint = 1;
CREATE TABLE t(a INT);
INSERT INTO t VALUES (1),(2);
ALTER TABLE t ADD COLUMN b INT DEFAULT 1 CHECK (b > 0); -- succeeds, @@warning_count = 0
SHOW CREATE TABLE t; -- no CONSTRAINT ... CHECK on b
SELECT COUNT(*) FROM information_schema.check_constraints
WHERE constraint_schema = DATABASE(); -- 0
INSERT INTO t(a,b) VALUES (3,0); -- accepted, though b > 0 should reject it
SELECT a,b FROM t; -- (1,1),(2,1),(3,0)
```
A **named** inline constraint (`ADD COLUMN b INT DEFAULT 1 CONSTRAINT ck CHECK (b > 0)`) behaves identically: no constraint published, `b = 0` accepted.
### 2. What did you expect to see? (Required)
The inline `CHECK` should be enforced, exactly as it is on the two sibling paths:
```sql
-- direct CREATE TABLE publishes and enforces the same inline CHECK:
CREATE TABLE d(a INT, b INT DEFAULT 1 CHECK (b > 0));
INSERT INTO d(a,b) VALUES (1,0); -- ERROR 3819: Check constraint 'd_chk_1' is violated.
-- sequential ADD COLUMN + ADD CONSTRAINT also enforces it:
CREATE TABLE s(a INT);
ALTER TABLE s ADD COLUMN b INT DEFAULT 1;
ALTER TABLE s ADD CONSTRAINT ck CHECK (b > 0);
INSERT INTO s(a,b) VALUES (1,0); -- ERROR 3819: Check constraint 'ck' is violated.
```
So the expected outcome is either the constraint is published and enforced, or the statement is rejected — not silently dropped.
### 3. What did you see instead (Required)
`ALTER TABLE ... ADD COLUMN ... CHECK (...)` reports success with `@@warning_count = 0`, `SHOW CREATE TABLE` shows no `CHECK` on the new column, `information_schema.check_constraints` has no row, and a subsequent `INSERT` that violates the requested predicate is accepted. The user asked for an enforced integrity constraint and got a silently unconstrained column.
### 4. What is your TiDB version? (Required)
```
Release Version: v9.0.0-beta.2.pre-1774-g81ec977cb8
Git Commit Hash: 81ec977cb8bf97e0c9805dfc0be8ffcbd4b0bbeb
UTC Build Time: 2026-05-28 03:35:30
Edition: Community
Store: tikv
```
Likely root cause — ADD COLUMN discards the column-level constraint it extracts
In `pkg/ddl/add_column.go`, `CreateNewColumn` calls `buildColumnAndConstraint(...)` as `col, _, err := ...`, discarding the second return value — the column-level constraints extracted from `ColumnOptionCheck`. `AddColumn` then submits only an `ActionAddColumn` job with `TableColumnArgs`, so the extracted `CHECK` never reaches an owner. By contrast, `CREATE TABLE` consumes `ast.ConstraintCheck` into the table metadata, and table-level `ALTER TABLE ADD CHECK` is a separate `ActionAddCheckConstraint` path that validates existing rows before publishing. The fix is to carry the column-level `CHECK` returned by `buildColumnAndConstraint` into an `ActionAddCheckConstraint` sub-job after the new column is public (including existing-row validation), or to reject inline column `CHECK` in `ADD COLUMN` explicitly instead of dropping it.
Contributor guide
Research direction
Start in pkg/ddl/add_column.go at CreateNewColumn and trace buildColumnAndConstraint through AddColumn, including the discarded constraint result and the ActionAddColumn/TableColumnArgs path. Compare this with the ActionAddCheckConstraint path used by ALTER TABLE ADD CHECK. Done means the inline CHECK is either published and enforced with existing-row validation, or the ADD COLUMN statement rejects it instead of silently dropping it.
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
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100