schema/field: json type support (#38)

Summary:
Pull Request resolved: https://github.com/facebookincubator/ent/pull/38

Only `IsNil` and `NotNil` predicates are supported this moment

Reviewed By: alexsn

Differential Revision: D17444976

fbshipit-source-id: 37336fa0bc7749af995933baee2e23bb7366dd78
This commit is contained in:
Ariel Mashraki
2019-09-19 04:58:21 -07:00
committed by Facebook Github Bot
parent 83d0063437
commit c3955a08f1
214 changed files with 4005 additions and 1296 deletions

View File

@@ -718,6 +718,11 @@ func (u *UpdateBuilder) Where(p *Predicate) *UpdateBuilder {
return u
}
// Empty reports whether this builder does not contain update changes.
func (u *UpdateBuilder) Empty() bool {
return len(u.columns) == 0 && len(u.nulls) == 0
}
// Query returns query representation of an `UPDATE` statement.
func (u *UpdateBuilder) Query() (string, []interface{}) {
u.b.WriteString("UPDATE ")

View File

@@ -54,17 +54,18 @@ func TestMySQL_Create(t *testing.T) {
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Nullable: true},
{Name: "age", Type: field.TypeInt},
{Name: "doc", Type: field.TypeJSON, Nullable: true},
},
},
},
before: func(mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectQuery(escape("SHOW VARIABLES LIKE 'version'")).
WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}).AddRow("version", "5.7.23"))
WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}).AddRow("version", "5.7.8"))
mock.ExpectQuery(escape("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
mock.ExpectExec(escape("CREATE TABLE IF NOT EXISTS `users`(`id` bigint AUTO_INCREMENT NOT NULL, `name` varchar(255) NULL, `age` bigint 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, PRIMARY KEY(`id`)) CHARACTER SET utf8mb4")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
@@ -81,6 +82,7 @@ func TestMySQL_Create(t *testing.T) {
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "age", Type: field.TypeInt},
{Name: "name", Type: field.TypeString, Unique: true},
{Name: "doc", Type: field.TypeJSON, Nullable: true},
},
},
},
@@ -91,7 +93,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("users").
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
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")).
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")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},
@@ -201,6 +203,7 @@ func TestMySQL_Create(t *testing.T) {
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Nullable: true},
{Name: "age", Type: field.TypeInt, Default: 10},
{Name: "doc", Type: field.TypeJSON, Nullable: true},
},
PrimaryKey: []*Column{
{Name: "id", Type: field.TypeInt, Increment: true},
@@ -210,7 +213,7 @@ func TestMySQL_Create(t *testing.T) {
before: func(mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectQuery(escape("SHOW VARIABLES LIKE 'version'")).
WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}).AddRow("version", "5.7.23"))
WillReturnRows(sqlmock.NewRows([]string{"Variable_name", "Value"}).AddRow("version", "5.6.0"))
mock.ExpectQuery(escape("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE `TABLE_SCHEMA` = (SELECT DATABASE()) AND `TABLE_NAME` = ?")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
@@ -218,7 +221,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", "", "", ""))
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` = ?")).
WithArgs("users").
WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}).

View File

@@ -253,6 +253,11 @@ func (c *Column) MySQLType(version string) (t string) {
case size <= math.MaxUint32:
t = "longblob"
}
case field.TypeJSON:
t = "json"
if compareVersions(version, "5.7.8") == -1 {
t = "longblob"
}
case field.TypeString:
size := c.Size
if size == 0 {
@@ -298,6 +303,8 @@ func (c *Column) SQLiteType() (t string) {
t = "real"
case field.TypeTime:
t = "datetime"
case field.TypeJSON:
t = "json"
default:
panic("unsupported type " + c.Type.String())
}
@@ -371,6 +378,8 @@ func (c *Column) ScanMySQL(rows *sql.Rows) error {
case "longtext":
c.Size = math.MaxInt32
c.Type = field.TypeString
case "json":
c.Type = field.TypeJSON
}
if defaults.Valid && defaults.String != Null {
return c.ScanDefault(defaults.String)
@@ -504,12 +513,9 @@ func (c *Column) nullable(b *sql.ColumnBuilder) {
// prefix key limit (767).
func (c *Column) defaultSize(version string) int {
size := DefaultStringLen
parts := strings.Split(version, ".")
switch {
// invalid version.
case len(parts) == 1 || parts[0] == "" || parts[1] == "":
// version is > 5.6.*.
case parts[0] > "5" || parts[1] > "6":
// 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:
@@ -643,3 +649,63 @@ func (i *Indexes) ScanMySQL(rows *sql.Rows) error {
}
return nil
}
// compareVersions returns an integer comparing the 2 versions.
func compareVersions(v1, v2 string) int {
pv1, ok1 := parseVersion(v1)
pv2, ok2 := parseVersion(v2)
if !ok1 && !ok2 {
return 0
}
if !ok1 {
return -1
}
if !ok2 {
return 1
}
if v := compare(pv1.major, pv2.major); v != 0 {
return v
}
if v := compare(pv1.minor, pv2.minor); v != 0 {
return v
}
return compare(pv1.patch, pv2.patch)
}
// version represents a parsed MySQL version.
type version struct {
major int
minor int
patch int
}
// parseVersion returns an integer comparing the 2 versions.
func parseVersion(v string) (*version, bool) {
parts := strings.Split(v, ".")
if len(parts) == 0 {
return nil, false
}
var (
err error
ver = &version{}
)
for i, e := range []*int{&ver.major, &ver.minor, &ver.patch} {
if i == len(parts) {
break
}
if *e, err = strconv.Atoi(parts[i]); err != nil {
return nil, false
}
}
return ver, true
}
func compare(v1, v2 int) int {
if v1 == v2 {
return 0
}
if v1 < v2 {
return -1
}
return 1
}

View File

@@ -61,6 +61,7 @@ func TestSQLite_Create(t *testing.T) {
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Nullable: true},
{Name: "age", Type: field.TypeInt},
{Name: "doc", Type: field.TypeJSON, Nullable: true},
},
},
},
@@ -71,7 +72,7 @@ func TestSQLite_Create(t *testing.T) {
mock.ExpectQuery(escape("SELECT COUNT(*) FROM `sqlite_master` WHERE `type` = ? AND `name` = ?")).
WithArgs("table", "users").
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
mock.ExpectExec(escape("CREATE TABLE `users`(`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` varchar(255) NULL, `age` integer NOT NULL)")).
mock.ExpectExec(escape("CREATE TABLE `users`(`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` varchar(255) NULL, `age` integer NOT NULL, `doc` json NULL)")).
WillReturnResult(sqlmock.NewResult(0, 1))
mock.ExpectCommit()
},