dialect/sql/schema: handle index prefix key limit in older versions of mariadb

Fixed #1378
This commit is contained in:
Ariel Mashraki
2021-03-23 15:57:12 +02:00
committed by Ariel Mashraki
parent 56a0cf1e8b
commit 305edd00e0
3 changed files with 56 additions and 17 deletions

View File

@@ -244,7 +244,7 @@ func (d *MySQL) cType(c *Column) (t string) {
case field.TypeString:
size := c.Size
if size == 0 {
size = c.defaultSize(d.version)
size = d.defaultSize(c)
}
if size <= math.MaxUint16 {
t = fmt.Sprintf("varchar(%d)", size)
@@ -659,3 +659,24 @@ func (d *MySQL) fkNames(ctx context.Context, tx dialect.Tx, table, column string
}
return names, nil
}
// defaultSize returns the default size for MySQL/MariaDB varchar type
// based on column size, charset and table indexes, in order to avoid
// index prefix key limit (767) for older versions of MySQL/MariaDB.
func (d *MySQL) defaultSize(c *Column) int64 {
size := DefaultStringLen
version, checked := d.version, "5.7.0"
if v, ok := d.mariadb(); ok {
version, checked = v, "10.2.2"
}
switch {
// Version is >= 5.7 for MySQL, or >= 10.2.2 for MariaDB.
case compareVersions(version, checked) != -1:
// Column is non-unique, or not part of any index (reaching
// the error 1071).
case !c.Unique && len(c.indexes) == 0:
default:
size = 191
}
return size
}

View File

@@ -1143,6 +1143,40 @@ func TestMySQL_Create(t *testing.T) {
mock.ExpectCommit()
},
},
{
name: "mariadb/10.1.37/create table",
tables: []*Table{
{
Name: "users",
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
},
Columns: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "age", Type: field.TypeInt},
{Name: "name", Type: field.TypeString, Unique: true},
},
},
},
options: []MigrateOption{WithGlobalUniqueID(true)},
before: func(mock mysqlMock) {
mock.start("10.1.48-MariaDB-1~bionic")
mock.tableExists("ent_types", false)
// create ent_types table.
mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `ent_types`(`id` bigint unsigned AUTO_INCREMENT NOT NULL, `type` varchar(191) UNIQUE NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.tableExists("users", false)
mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `age` bigint NOT NULL, `name` varchar(191) UNIQUE NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")).
WillReturnResult(sqlmock.NewResult(0, 1))
// set users id range.
mock.ExpectExec(escape("INSERT INTO `ent_types` (`type`) VALUES (?)")).
WithArgs("users").
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectExec(escape("ALTER TABLE `users` AUTO_INCREMENT = 0")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -318,22 +318,6 @@ func (c *Column) nullable(b *sql.ColumnBuilder) {
b.Attr(attr)
}
// defaultSize returns the default size for MySQL varchar type based
// on column size, charset and table indexes, in order to avoid index
// prefix key limit (767).
func (c *Column) defaultSize(version string) int64 {
size := DefaultStringLen
switch {
// version is >= 5.7.
case compareVersions(version, "5.7.0") != -1:
// non-unique, or not part of any index (reaching the error 1071).
case !c.Unique && len(c.indexes) == 0:
default:
size = 191
}
return size
}
// scanTypeOr returns the scanning type or the given value.
func (c *Column) scanTypeOr(t string) string {
if c.typ != "" {