What did you do?
Replicate an ALTER TABLE carrying an ALGORITHM qualifier from MySQL/Aurora to TiDB.
Upstream:
ALTER TABLE db.orders ADD COLUMN c INT NULL, ALGORITHM=INSTANT;
What did you expect to see?
DM applies one statement downstream, preserving the qualifier:
ALTER TABLE db.orders ADD COLUMN c INT NULL, ALGORITHM = INSTANT
What did you see instead?
DM emits two statements — the operation loses its qualifier, and the qualifier becomes a standalone no-op:
dm-worker.log:
ddl.go:459 originSQL: ALTER TABLE db.orders ADD COLUMN c INT NULL, ALGORITHM=INSTANT
baseconn.go:184 query="ALTER TABLE db.orders ADD COLUMN c INT NULL" ← qualifier lost
baseconn.go:184 query="ALTER TABLE db.orders ALGORITHM = INSTANT" ← orphan
Root cause
dm/pkg/parser/common.go, SplitDDL(), *ast.AlterTableStmt case (line 295 on v8.5.5):
v.Specs = []*ast.AlterTableSpec{spec} // one emitted statement per AST spec
ALGORITHM/LOCK are modelled as their own AlterTableSpec (ast.AlterTableAlgorithm / ast.AlterTableLock), not as attributes of the operation, so per-spec splitting separates them.
The splitting is justified at dm/syncer/ddl.go:311:
// TiDB can't handle multi schema change DDL, so we split it here.
On TiDB v8.5.5 the premise no longer holds — both of these execute successfully:
ALTER TABLE t ADD COLUMN b INT NULL, ADD COLUMN c INT NULL, DROP COLUMN a;
ALTER TABLE t ADD COLUMN d INT NULL, ADD COLUMN e INT NULL, ALGORITHM=INSTANT;
Why it matters
Against a TiDB-only target the impact is cosmetic — TiDB ignores the qualifier semantically (isIgnorableSpec). But TiDB still records the original query text in the DDL job (setDDLJobQuery uses s.OriginText()), so the qualifier is meaningful to anything replicating onward from TiDB.
In an Aurora → DM → TiDB → TiCDC → Aurora topology, TiCDC forwards job.Query verbatim. Because DM strips the qualifier, the downstream MySQL receives a bare ALTER — and MySQL silently falls back to a full table rebuild when an instant operation isn't possible.
Versions of the cluster
DM version (run dmctl -V or dm-worker -V or dm-master -V):
Upstream MySQL/MariaDB server version:
Aurora MySQL 3.10.3 (MySQL 8.0.42)
Downstream TiDB cluster version (execute SELECT tidb_version(); in a MySQL client):
TiDB version: v8.5.5
Downstream of TiDB (via TiCDC): Aurora MySQL 3.10.3
How did you deploy DM: tiup or manually?
Other interesting information (system version, hardware config, etc):
current status of DM cluster (execute query-status <task-name> in dmctl)
What did you do?
Replicate an ALTER TABLE carrying an ALGORITHM qualifier from MySQL/Aurora to TiDB.
Upstream:
ALTER TABLE db.orders ADD COLUMN c INT NULL, ALGORITHM=INSTANT;
What did you expect to see?
DM applies one statement downstream, preserving the qualifier:
ALTER TABLE
db.ordersADD COLUMNcINT NULL, ALGORITHM = INSTANTWhat did you see instead?
DM emits two statements — the operation loses its qualifier, and the qualifier becomes a standalone no-op:
dm-worker.log:
ddl.go:459 originSQL: ALTER TABLE db.orders ADD COLUMN c INT NULL, ALGORITHM=INSTANT
baseconn.go:184 query="ALTER TABLE
db.ordersADD COLUMNcINT NULL" ← qualifier lostbaseconn.go:184 query="ALTER TABLE
db.ordersALGORITHM = INSTANT" ← orphanRoot cause
dm/pkg/parser/common.go, SplitDDL(), *ast.AlterTableStmt case (line 295 on v8.5.5):
v.Specs = []*ast.AlterTableSpec{spec} // one emitted statement per AST spec
ALGORITHM/LOCK are modelled as their own AlterTableSpec (ast.AlterTableAlgorithm / ast.AlterTableLock), not as attributes of the operation, so per-spec splitting separates them.
The splitting is justified at dm/syncer/ddl.go:311:
// TiDB can't handle multi schema change DDL, so we split it here.
On TiDB v8.5.5 the premise no longer holds — both of these execute successfully:
ALTER TABLE t ADD COLUMN b INT NULL, ADD COLUMN c INT NULL, DROP COLUMN a;
ALTER TABLE t ADD COLUMN d INT NULL, ADD COLUMN e INT NULL, ALGORITHM=INSTANT;
Why it matters
Against a TiDB-only target the impact is cosmetic — TiDB ignores the qualifier semantically (isIgnorableSpec). But TiDB still records the original query text in the DDL job (setDDLJobQuery uses s.OriginText()), so the qualifier is meaningful to anything replicating onward from TiDB.
In an Aurora → DM → TiDB → TiCDC → Aurora topology, TiCDC forwards job.Query verbatim. Because DM strips the qualifier, the downstream MySQL receives a bare ALTER — and MySQL silently falls back to a full table rebuild when an instant operation isn't possible.
Versions of the cluster
DM version (run
dmctl -Vordm-worker -Vordm-master -V):DM version: v8.5.5Upstream MySQL/MariaDB server version:
Aurora MySQL 3.10.3 (MySQL 8.0.42)Downstream TiDB cluster version (execute
SELECT tidb_version();in a MySQL client):How did you deploy DM: tiup or manually?
ManuallyOther interesting information (system version, hardware config, etc):
current status of DM cluster (execute
query-status <task-name>in dmctl)running