diff --git a/dialect/sql/schema/mysql.go b/dialect/sql/schema/mysql.go index 8380e49d1..9c6d2e743 100644 --- a/dialect/sql/schema/mysql.go +++ b/dialect/sql/schema/mysql.go @@ -174,7 +174,7 @@ func (d *MySQL) cType(c *Column) (t string) { case field.TypeUint16: t = "smallint unsigned" case field.TypeInt32: - t = "int" + t = c.scanTypeOr("int") // Or mediumint. case field.TypeUint32: t = "int unsigned" case field.TypeInt, field.TypeInt64: @@ -214,7 +214,7 @@ func (d *MySQL) cType(c *Column) (t string) { case field.TypeFloat32, field.TypeFloat64: t = "double" case field.TypeTime: - t = "timestamp" + t = c.scanTypeOr("timestamp") // In MySQL, timestamp columns are `NOT NULL by default, and assigning NULL // assigns the current_timestamp(). We avoid this if not set otherwise. c.Nullable = true @@ -324,7 +324,7 @@ func (d *MySQL) scanColumn(c *Column, rows *sql.Rows) error { return err } switch parts[0] { - case "int": + case "mediumint", "int": c.Type = field.TypeInt32 if unsigned { c.Type = field.TypeUint32 @@ -350,7 +350,7 @@ func (d *MySQL) scanColumn(c *Column, rows *sql.Rows) error { } case "double": c.Type = field.TypeFloat64 - case "timestamp", "datetime": + case "time", "timestamp", "date", "datetime": c.Type = field.TypeTime case "tinyblob": c.Size = math.MaxUint8 @@ -481,12 +481,12 @@ func (d *MySQL) alterColumns(table string, add, modify, drop []*Column) sql.Quer return sql.Queries{b} } -// parseColumn returns column parts, size and signedness by mysql type +// parseColumn returns column parts, size and signed-info from a MySQL type. func parseColumn(typ string) (parts []string, size int64, unsigned bool, err error) { switch parts = strings.FieldsFunc(typ, func(r rune) bool { return r == '(' || r == ')' || r == ' ' || r == ',' }); parts[0] { - case "int", "smallint", "bigint", "tinyint": + case "tinyint", "smallint", "mediumint", "int", "bigint": switch { case len(parts) == 2 && parts[1] == "unsigned": // int unsigned unsigned = true diff --git a/dialect/sql/schema/mysql_test.go b/dialect/sql/schema/mysql_test.go index 01288bf84..14ca3397f 100644 --- a/dialect/sql/schema/mysql_test.go +++ b/dialect/sql/schema/mysql_test.go @@ -64,7 +64,7 @@ func TestMySQL_Create(t *testing.T) { before: func(mock mysqlMock) { mock.start("5.7.8") mock.tableExists("users", false) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `age` bigint NOT NULL, `doc` json NULL, `enums` enum('a', 'b') NOT NULL, `uuid` char(36) binary NULL, `datetime` datetime NULL, `decimal` decimal(6,2) NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `age` bigint NOT NULL, `doc` json NULL, `enums` enum('a', 'b') NOT NULL, `uuid` char(36) binary NULL, `datetime` datetime NULL, `decimal` decimal(6,2) NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() }, @@ -88,7 +88,7 @@ func TestMySQL_Create(t *testing.T) { before: func(mock mysqlMock) { mock.start("5.6.35") 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, `doc` longblob NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + 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, `doc` longblob NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() }, @@ -132,10 +132,10 @@ func TestMySQL_Create(t *testing.T) { before: func(mock mysqlMock) { mock.start("5.7.23") mock.tableExists("users", false) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `created_at` timestamp NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `created_at` timestamp NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.tableExists("pets", false) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `pets`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NOT NULL, `owner_id` bigint NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `pets`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NOT NULL, `owner_id` bigint NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.fkExists("pets_owner", false) mock.ExpectExec(escape("ALTER TABLE `pets` ADD CONSTRAINT `pets_owner` FOREIGN KEY(`owner_id`) REFERENCES `users`(`id`) ON DELETE CASCADE")). @@ -153,6 +153,7 @@ func TestMySQL_Create(t *testing.T) { {Name: "name", Type: field.TypeString, Nullable: true}, {Name: "text", Type: field.TypeString, Nullable: true, Size: math.MaxInt32}, {Name: "uuid", Type: field.TypeUUID, Nullable: true}, + {Name: "date", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{dialect.MySQL: "date"}}, {Name: "age", Type: field.TypeInt}, {Name: "tiny", Type: field.TypeInt8}, {Name: "tiny_unsigned", Type: field.TypeUint8}, @@ -167,7 +168,7 @@ func TestMySQL_Create(t *testing.T) { }, }, before: func(mock mysqlMock) { - mock.start("5.7.23") + mock.start("8.0.19") mock.tableExists("users", true) mock.ExpectQuery(escape("SELECT `column_name`, `column_type`, `is_nullable`, `column_key`, `column_default`, `extra`, `character_set_name`, `collation_name` FROM INFORMATION_SCHEMA.COLUMNS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). WithArgs("users"). @@ -176,6 +177,7 @@ func TestMySQL_Create(t *testing.T) { AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", ""). AddRow("text", "longtext", "YES", "YES", "NULL", "", "", ""). AddRow("uuid", "char(36)", "YES", "YES", "NULL", "", "", "utf8mb4_bin"). + AddRow("date", "date", "YES", "YES", "NULL", "", "", ""). // 8.0.19: new int column type formats AddRow("tiny", "tinyint", "NO", "YES", "NULL", "", "", ""). AddRow("tiny_unsigned", "tinyint unsigned", "NO", "YES", "NULL", "", "", ""). @@ -183,7 +185,7 @@ func TestMySQL_Create(t *testing.T) { AddRow("small_unsigned", "smallint unsigned", "NO", "YES", "NULL", "", "", ""). AddRow("big", "bigint", "NO", "YES", "NULL", "", "", ""). AddRow("big_unsigned", "bigint unsigned", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -220,7 +222,7 @@ func TestMySQL_Create(t *testing.T) { AddRow("enums1", "enum('a')", "YES", "NO", "NULL", "", "", ""). AddRow("enums2", "enum('b', 'a')", "NO", "YES", "NULL", "", "", ""). AddRow("enums3", "enum('b', 'a')", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -236,7 +238,7 @@ func TestMySQL_Create(t *testing.T) { Name: "users", Columns: []*Column{ {Name: "id", Type: field.TypeInt, Increment: true}, - {Name: "created_at", Type: field.TypeTime, Nullable: true}, + {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "datetime"}, Nullable: true}, }, PrimaryKey: []*Column{ {Name: "id", Type: field.TypeInt, Increment: true}, @@ -251,7 +253,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("created_at", "datetime", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -281,9 +283,9 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). - AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""). - AddRow("doc", "longblob", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", ""). + AddRow("doc", "longblob", "YES", "YES", "NULL", "", "", "")) + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -316,7 +318,7 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -349,7 +351,7 @@ func TestMySQL_Create(t *testing.T) { AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("tiny", "varbinary(255)", "NO", "YES", "NULL", "", "", ""). AddRow("medium", "varbinary(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -381,7 +383,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -413,7 +415,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -444,8 +446,8 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). - AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "")) + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -476,8 +478,8 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). - AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "")) + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -508,7 +510,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -541,7 +543,7 @@ func TestMySQL_Create(t *testing.T) { AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", ""). AddRow("age", "bigint(20)", "NO", "NO", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -572,7 +574,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("age", "bigint(20)", "NO", "", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -604,7 +606,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("age", "bigint(20)", "NO", "UNI", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1"). @@ -635,7 +637,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("age", "bigint(20)", "NO", "UNI", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1"). @@ -681,7 +683,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1"). @@ -722,7 +724,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1"). @@ -765,7 +767,7 @@ func TestMySQL_Create(t *testing.T) { WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). AddRow("parent_id", "bigint(20)", "YES", "NULL", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1"). @@ -816,13 +818,13 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", ""). - AddRow("name", "varchar(255)", "NO", "YES", "NULL", "", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + AddRow("name", "varchar(255)", "YES", "YES", "NULL", "", "", "")) + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) mock.fkExists("user_spouse_____________________390ed76f91d3c57cd3516e7690f621dc", false) - mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `spouse_id` bigint")). + mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `spouse_id` bigint NULL")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.fkExists("user_spouse_____________________390ed76f91d3c57cd3516e7690f621dc", false) mock.ExpectExec("ALTER TABLE `users` ADD CONSTRAINT `.{64}` FOREIGN KEY\\(`spouse_id`\\) REFERENCES `users`\\(`id`\\) ON DELETE CASCADE"). @@ -841,10 +843,10 @@ func TestMySQL_Create(t *testing.T) { mock.start("5.7.23") mock.tableExists("ent_types", false) // create ent_types table. - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `ent_types`(`id` bigint AUTO_INCREMENT NOT NULL, `type` varchar(255) UNIQUE NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `ent_types`(`id` bigint AUTO_INCREMENT NOT NULL, `type` varchar(255) 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, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT 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 (?)")). @@ -855,7 +857,7 @@ func TestMySQL_Create(t *testing.T) { mock.ExpectQuery(escape("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). WithArgs("groups"). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) // set groups id range. mock.ExpectExec(escape("INSERT INTO `ent_types` (`type`) VALUES (?)")). @@ -885,7 +887,7 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -893,7 +895,7 @@ func TestMySQL_Create(t *testing.T) { mock.ExpectQuery(escape("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). WithArgs("groups"). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) // set groups id range. mock.ExpectExec(escape("INSERT INTO `ent_types` (`type`) VALUES (?)")). @@ -918,13 +920,13 @@ func TestMySQL_Create(t *testing.T) { mock.ExpectQuery(escape("SELECT `type` FROM `ent_types` ORDER BY `id` ASC")). WillReturnRows(sqlmock.NewRows([]string{"type"}).AddRow("users")) mock.tableExists("users", false) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) // set users id range (without inserting to ent_types). mock.ExpectExec(escape("ALTER TABLE `users` AUTO_INCREMENT = 0")). WillReturnResult(sqlmock.NewResult(0, 1)) mock.tableExists("groups", false) - mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")). + mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `groups`(`id` bigint AUTO_INCREMENT NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")). WillReturnResult(sqlmock.NewResult(0, 1)) // set groups id range. mock.ExpectExec(escape("INSERT INTO `ent_types` (`type`) VALUES (?)")). @@ -955,7 +957,7 @@ func TestMySQL_Create(t *testing.T) { WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"column_name", "column_type", "is_nullable", "column_key", "column_default", "extra", "character_set_name", "collation_name"}). AddRow("id", "bigint(20)", "NO", "PRI", "NULL", "auto_increment", "", "")) - mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")). + mock.ExpectQuery(escape("SELECT `index_name`, `column_name`, `non_unique`, `seq_in_index` FROM INFORMATION_SCHEMA.STATISTICS WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ? ORDER BY `index_name`, `seq_in_index`")). WithArgs("users"). WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). AddRow("PRIMARY", "id", "0", "1")) @@ -1020,5 +1022,5 @@ func escape(query string) string { rows[i] = strings.TrimPrefix(rows[i], " ") } query = strings.Join(rows, " ") - return regexp.QuoteMeta(query) + return strings.TrimSpace(regexp.QuoteMeta(query)) + "$" } diff --git a/dialect/sql/schema/schema.go b/dialect/sql/schema/schema.go index 0ef29aed2..9734651d0 100644 --- a/dialect/sql/schema/schema.go +++ b/dialect/sql/schema/schema.go @@ -291,6 +291,14 @@ func (c *Column) defaultSize(version string) int64 { return size } +// scanTypeOr returns the scanning type or the given value. +func (c *Column) scanTypeOr(t string) string { + if c.typ != "" { + return strings.ToLower(c.typ) + } + return t +} + // ForeignKey definition for creation. type ForeignKey struct { Symbol string // foreign-key name. Generated if empty.