pingcap / pingcap/tidb

`ALTER SEQUENCE ... RESTART WITH n` returns wrong value — rounds to arithmetic progression instead of returning `n` exactly

Open
#67,943 3 comments 0 reactions 0 assignees View on GitHub
compatibility-mariadb component/ddl may-affects-7.1 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major type/bug type/compatibility
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Summary

`ALTER SEQUENCE seq RESTART WITH n` is specified by the SQL standard (ISO/IEC 9075-2:2003, §11 "Alter sequence generator statement") to make the very next `NEXTVAL` return **exactly `n`**. TiDB instead returns the **smallest value in the sequence's arithmetic progression `{START + k×INCREMENT}` that is ≥ n**, rounding up when `n` is not itself a member of that progression. This causes incorrect behavior in ORM frameworks (e.g. Hibernate ORM) that rely on standard `RESTART WITH` semantics for sequence resynchronization.

## Minimal Reproduction

```sql
-- Sequence with START=1 INCREMENT=20; valid values are {1, 21, 41, ..., 201, 221, ...}
CREATE SEQUENCE s START WITH 1 INCREMENT BY 20;

SELECT NEXTVAL(s); -- 1
SELECT NEXTVAL(s); -- 21

-- 220 is NOT in {1, 21, ..., 201, 221, ...}
ALTER SEQUENCE s RESTART WITH 220;
SELECT NEXTVAL(s); -- Expected: 220 Actual: 221

-- 221 IS in the progression — no rounding here
ALTER SEQUENCE s RESTART WITH 221;
SELECT NEXTVAL(s); -- Expected: 221 Actual: 221 ✓

-- NOCACHE does NOT fix the behavior
DROP SEQUENCE s;
CREATE SEQUENCE s START WITH 1 INCREMENT BY 20 NOCACHE;
ALTER SEQUENCE s RESTART WITH 220;
SELECT NEXTVAL(s); -- Expected: 220 Actual: 221 (still wrong)

DROP SEQUENCE s;
```

## Expected vs Actual

| Statement | Expected (SQL standard) | Actual (TiDB) |
|---|---|---|
| `RESTART WITH 220` on `{1,21,…,201,221,…}` | **220** | **221** |
| `RESTART WITH 219` on `{1,21,…,201,221,…}` | 219 | 221 |
| `RESTART WITH 200` on `{20,40,…,200,220,…}` | 200 | 200 ✓ (happens to be in progression) |
| `RESTART WITH 201` on `{20,40,…,200,220,…}` | 201 | 220 |

TiDB returns the correct value **only** when `n` is already a member of `{START + k×INCREMENT}`.

## SQL Standard Reference

**ISO/IEC 9075-2:2003 (SQL:2003), section 11 — "Alter sequence generator statement"** specifies that `RESTART WITH n` sets the sequence's current value so that the next execution of `NEXT VALUE FOR` returns exactly `n`. No rounding to the arithmetic progression is permitted.

All major SQL-standard-compliant databases confirm this:

- **PostgreSQL**: *"RESTART WITH restart — The specified value will be returned by the next call of `nextval`."* (https://www.postgresql.org/docs/current/sql-altersequence.html)
- **MariaDB**: *"Equivalent to calling `SETVAL()` with `is_used=0` — the specified value will be returned by the next call of `nextval`."* (https://mariadb.com/docs/server/reference/sql-structure/sequences/alter-sequence)
- **SQL Server**: *"`RESTART WITH` — the next value returned by the sequence object"* (https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-sequence-transact-sql)

## SETVAL Has the Same Problem

`SETVAL(seq, n)` exhibits the same rounding behavior. Additionally, when `n` is less than the sequence's current value, `SETVAL` silently returns `NULL` and has no effect — making it impossible to reset a sequence backwards via `SETVAL`.

```sql
CREATE SEQUENCE s START WITH 1 INCREMENT BY 20;
SELECT NEXTVAL(s); -- 1
SELECT NEXTVAL(s); -- 21
SELECT SETVAL(s, 200); -- returns 200
SELECT NEXTVAL(s); -- 201 (rounds to next valid ≥ 200+1)

-- Try to go backwards — silently ignored
SELECT SETVAL(s, 10); -- NULL (no-op, can't go backwards)
SELECT NEXTVAL(s); -- 221 (continues unaffected)
```

## Impact on Hibernate ORM

Hibernate ORM's `SchemaManager.resynchronizeGenerators()` (SequenceStructure.java) issues `ALTER SEQUENCE seq RESTART WITH n` to realign a sequence after rows are bulk-inserted with explicit IDs. With `PooledOptimizer` (allocation size = 20), after inserting rows with max ID = 200, Hibernate computes `startWith = 220` and issues:

```sql
ALTER SEQUENCE TheSequence RESTART WITH 220;
```

On PostgreSQL/MariaDB: next `NEXTVAL` = 220 → `PooledOptimizer` allocates IDs [201, 220] → first generated ID = **201** ✓

On TiDB: next `NEXTVAL` = **221** → `PooledOptimizer` allocates IDs [202, 221] → first generated ID = **202** ✗

This causes `SchemaManagerResyncSequencesTest` to fail. A workaround was implemented in Hibernate (TiDBSequenceSupport) that changes sequence creation to `START WITH incrementSize` and adjusts the restart value to `RESTART WITH (startWith − increment + 1)` to compensate for TiDB's rounding. This workaround would not be needed if TiDB followed the standard.

- Test: https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerResyncSequencesTest.java
- Workaround: https://github.com/hibernate/hibernate-orm/blob/main/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/sequence/TiDBSequenceSupport.java

## TiDB Version

Reproduced on TiDB v8.5.6 (April 2026)

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.