Skip to content

Commit 6bd2573

Browse files
elianddbclaude
andcommitted
Fix auto-increment overflow handling to match MySQL behavior
Added bounds checking for auto-increment values to prevent overflow beyond data type limits. The fix ensures auto-increment values don't exceed type maximums (e.g., 127 for TINYINT) and correctly throws duplicate key errors instead of allowing overflow. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6a19649 commit 6bd2573

File tree

3 files changed

+129
-6
lines changed

3 files changed

+129
-6
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2025 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package queries
16+
17+
import (
18+
"github.com/dolthub/go-mysql-server/sql"
19+
"github.com/dolthub/go-mysql-server/sql/types"
20+
)
21+
22+
var AutoIncrementOverflowTests = []QueryTest{
23+
{
24+
Query: "CREATE TABLE test_tinyint (id TINYINT AUTO_INCREMENT PRIMARY KEY, value VARCHAR(50))",
25+
Expected: []sql.Row{
26+
{types.NewOkResult(0)},
27+
},
28+
},
29+
{
30+
Query: "INSERT INTO test_tinyint (id, value) VALUES (127, 'test127')",
31+
Expected: []sql.Row{
32+
{types.NewOkResult(1)},
33+
},
34+
},
35+
{
36+
Query: "SHOW CREATE TABLE test_tinyint",
37+
Expected: []sql.Row{
38+
{"test_tinyint", "CREATE TABLE `test_tinyint` (\n `id` tinyint NOT NULL AUTO_INCREMENT,\n `value` varchar(50),\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=127 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
39+
},
40+
},
41+
{
42+
Query: "CREATE TABLE test_smallint (id SMALLINT AUTO_INCREMENT PRIMARY KEY, value VARCHAR(50))",
43+
Expected: []sql.Row{
44+
{types.NewOkResult(0)},
45+
},
46+
},
47+
{
48+
Query: "INSERT INTO test_smallint (id, value) VALUES (32767, 'test32767')",
49+
Expected: []sql.Row{
50+
{types.NewOkResult(1)},
51+
},
52+
},
53+
{
54+
Query: "SHOW CREATE TABLE test_smallint",
55+
Expected: []sql.Row{
56+
{"test_smallint", "CREATE TABLE `test_smallint` (\n `id` smallint NOT NULL AUTO_INCREMENT,\n `value` varchar(50),\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=32767 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
57+
},
58+
},
59+
{
60+
Query: "CREATE TABLE test_int (id INT AUTO_INCREMENT PRIMARY KEY, value VARCHAR(50))",
61+
Expected: []sql.Row{
62+
{types.NewOkResult(0)},
63+
},
64+
},
65+
{
66+
Query: "INSERT INTO test_int (id, value) VALUES (2147483647, 'test2147483647')",
67+
Expected: []sql.Row{
68+
{types.NewOkResult(1)},
69+
},
70+
},
71+
{
72+
Query: "SHOW CREATE TABLE test_int",
73+
Expected: []sql.Row{
74+
{"test_int", "CREATE TABLE `test_int` (\n `id` int NOT NULL AUTO_INCREMENT,\n `value` varchar(50),\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=2147483647 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
75+
},
76+
},
77+
{
78+
Query: "CREATE TABLE test_bigint (id BIGINT AUTO_INCREMENT PRIMARY KEY, value VARCHAR(50))",
79+
Expected: []sql.Row{
80+
{types.NewOkResult(0)},
81+
},
82+
},
83+
{
84+
Query: "INSERT INTO test_bigint (id, value) VALUES (9223372036854775807, 'test9223372036854775807')",
85+
Expected: []sql.Row{
86+
{types.NewOkResult(1)},
87+
},
88+
},
89+
{
90+
Query: "SHOW CREATE TABLE test_bigint",
91+
Expected: []sql.Row{
92+
{"test_bigint", "CREATE TABLE `test_bigint` (\n `id` bigint NOT NULL AUTO_INCREMENT,\n `value` varchar(50),\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=9223372036854775807 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
93+
},
94+
},
95+
}
96+
97+
var AutoIncrementOverflowErrorTests = []QueryErrorTest{
98+
{
99+
Query: "INSERT INTO test_tinyint (value) VALUES ('test_overflow')",
100+
ExpectedErr: sql.ErrPrimaryKeyViolation,
101+
},
102+
{
103+
Query: "INSERT INTO test_smallint (value) VALUES ('test_overflow')",
104+
ExpectedErr: sql.ErrPrimaryKeyViolation,
105+
},
106+
{
107+
Query: "INSERT INTO test_int (value) VALUES ('test_overflow')",
108+
ExpectedErr: sql.ErrPrimaryKeyViolation,
109+
},
110+
{
111+
Query: "INSERT INTO test_bigint (value) VALUES ('test_overflow')",
112+
ExpectedErr: sql.ErrPrimaryKeyViolation,
113+
},
114+
}

memory/table.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,12 @@ func (t *Table) PeekNextAutoIncrementValue(ctx *sql.Context) (uint64, error) {
11471147
return data.autoIncVal, nil
11481148
}
11491149

1150+
func canIncrementAutoIncVal(ctx *sql.Context, colType sql.Type, currentVal uint64) bool {
1151+
nextVal := currentVal + 1
1152+
_, inRange, err := colType.Convert(ctx, nextVal)
1153+
return err == nil && inRange == sql.InRange
1154+
}
1155+
11501156
// GetNextAutoIncrementValue gets the next auto increment value for the memory table the increment.
11511157
func (t *Table) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{}) (uint64, error) {
11521158
data := t.sessionTableData(ctx)
@@ -1163,7 +1169,6 @@ func (t *Table) GetNextAutoIncrementValue(ctx *sql.Context, insertVal interface{
11631169
}
11641170
data.autoIncVal = v.(uint64)
11651171
}
1166-
11671172
return data.autoIncVal, nil
11681173
}
11691174

@@ -1257,7 +1262,9 @@ func addColumnToSchema(ctx *sql.Context, data *TableData, newCol *sql.Column, or
12571262
data.autoIncVal = 0
12581263
}
12591264

1260-
data.autoIncVal++
1265+
if canIncrementAutoIncVal(ctx, newCol.Type, data.autoIncVal) {
1266+
data.autoIncVal++
1267+
}
12611268
}
12621269

12631270
newPkOrds := data.schema.PkOrdinals

memory/table_editor.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,18 @@ func (t *tableEditor) Insert(ctx *sql.Context, row sql.Row) error {
188188
return err
189189
}
190190
if cmp > 0 {
191-
// Provided value larger than autoIncVal, set autoIncVal to that value
192191
v, _, err := types.Uint64.Convert(ctx, row[idx])
193192
if err != nil {
194193
return err
195194
}
196195
t.ea.TableData().autoIncVal = v.(uint64)
197-
t.ea.TableData().autoIncVal++ // Move onto next autoIncVal
196+
if canIncrementAutoIncVal(ctx, autoCol.Type, v.(uint64)) {
197+
t.ea.TableData().autoIncVal++
198+
}
198199
} else if cmp == 0 {
199-
// Provided value equal to autoIncVal
200-
t.ea.TableData().autoIncVal++ // Move onto next autoIncVal
200+
if canIncrementAutoIncVal(ctx, autoCol.Type, t.ea.TableData().autoIncVal) {
201+
t.ea.TableData().autoIncVal++
202+
}
201203
}
202204
}
203205

0 commit comments

Comments
 (0)