dialect/entsql: add support for column default using annotation

Fixed #1033
This commit is contained in:
Ariel Mashraki
2021-04-02 17:10:00 +03:00
committed by Ariel Mashraki
parent 3db3f5fd1a
commit 745afde770
16 changed files with 319 additions and 30 deletions

View File

@@ -58,7 +58,7 @@ func TestMySQL_Create(t *testing.T) {
{Name: "doc", Type: field.TypeJSON, Nullable: true},
{Name: "enums", Type: field.TypeEnum, Enums: []string{"a", "b"}},
{Name: "uuid", Type: field.TypeUUID, Nullable: true},
{Name: "datetime", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "datetime"}, Nullable: true},
{Name: "datetime", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "datetime"}, Default: "CURRENT_TIMESTAMP"},
{Name: "decimal", Type: field.TypeFloat32, SchemaType: map[string]string{dialect.MySQL: "decimal(6,2)"}},
},
Annotation: &entsql.Annotation{
@@ -71,7 +71,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 utf8 COLLATE utf8_general_ci ENGINE = INNODB")).
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 NOT NULL DEFAULT CURRENT_TIMESTAMP, `decimal` decimal(6,2) NOT NULL, PRIMARY KEY(`id`)) CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = INNODB")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
@@ -219,7 +219,7 @@ func TestMySQL_Create(t *testing.T) {
{Name: "big", Type: field.TypeInt64},
{Name: "big_unsigned", Type: field.TypeUint64},
{Name: "decimal", Type: field.TypeFloat64, SchemaType: map[string]string{dialect.MySQL: "decimal(6,2)"}},
{Name: "timestamp", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "TIMESTAMP"}},
{Name: "timestamp", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "TIMESTAMP"}, Default: "CURRENT_TIMESTAMP"},
},
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},

View File

@@ -62,7 +62,7 @@ func TestPostgres_Create(t *testing.T) {
{Name: "age", Type: field.TypeInt},
{Name: "doc", Type: field.TypeJSON, Nullable: true},
{Name: "enums", Type: field.TypeEnum, Enums: []string{"a", "b"}, Default: "a"},
{Name: "uuid", Type: field.TypeUUID},
{Name: "uuid", Type: field.TypeUUID, Default: "uuid_generate_v4()"},
{Name: "price", Type: field.TypeFloat64, SchemaType: map[string]string{dialect.Postgres: "numeric(5,2)"}},
},
},
@@ -70,7 +70,7 @@ func TestPostgres_Create(t *testing.T) {
before: func(mock pgMock) {
mock.start("120000")
mock.tableExists("users", false)
mock.ExpectExec(escape(`CREATE TABLE IF NOT EXISTS "users"("id" bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL, "name" varchar NULL, "age" bigint NOT NULL, "doc" jsonb NULL, "enums" varchar NOT NULL DEFAULT 'a', "uuid" uuid NOT NULL, "price" numeric(5,2) NOT NULL, PRIMARY KEY("id"))`)).
mock.ExpectExec(escape(`CREATE TABLE IF NOT EXISTS "users"("id" bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL, "name" varchar NULL, "age" bigint NOT NULL, "doc" jsonb NULL, "enums" varchar NOT NULL DEFAULT 'a', "uuid" uuid NOT NULL DEFAULT uuid_generate_v4(), "price" numeric(5,2) NOT NULL, PRIMARY KEY("id"))`)).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
@@ -241,7 +241,7 @@ func TestPostgres_Create(t *testing.T) {
{Name: "uuid", Type: field.TypeUUID, Nullable: true},
{Name: "text", Type: field.TypeString, Nullable: true, Size: math.MaxInt32},
{Name: "age", Type: field.TypeInt},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{dialect.Postgres: "date"}},
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{dialect.Postgres: "date"}, Default: "CURRENT_DATE"},
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{dialect.MySQL: "date"}, Nullable: true},
{Name: "deleted_at", Type: field.TypeTime, Nullable: true},
{Name: "cidr", Type: field.TypeString, SchemaType: map[string]string{dialect.Postgres: "cidr"}},

View File

@@ -271,31 +271,32 @@ func (c *Column) ScanDefault(value string) error {
// Note that, in SQLite if a NOT NULL constraint is specified,
// then the column must have a default value which not NULL.
func (c *Column) defaultValue(b *sql.ColumnBuilder) {
// has default, and it's supported in the database level.
if c.Default != nil && c.supportDefault() {
attr := "DEFAULT "
switch v := c.Default.(type) {
case bool:
attr += strconv.FormatBool(v)
case string:
// Escape single quote by replacing each with 2.
attr += fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''"))
default:
attr += fmt.Sprint(v)
}
b.Attr(attr)
if c.Default == nil || !c.supportDefault() {
return
}
// Has default and the database supports adding this default.
attr := fmt.Sprint(c.Default)
switch v := c.Default.(type) {
case bool:
attr = strconv.FormatBool(v)
case string:
if t := c.Type; t != field.TypeUUID && t != field.TypeTime {
// Escape single quote by replacing each with 2.
attr = fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''"))
}
}
b.Attr("DEFAULT " + attr)
}
// supportDefault reports if the column type supports default value.
func (c Column) supportDefault() bool {
switch {
case c.Type == field.TypeString || c.Type == field.TypeEnum:
switch t := c.Type; t {
case field.TypeString, field.TypeEnum:
return c.Size < 1<<16 // not a text.
case c.Type.Numeric(), c.Type == field.TypeBool:
case field.TypeBool, field.TypeTime, field.TypeUUID:
return true
default:
return false
return t.Numeric()
}
}