From 7dfe3c174c7810a2344acb699a040f5662f3335f Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Tue, 10 Sep 2019 12:38:53 -0700 Subject: [PATCH] sql/schema: more precise blob definition Summary: Pull Request resolved: https://github.com/facebookincubator/ent/pull/29 Reviewed By: alexsn Differential Revision: D17284406 fbshipit-source-id: 84c2ffb50b8f016ad361f1420c5352c7969cbc77 --- dialect/sql/schema/mysql_test.go | 37 ++++++ dialect/sql/schema/schema.go | 28 ++++- entc/gen/internal/bindata.go | 10 +- entc/gen/template/dialect/gremlin/update.tmpl | 2 +- entc/integration/ent/comment_update.go | 8 +- .../integration/migrate/entv1/example_test.go | 1 + .../migrate/entv1/migrate/schema.go | 1 + entc/integration/migrate/entv1/schema/user.go | 3 + entc/integration/migrate/entv1/user.go | 6 + entc/integration/migrate/entv1/user/user.go | 3 + entc/integration/migrate/entv1/user/where.go | 119 ++++++++++++++++++ entc/integration/migrate/entv1/user_create.go | 11 ++ entc/integration/migrate/entv1/user_update.go | 23 ++++ .../integration/migrate/entv2/example_test.go | 1 + .../migrate/entv2/migrate/schema.go | 1 + entc/integration/migrate/entv2/schema/user.go | 4 + entc/integration/migrate/entv2/user.go | 6 + entc/integration/migrate/entv2/user/user.go | 3 + entc/integration/migrate/entv2/user/where.go | 119 ++++++++++++++++++ entc/integration/migrate/entv2/user_create.go | 11 ++ entc/integration/migrate/entv2/user_update.go | 23 ++++ entc/integration/migrate/migrate_test.go | 11 ++ schema/field/field.go | 8 ++ 23 files changed, 427 insertions(+), 12 deletions(-) diff --git a/dialect/sql/schema/mysql_test.go b/dialect/sql/schema/mysql_test.go index 532690db9..3acec2402 100644 --- a/dialect/sql/schema/mysql_test.go +++ b/dialect/sql/schema/mysql_test.go @@ -225,6 +225,43 @@ func TestMySQL_Create(t *testing.T) { mock.ExpectCommit() }, }, + { + name: "add blob columns", + tables: []*Table{ + { + Name: "users", + Columns: []*Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "tiny", Type: field.TypeBytes, Size: 100}, + {Name: "blob", Type: field.TypeBytes, Size: 1e3}, + {Name: "medium", Type: field.TypeBytes, Size: 1e5}, + {Name: "long", Type: field.TypeBytes, Size: 1e8}, + }, + PrimaryKey: []*Column{ + {Name: "id", Type: field.TypeInt, Increment: 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")) + 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)) + 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"). + 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` = ?")). + WithArgs("users"). + WillReturnRows(sqlmock.NewRows([]string{"index_name", "column_name", "non_unique", "seq_in_index"}). + AddRow("PRIMARY", "id", "0", "1")) + mock.ExpectExec(escape("ALTER TABLE `users` ADD COLUMN `tiny` tinyblob NOT NULL, ADD COLUMN `blob` blob NOT NULL, ADD COLUMN `medium` mediumblob NOT NULL, ADD COLUMN `long` longblob NOT NULL")). + WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectCommit() + }, + }, { name: "add float column with default value to table", tables: []*Table{ diff --git a/dialect/sql/schema/schema.go b/dialect/sql/schema/schema.go index 798f0a537..7a984b317 100644 --- a/dialect/sql/schema/schema.go +++ b/dialect/sql/schema/schema.go @@ -6,6 +6,7 @@ package schema import ( "fmt" + "math" "strconv" "strings" @@ -238,13 +239,26 @@ func (c *Column) MySQLType(version string) (t string) { case field.TypeUint, field.TypeUint64: t = "bigint unsigned" case field.TypeBytes: - t = "blob" + size := math.MaxUint16 + if c.Size > 0 { + size = c.Size + } + switch { + case size <= math.MaxUint8: + t = "tinyblob" + case size <= math.MaxUint16: + t = "blob" + case size < 1<<24: + t = "mediumblob" + case size <= math.MaxUint32: + t = "longblob" + } case field.TypeString: size := c.Size if size == 0 { size = c.defaultSize(version) } - if size < 1<<16 { + if size <= math.MaxUint16 { t = fmt.Sprintf("varchar(%d)", size) } else { t = "longtext" @@ -335,7 +349,17 @@ func (c *Column) ScanMySQL(rows *sql.Rows) error { c.Type = field.TypeFloat64 case "timestamp": c.Type = field.TypeTime + case "tinyblob": + c.Size = math.MaxUint8 + c.Type = field.TypeBytes case "blob": + c.Size = math.MaxUint16 + c.Type = field.TypeBytes + case "mediumblob": + c.Size = 1<<24 - 1 + c.Type = field.TypeBytes + case "longblob": + c.Size = math.MaxUint32 c.Type = field.TypeBytes case "varchar": c.Type = field.TypeString diff --git a/entc/gen/internal/bindata.go b/entc/gen/internal/bindata.go index 7e5c3c4ab..ea08ae178 100644 --- a/entc/gen/internal/bindata.go +++ b/entc/gen/internal/bindata.go @@ -201,7 +201,7 @@ func templateBuilderSetterTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/setter.tmpl", size: 3778, mode: os.FileMode(420), modTime: time.Unix(1568034835, 0)} + info := bindataFileInfo{name: "template/builder/setter.tmpl", size: 3778, mode: os.FileMode(420), modTime: time.Unix(1568122710, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -221,7 +221,7 @@ func templateBuilderUpdateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/update.tmpl", size: 7931, mode: os.FileMode(420), modTime: time.Unix(1568034835, 0)} + info := bindataFileInfo{name: "template/builder/update.tmpl", size: 7931, mode: os.FileMode(420), modTime: time.Unix(1568122710, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -486,7 +486,7 @@ func templateDialectGremlinSelectTmpl() (*asset, error) { return a, nil } -var _templateDialectGremlinUpdateTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x58\x5f\x73\xdb\x36\x12\x7f\xa6\x3e\xc5\x9e\x46\xc9\x90\x3e\x15\x76\xfa\x76\xee\xf8\x66\x52\x47\xb9\xea\xa6\x67\xb7\x91\xdb\x7b\xc8\x64\x34\x30\xb1\x94\x30\xa6\x00\x16\x00\x19\xfb\x34\xfc\xee\x37\xf8\xc3\xbf\x92\x9d\xc6\x69\x9b\x17\x9b\x04\x96\xbb\xfb\xdb\xfd\xed\x2e\xa0\xfd\xfe\xf4\x64\x72\x29\x8b\x07\xc5\x37\x5b\x03\xdf\x9e\xbd\xfa\xc7\x37\x85\x42\x8d\xc2\xc0\x5b\x9a\xe2\xad\x94\x77\xb0\x14\x29\x81\xd7\x79\x0e\x4e\x48\x83\xdd\x57\x15\x32\x32\xb9\xd9\x72\x0d\x5a\x96\x2a\x45\x48\x25\x43\xe0\x1a\x72\x9e\xa2\xd0\xc8\xa0\x14\x0c\x15\x98\x2d\xc2\xeb\x82\xa6\x5b\x84\x6f\xc9\x59\xb3\x0b\x99\x2c\x05\x9b\x70\xe1\xf6\x7f\x5c\x5e\x2e\xae\x56\x0b\xc8\x78\x8e\x10\xd6\x94\x94\x06\x18\x57\x98\x1a\xa9\x1e\x40\x66\x60\x7a\xc6\x8c\x42\x24\x93\x93\xd3\xba\x9e\x4c\xf6\x7b\x60\x98\x71\x81\x30\x65\x9c\xe6\x98\x9a\xd3\x8d\xc2\x5d\xce\xc5\x69\x59\x30\x6a\x70\x0a\x75\x6d\xa5\x66\xb7\x25\xcf\xad\x4f\xe7\x17\x50\x50\x9d\xd2\x1c\x66\x64\x95\xca\x02\xc9\xf7\x61\x27\x08\x2a\x4c\x91\x57\x5e\xb2\x7d\x6e\x3f\x0f\x42\x52\xa0\xdd\xdf\x52\xbd\x2a\xb3\x8c\xdf\x77\x02\xd3\x6b\xd1\x19\xfd\x1f\x2a\x69\xe5\xce\xa0\xae\xf7\x7b\xe0\x99\xff\xd2\xbd\xf8\xcd\x0b\x98\x0a\x9e\x4f\xfd\x12\x0a\x66\xbf\x9c\x64\xa5\x48\x21\x1e\x38\x53\xd7\x70\xd2\x87\x51\xd7\x09\x04\xa4\x2b\x5a\x61\x9c\x9a\x7b\x48\xa5\x30\x78\x6f\xc8\xa5\xff\x9f\x58\x15\xdf\xf4\x8c\x3a\x05\xe4\x8a\xee\x82\x07\x98\x6b\xfb\xc4\x85\x69\x6d\xcf\x01\x95\x92\x2a\x81\xfd\x24\x52\xa8\xad\xef\x2f\x83\x19\xf2\x0e\x75\x21\x85\xc6\x7d\x3d\x89\x7e\x2b\x51\x3d\xcc\xe1\x96\x0b\xc6\xc5\xc6\xc9\x8d\xdc\x25\xe1\xb3\x91\x0f\x63\x29\xce\x5a\xdb\x09\xf9\xd9\x6a\x8d\x93\x49\xc4\x33\xeb\xc7\x31\xad\x4c\xd9\x27\xb2\xb8\xc7\xd4\x62\x9e\xc3\xc8\x93\xb9\x65\x68\xf2\x9d\xfb\xfc\x6f\x17\x20\x78\x6e\xa1\x44\x0a\x4d\xa9\x04\xb4\x61\x0f\x48\x27\x51\xdd\x18\x9b\x83\xbc\xb3\x06\xb9\xbe\x94\x42\x1b\x2a\xcc\xc2\x46\x22\xf6\xea\xe4\xdd\x27\xd5\x0c\x71\x4e\x22\xb7\x30\x73\x20\x66\xe4\x5d\x07\xc1\xed\xd8\x8d\xba\x76\xe1\x1d\x24\x25\x95\x22\xe3\x9b\xf3\x03\xd8\x7e\xdd\x7e\x3b\x0a\x8d\xdd\x7c\xab\xe4\xae\x49\x4e\x7c\x14\x7e\xe3\xb8\xe0\x79\x70\xd8\x7a\xdc\x87\xa3\x1c\x16\xc1\x73\x0f\x24\x50\xa3\x93\x51\xa8\xc9\x3b\xa4\x6c\x29\x8c\x4d\x90\x93\xf1\x6c\xfd\x6c\xbe\xc6\x83\x4a\xe0\xcc\xd9\x27\xcb\x37\xe4\xe6\xa1\xc0\x7e\x21\x24\x70\xc2\x74\x4e\x6e\x14\xad\x50\x69\xea\xa0\x58\xc3\x1f\xb9\xd9\x02\xb9\x2a\x77\x2e\x53\x8a\x72\x61\xbc\xaf\xc6\x2a\x48\xbb\x45\x6d\x54\x99\x1a\x1f\x81\x42\x21\x1b\xeb\x3b\x3d\xed\x4b\x5b\x09\x9e\x52\x83\xc4\xca\x1b\xd4\xe6\x88\xbc\x5b\xde\x51\x93\x6e\x51\x03\x15\x0c\xb8\xd1\x5e\x09\x15\x86\x84\xb8\x76\x4a\x5d\x65\xec\xe8\x1d\xc6\xef\x3f\x9c\x74\xcb\x73\x38\x9b\x5b\xd8\xc4\xa2\x1c\x44\xd3\x3d\x9f\x9e\x40\x4a\x35\xda\xc6\xe7\xbb\x18\xe8\x02\x53\x9e\xf1\x14\x2a\x54\x06\xef\xc1\x75\xbf\x43\xca\x55\xd6\xdc\x86\xfc\x1a\x73\x96\xb4\xaa\x36\x28\x50\xd1\xbc\x51\x95\x49\x05\x57\x4e\x0f\x4f\x51\xf7\x34\x75\x39\x6f\xd5\x24\xe4\x07\xaa\x7f\xa4\xb7\x98\xbb\xec\x92\x9f\x68\x7a\x47\x37\x56\x8a\xb8\xd5\x64\x12\x45\x56\xdf\x7a\x0e\x85\xeb\x97\x54\x6c\xf0\x80\xbc\x6d\x60\x75\x48\x45\x5c\x25\x3e\x52\x7d\xe0\x15\x55\x10\xfb\xe2\xe0\x19\x48\x35\xce\x70\x9c\xa3\x80\x19\x59\xb0\x0d\xea\xc4\xfb\x19\xa9\x0a\x2e\xa0\x22\x97\xb9\x14\x68\x69\x19\x45\x6b\xb8\x00\x55\x79\x35\x8d\xe6\xc8\x28\x0d\xef\x3f\x0c\x93\x39\x89\x42\x84\xbc\xcf\xb3\xf5\x1c\x66\x99\x2f\xd6\xb7\x1c\x73\xa6\xbb\x22\xf6\xee\xc4\x42\x1a\x98\x65\x64\xb9\xdb\x95\x86\xde\xe6\x98\xd8\xb7\x5f\x5c\x50\xdf\x60\x46\xcb\x3c\xb0\xd0\x96\x68\x45\xf3\x12\x8f\xf5\x2f\xfb\x9e\x91\x95\x23\xa6\xb3\x03\x75\xfd\x5d\x10\xef\x17\x6c\x9b\xdb\x8c\xfc\x22\xf8\x6f\x65\xc8\x4c\x34\x24\xd7\x05\xd0\xa2\x40\xc1\xe2\xde\xe2\x1c\x5e\x76\x6f\x5e\x97\x67\xff\x79\x97\xd2\xe3\xd9\x9c\xc3\x78\xd9\x7b\xdb\x34\x44\xd7\x22\x4e\x9c\xaf\x09\xb9\x94\xa5\x6d\x05\xf3\x60\xc0\xd6\xc5\x39\xac\xd7\x64\xa9\xe3\x82\x5c\x2d\x7e\x8e\xcf\x92\xa4\xfd\x32\xbe\xc2\x8f\x0b\xa5\x3c\x12\x07\xfb\xcb\x3d\x68\x4c\xd7\x49\x1b\xaf\x36\xe1\x51\x54\x91\x9f\x94\x2c\x50\x99\x87\xd8\xa6\x7d\xc5\xc5\x26\xc7\xcf\x51\x6f\xb5\x38\x55\x5d\x22\x6c\x7f\xb2\xa4\x44\xc5\xd3\xc6\xce\x53\xb9\xa6\x8c\xfd\xee\x74\x3f\x9e\xef\x88\x32\xf6\x6b\x63\x42\xb5\x64\xb7\x62\x52\xc4\xeb\x35\x71\x9b\x87\x29\x3d\x80\x96\xcc\x6d\x7e\xda\x94\x34\x61\x24\xab\x72\x17\x27\xe4\x0a\xef\x8d\x2f\xa1\xe7\x72\xec\x0f\x24\x59\x03\xf9\x80\x66\x7f\x25\xcf\xb2\x9d\x21\xab\x42\x71\x61\xb2\x78\xfa\xf7\x0b\x78\xc1\xa6\x1d\xf9\x5a\x8f\x02\xfd\xc6\xfc\xfb\x02\x02\xae\xd7\x7f\x70\x6e\xbd\x87\x2d\x99\x5b\x2f\xc7\x63\xa7\xff\xdc\xeb\x8a\xe8\xbb\xa2\x6b\xbc\xbd\x93\x8d\x3f\x9f\x73\x29\xec\xf6\x74\x29\xa6\xbd\x3d\x61\x8f\x33\xf6\xa4\xed\xa2\x07\xd3\x17\x9a\xbc\xd0\xd3\x1e\x86\x19\xf6\xbd\xef\xfa\xec\x0c\xc9\x52\x2f\x85\x6d\xd1\x4d\x05\x8c\x8c\x5d\xc0\xf4\xba\x34\xd3\xfe\xa6\xb3\x76\x68\x0c\x7d\xc1\x3e\x69\x72\x10\x8b\xd3\x13\x50\xb8\x93\x15\x02\x3a\xac\x7e\x34\xf6\x5c\xeb\x57\x26\xcf\x0e\x0f\x69\x39\x52\x85\xb6\xe6\x9b\x1b\x06\x36\x07\x3b\x57\xe5\xc3\x21\x6b\xe7\x26\x67\x8f\x4f\x4d\xef\xca\x27\xb4\xf5\xdd\xf7\x3e\xae\x30\xcf\xde\x61\x16\xe2\x63\xd4\xa8\x6b\x7c\x2f\xcd\x76\xe1\xf8\x24\xbc\xae\xc4\xcf\x5b\x37\xdc\x7a\x08\xc9\x7f\xb7\xa8\xd0\x52\xf0\x5a\xd9\xbf\x4b\x11\xaa\x7a\xf9\xc6\x1e\x2e\x1c\xdd\xae\x4b\x33\x58\x4c\x92\x76\xe8\x92\x37\x4a\x16\x71\x42\x96\x06\x15\x35\x7e\x36\xb7\xf0\x8f\xe7\xf9\xc0\xd5\xa5\xf8\x4c\x47\xcd\x16\xd5\xd0\xa1\xdf\xe7\xcf\x23\xf6\xaf\x4b\xf3\x17\x38\xd0\xb6\x0c\x7b\x48\x69\x7b\xad\x51\x7a\x0e\x46\x85\x63\x52\xc3\xce\x70\x82\x1b\xb0\xf3\x53\x34\xb2\xef\x38\x9a\x40\x2d\x7f\x8e\x67\xa2\x22\xaf\x19\x1b\x42\x77\x77\x8c\x38\x9c\x2c\x13\xcf\x86\xc3\x10\x1e\xfb\xf0\x46\x76\x9f\x79\xc2\x3c\xce\xdd\x1f\xa8\x1e\x1f\xe9\x1f\x65\xf6\xb3\x66\x94\x9f\x50\xa3\x72\x18\xfa\x3b\x1c\x38\x9f\x31\x6e\x6c\x7f\x7c\x6a\xda\x04\x0b\x73\xb0\xa1\xf0\xea\xc3\xec\x78\x3e\x92\x0d\x59\x8c\xcf\xe8\x2d\x90\x67\x15\xf0\x57\x80\x3f\xe2\xd0\x9f\x14\x0d\xfb\xd2\x0d\x91\xba\x1e\xe0\xfe\x5a\xa8\x8f\x0d\xe2\xf1\xb5\x68\x70\x59\xae\xfc\x81\xe0\x3f\xb4\x88\x8d\x2a\x31\xe9\x7e\xc4\xa9\x1a\x0c\xbd\x7b\xcf\x93\x77\x65\x9e\x41\x8e\xa2\x1f\xd8\x04\xfe\x09\x67\xfe\x38\x1a\xfa\x8d\xbd\xb8\x82\x2e\x15\xba\x5f\xe5\x4c\x7b\x0f\x66\x12\xb5\xeb\x83\xa9\x14\x86\x72\x01\x3b\xe9\x64\xa8\x00\xeb\x67\xb8\xa3\xf2\x0c\x3e\x22\x6c\x69\x35\xb8\x93\x87\xb6\xd5\xd4\xb5\xeb\xa6\xed\xfd\xf5\x4b\xab\xfa\x89\x34\xfe\xeb\x26\x7e\xd5\xcf\xe2\xcb\x85\x52\x5d\x4c\xde\x52\x9e\x23\xdb\xef\xf4\xe6\x1c\xa6\xa1\xcd\x76\x78\x03\x4c\x7d\x14\xe7\xb4\x7e\x3c\xb1\x91\xbd\x9f\xf6\xbc\x7f\x7f\xf6\xc1\xdd\x86\xc9\xa5\xa4\x39\xea\x14\xe3\xd1\xa6\xf5\x79\x0e\xee\x7a\xdc\x5c\xac\x53\xd5\x35\xf7\xbe\xf4\xab\xf3\x0f\xe1\xee\xe0\x8c\xa8\xb1\x62\x35\x50\x76\x84\x59\x87\x03\xc7\x8a\x86\xdf\x7b\xec\x99\xf5\xdf\x92\x0b\xbb\x41\x08\x49\x26\xee\x07\xcd\xf0\xe9\xff\x03\x00\x00\xff\xff\x56\xb4\x6d\x62\x3a\x16\x00\x00") +var _templateDialectGremlinUpdateTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x58\x5f\x73\xdb\x36\x12\x7f\xa6\x3e\xc5\x9e\x46\xc9\x90\x3e\x16\x72\xfa\x76\xee\xe8\x66\x52\x47\xb9\xea\xa6\x67\xb7\x91\xdb\x7b\xc8\x64\x34\x30\xb9\x94\x30\xa6\x00\x16\x00\x19\xfb\x34\xfc\xee\x37\xf8\xc3\xbf\x92\x9d\xc6\x69\x9b\x17\x9b\x04\x96\xbb\xfb\xdb\xfd\xed\x2e\xa0\xc3\x61\x7e\x36\xb9\x14\xc5\x83\x64\xdb\x9d\x86\x6f\xcf\x5f\xfd\xe3\x9b\x42\xa2\x42\xae\xe1\x2d\x4d\xf0\x56\x88\x3b\x58\xf1\x84\xc0\xeb\x3c\x07\x2b\xa4\xc0\xec\xcb\x0a\x53\x32\xb9\xd9\x31\x05\x4a\x94\x32\x41\x48\x44\x8a\xc0\x14\xe4\x2c\x41\xae\x30\x85\x92\xa7\x28\x41\xef\x10\x5e\x17\x34\xd9\x21\x7c\x4b\xce\x9b\x5d\xc8\x44\xc9\xd3\x09\xe3\x76\xff\xc7\xd5\xe5\xf2\x6a\xbd\x84\x8c\xe5\x08\x7e\x4d\x0a\xa1\x21\x65\x12\x13\x2d\xe4\x03\x88\x0c\x74\xcf\x98\x96\x88\x64\x72\x36\xaf\xeb\xc9\xe4\x70\x80\x14\x33\xc6\x11\xa6\x29\xa3\x39\x26\x7a\xbe\x95\xb8\xcf\x19\x9f\x97\x45\x4a\x35\x4e\xa1\xae\x8d\xd4\xec\xb6\x64\xb9\xf1\xe9\x62\x01\x05\x55\x09\xcd\x61\x46\xd6\x89\x28\x90\x7c\xef\x77\xbc\xa0\xc4\x04\x59\xe5\x24\xdb\xe7\xf6\x73\x2f\x24\x38\x9a\xfd\x1d\x55\xeb\x32\xcb\xd8\x7d\x27\x30\xbd\xe6\x9d\xd1\xff\xa1\x14\x46\xee\x1c\xea\xfa\x70\x00\x96\xb9\x2f\xed\x8b\xdb\x5c\xc0\x94\xb3\x7c\xea\x96\x90\xa7\xe6\xcb\x49\x56\xf2\x04\xc2\x81\x33\x75\x0d\x67\x7d\x18\x75\x1d\x81\x47\xba\xa6\x15\x86\x89\xbe\x87\x44\x70\x8d\xf7\x9a\x5c\xba\xff\x91\x51\xf1\x4d\xcf\xa8\x55\x40\xae\xe8\xde\x7b\x80\xb9\x32\x4f\x8c\xeb\xd6\x76\x0c\x28\xa5\x90\x11\x1c\x26\x81\x44\x65\x7c\x7f\xe9\xcd\x90\x77\xa8\x0a\xc1\x15\x1e\xea\x49\xf0\x5b\x89\xf2\x21\x86\x5b\xc6\x53\xc6\xb7\x56\x6e\xe4\x2e\xf1\x9f\x8d\x7c\x18\x4b\xb1\xb4\xb5\x1d\x91\x9f\x8d\xd6\x30\x9a\x04\x2c\x33\x7e\x9c\xd2\x9a\x4a\xf3\x44\x96\xf7\x98\x18\xcc\x31\x8c\x3c\x89\x0d\x43\xa3\xef\xec\xe7\x7f\x5b\x00\x67\xb9\x81\x12\x48\xd4\xa5\xe4\xd0\x86\xdd\x23\x9d\x04\x75\x63\x2c\x06\x71\x67\x0c\x32\x75\x29\xb8\xd2\x94\xeb\xa5\x89\x44\xe8\xd4\x89\xbb\x4f\xaa\x19\xe2\x9c\x04\x76\x61\x66\x41\xcc\xc8\xbb\x0e\x82\xdd\x31\x1b\x75\x6d\xc3\x3b\x48\x4a\x22\x78\xc6\xb6\x17\x47\xb0\xdd\xba\xf9\x76\x14\x1a\xb3\xf9\x56\x8a\x7d\x93\x9c\xf0\x24\xfc\xc6\x71\xce\x72\xef\xb0\xf1\xb8\x0f\x47\x5a\x2c\x9c\xe5\x0e\x88\xa7\x46\x27\x23\x51\x91\x77\x48\xd3\x15\xd7\x26\x41\x56\xc6\xb1\xf5\xb3\xf9\x1a\x0e\x2a\x81\xa5\xd6\x3e\x59\xbd\x21\x37\x0f\x05\xf6\x0b\x21\x82\xb3\x54\xe5\xe4\x46\xd2\x0a\xa5\xa2\x16\x8a\x31\xfc\x91\xe9\x1d\x90\xab\x72\x6f\x33\x25\x29\xe3\xda\xf9\xaa\x8d\x82\xa4\x5b\x54\x5a\x96\x89\x76\x11\x28\x24\xa6\x63\x7d\xf3\x79\x5f\xda\x48\xb0\x84\x6a\x24\x46\x5e\xa3\xd2\x27\xe4\xed\xf2\x9e\xea\x64\x87\x0a\x28\x4f\x81\x69\xe5\x94\x50\xae\x89\x8f\x6b\xa7\xd4\x56\xc6\x9e\xde\x61\xf8\xfe\xc3\x59\xb7\x1c\xc3\x79\x6c\x60\x13\x83\x72\x10\x4d\xfb\x3c\x3f\x83\x84\x2a\x34\x8d\xcf\x75\x31\x50\x05\x26\x2c\x63\x09\x54\x28\x35\xde\x83\xed\x7e\xc7\x94\xab\x8c\xb9\x2d\xf9\x35\x64\x69\xd4\xaa\xda\x22\x47\x49\xf3\x46\x55\x26\x24\x5c\x59\x3d\x2c\x41\xd5\xd3\xd4\xe5\xbc\x55\x13\x91\x1f\xa8\xfa\x91\xde\x62\x6e\xb3\x4b\x7e\xa2\xc9\x1d\xdd\x1a\x29\x62\x57\xa3\x49\x10\x18\x7d\x9b\x18\x0a\xdb\x2f\x29\xdf\xe2\x11\x79\xdb\xc0\x2a\x9f\x8a\xb0\x8a\x5c\xa4\xfa\xc0\x2b\x2a\x21\x74\xc5\xc1\x32\x10\x72\x9c\xe1\x30\x47\x0e\x33\xb2\x4c\xb7\xa8\x22\xe7\x67\x20\x2b\x58\x40\x45\x2e\x73\xc1\xd1\xd0\x32\x08\x36\xb0\x00\x59\x39\x35\x8d\xe6\x40\x4b\x05\xef\x3f\x0c\x93\x39\x09\x7c\x84\x9c\xcf\xb3\x4d\x0c\xb3\xcc\x15\xeb\x5b\x86\x79\xaa\xba\x22\x76\xee\x84\x5c\x68\x98\x65\x64\xb5\xdf\x97\x9a\xde\xe6\x18\x99\xb7\x5f\x6c\x50\xdf\x60\x46\xcb\xdc\xb3\xd0\x94\x68\x45\xf3\x12\x4f\xf5\x2f\xf3\x9e\x91\xb5\x25\xa6\xb5\x03\x75\xfd\x9d\x17\xef\x17\x6c\x9b\xdb\x8c\xfc\xc2\xd9\x6f\xa5\xcf\x4c\x30\x24\xd7\x02\x68\x51\x20\x4f\xc3\xde\x62\x0c\x2f\xbb\x37\xa7\xcb\xb1\xff\xa2\x4b\xe9\xe9\x6c\xc6\x30\x5e\x76\xde\x36\x0d\xd1\xb6\x88\x33\xeb\x6b\x44\x2e\x45\x69\x5a\x41\xec\x0d\x98\xba\xb8\x80\xcd\x86\xac\x54\x58\x90\xab\xe5\xcf\xe1\x79\x14\xb5\x5f\x86\x57\xf8\x71\x29\xa5\x43\x62\x61\x7f\xb9\x07\x8d\xe9\x3a\x6a\xe3\xd5\x26\x3c\x08\x2a\xf2\x93\x14\x05\x4a\xfd\x10\x9a\xb4\xaf\x19\xdf\xe6\xf8\x39\xea\x8d\x16\xab\xaa\x4b\x84\xe9\x4f\x86\x94\x28\x59\xd2\xd8\x79\x2a\xd7\x34\x4d\x7f\x77\xba\x1f\xcf\x77\x40\xd3\xf4\xd7\xc6\x84\x6c\xc9\x6e\xc4\x04\x0f\x37\x1b\x62\x37\x8f\x53\x7a\x04\x2d\x8a\x4d\x7e\xda\x94\x34\x61\x24\xeb\x72\x1f\x46\xe4\x0a\xef\xb5\x2b\xa1\xe7\x72\xec\x0f\x24\x59\x03\xf9\x88\x66\x7f\x25\xcf\xb2\xbd\x26\xeb\x42\x32\xae\xb3\x70\xfa\xf7\x05\xbc\xa8\xa6\x1d\xf9\x5a\x8f\x3c\xfd\xc6\xfc\xfb\x02\x02\x6e\x36\x7f\x70\x6e\x9d\x87\x2d\x99\x5b\x2f\xc7\x63\xa7\xff\xdc\xeb\x8a\xe8\xba\xa2\x6d\xbc\xbd\x93\x8d\x3b\x9f\x33\xc1\xcd\xf6\x74\xc5\xa7\xbd\x3d\x6e\x8e\x33\xe6\xa4\x6d\xa3\x07\xd3\x17\x8a\xbc\x50\xd3\x1e\x86\x19\xf6\xbd\xef\xfa\xec\x0c\xc9\x4a\xad\xb8\x69\xd1\x4d\x05\x8c\x8c\x2d\x60\x7a\x5d\xea\x69\x7f\xd3\x5a\x3b\x36\x86\xae\x60\x9f\x34\x39\x88\xc5\xfc\x0c\x24\xee\x45\x85\x80\x16\xab\x1b\x8d\x3d\xd7\xfa\x95\xc9\xb2\xe3\x43\x5a\x8e\x54\xa2\xa9\xf9\xe6\x86\x81\xcd\xc1\xce\x56\xf9\x70\xc8\x9a\xb9\xc9\xd2\xc7\xa7\xa6\x73\xe5\x13\xda\xfa\xee\x3b\x1f\xd7\x98\x67\xef\x30\xf3\xf1\xd1\x72\xd4\x35\xbe\x17\x7a\xb7\xb4\x7c\xe2\x4e\x57\xe4\xe6\xad\x1d\x6e\x3d\x84\xe4\xbf\x3b\x94\x68\x28\x78\x2d\xcd\xdf\x15\xf7\x55\xbd\x7a\x63\x0e\x17\x96\x6e\xd7\xa5\x1e\x2c\x46\x51\x3b\x74\xc9\x1b\x29\x8a\x30\x22\x2b\x8d\x92\x6a\x37\x9b\x5b\xf8\xa7\xf3\x7c\xe4\xea\x8a\x7f\xa6\xa3\x7a\x87\x72\xe8\xd0\xef\xf3\xe7\x11\xfb\xd7\xa5\xfe\x0b\x1c\x68\x5b\x86\x39\xa4\xb4\xbd\x56\x4b\x15\x83\x96\xfe\x98\xd4\xb0\xd3\x9f\xe0\x06\xec\xfc\x14\x8d\xcc\x3b\x8e\x26\x50\xcb\x9f\xd3\x99\xa8\xc8\xeb\x34\x1d\x42\xb7\x77\x8c\xd0\x9f\x2c\x23\xc7\x86\xe3\x10\x9e\xfa\xf0\x46\x74\x9f\x39\xc2\x3c\xce\xdd\x1f\xa8\x1a\x1f\xe9\x1f\x65\xf6\xb3\x66\x94\x9b\x50\xa3\x72\x18\xfa\x3b\x1c\x38\x9f\x31\x6e\x4c\x7f\x7c\x6a\xda\x78\x0b\x31\x98\x50\x38\xf5\x7e\x76\x3c\x1f\xc9\x96\x2c\xc7\x67\xf4\x16\xc8\xb3\x0a\xf8\x2b\xc0\x1f\x71\xe8\x4f\x8a\x86\x79\xe9\x86\x48\x5d\x0f\x70\x7f\x2d\xd4\xa7\x06\xf1\xf8\x5a\x34\xb8\x2c\x57\xee\x40\xf0\x1f\x5a\x84\x5a\x96\x18\x75\x3f\xe2\x54\x0d\x86\xde\xbd\xe7\xc9\xbb\x32\xcb\x20\x47\xde\x0f\x6c\x04\xff\x84\x73\x77\x1c\xf5\xfd\xc6\x5c\x5c\x41\x95\x12\xed\xaf\x72\xba\xbd\x07\xa7\x02\x95\xed\x83\x89\xe0\x9a\x32\x0e\x7b\x61\x65\x28\x07\xe3\xa7\xbf\xa3\xb2\x0c\x3e\x22\xec\x68\x35\xb8\x93\xfb\xb6\xd5\xd4\xb5\xed\xa6\xed\xfd\xf5\x4b\xab\xfa\x89\x34\xfe\xeb\x26\x7c\xd5\xcf\xe2\xcb\xa5\x94\x5d\x4c\xde\x52\x96\x63\x7a\xd8\xab\xed\x05\x4c\x7d\x9b\xed\xf0\x7a\x98\xea\x24\xce\x69\xfd\x78\x62\x03\x73\x3f\xed\x79\xff\xfe\xfc\x83\xbd\x0d\x93\x4b\x41\x73\x54\x09\x86\xa3\x4d\xe3\x73\x0c\xf6\x7a\xdc\x5c\xac\x13\xd9\x35\xf7\xbe\xf4\xab\x8b\x0f\xfe\xee\x60\x8d\xc8\xb1\x62\x39\x50\x76\x82\x59\xc7\x03\xc7\x88\xfa\xdf\x7b\xcc\x99\xf5\xdf\x82\x71\xb3\x41\x08\x89\x26\xf6\x07\x4d\xff\xe9\xff\x03\x00\x00\xff\xff\x28\x1a\x18\x16\x3a\x16\x00\x00") func templateDialectGremlinUpdateTmplBytes() ([]byte, error) { return bindataRead( @@ -501,7 +501,7 @@ func templateDialectGremlinUpdateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/gremlin/update.tmpl", size: 5690, mode: os.FileMode(420), modTime: time.Unix(1568038024, 0)} + info := bindataFileInfo{name: "template/dialect/gremlin/update.tmpl", size: 5690, mode: os.FileMode(420), modTime: time.Unix(1568127836, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -721,7 +721,7 @@ func templateDialectSqlUpdateTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/update.tmpl", size: 12115, mode: os.FileMode(420), modTime: time.Unix(1568035617, 0)} + info := bindataFileInfo{name: "template/dialect/sql/update.tmpl", size: 12115, mode: os.FileMode(420), modTime: time.Unix(1568122710, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/entc/gen/template/dialect/gremlin/update.tmpl b/entc/gen/template/dialect/gremlin/update.tmpl index 482894560..c5e86974f 100644 --- a/entc/gen/template/dialect/gremlin/update.tmpl +++ b/entc/gen/template/dialect/gremlin/update.tmpl @@ -73,7 +73,7 @@ func ({{ $receiver }} *{{ $builder }}) gremlin({{ if $one }}id {{ $.ID.Type }}{{ addValue := rv.Clone().Union(__.Values({{ $.Package }}.{{ $f.Constant }}), __.Constant(*value)).Sum().Next() constraints = append(constraints, &constraint{ pred: g.V().Has({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, addValue).Count(), - test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, fmt.Sprintf("+= %d", *value))), + test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, fmt.Sprintf("+= %v", *value))), }) {{- end }} v.Property(dsl.Single, {{ $.Package }}.{{ $f.Constant }}, __.Union(__.Values({{ $.Package }}.{{ $f.Constant }}), __.Constant(*value)).Sum()) diff --git a/entc/integration/ent/comment_update.go b/entc/integration/ent/comment_update.go index f3561d480..681741b95 100644 --- a/entc/integration/ent/comment_update.go +++ b/entc/integration/ent/comment_update.go @@ -226,7 +226,7 @@ func (cu *CommentUpdate) gremlin() *dsl.Traversal { addValue := rv.Clone().Union(__.Values(comment.FieldUniqueInt), __.Constant(*value)).Sum().Next() constraints = append(constraints, &constraint{ pred: g.V().Has(comment.Label, comment.FieldUniqueInt, addValue).Count(), - test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueInt, fmt.Sprintf("+= %d", *value))), + test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueInt, fmt.Sprintf("+= %v", *value))), }) v.Property(dsl.Single, comment.FieldUniqueInt, __.Union(__.Values(comment.FieldUniqueInt), __.Constant(*value)).Sum()) } @@ -241,7 +241,7 @@ func (cu *CommentUpdate) gremlin() *dsl.Traversal { addValue := rv.Clone().Union(__.Values(comment.FieldUniqueFloat), __.Constant(*value)).Sum().Next() constraints = append(constraints, &constraint{ pred: g.V().Has(comment.Label, comment.FieldUniqueFloat, addValue).Count(), - test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueFloat, fmt.Sprintf("+= %d", *value))), + test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueFloat, fmt.Sprintf("+= %v", *value))), }) v.Property(dsl.Single, comment.FieldUniqueFloat, __.Union(__.Values(comment.FieldUniqueFloat), __.Constant(*value)).Sum()) } @@ -477,7 +477,7 @@ func (cuo *CommentUpdateOne) gremlin(id string) *dsl.Traversal { addValue := rv.Clone().Union(__.Values(comment.FieldUniqueInt), __.Constant(*value)).Sum().Next() constraints = append(constraints, &constraint{ pred: g.V().Has(comment.Label, comment.FieldUniqueInt, addValue).Count(), - test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueInt, fmt.Sprintf("+= %d", *value))), + test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueInt, fmt.Sprintf("+= %v", *value))), }) v.Property(dsl.Single, comment.FieldUniqueInt, __.Union(__.Values(comment.FieldUniqueInt), __.Constant(*value)).Sum()) } @@ -492,7 +492,7 @@ func (cuo *CommentUpdateOne) gremlin(id string) *dsl.Traversal { addValue := rv.Clone().Union(__.Values(comment.FieldUniqueFloat), __.Constant(*value)).Sum().Next() constraints = append(constraints, &constraint{ pred: g.V().Has(comment.Label, comment.FieldUniqueFloat, addValue).Count(), - test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueFloat, fmt.Sprintf("+= %d", *value))), + test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField(comment.Label, comment.FieldUniqueFloat, fmt.Sprintf("+= %v", *value))), }) v.Property(dsl.Single, comment.FieldUniqueFloat, __.Union(__.Values(comment.FieldUniqueFloat), __.Constant(*value)).Sum()) } diff --git a/entc/integration/migrate/entv1/example_test.go b/entc/integration/migrate/entv1/example_test.go index 63fa13205..4b017c565 100644 --- a/entc/integration/migrate/entv1/example_test.go +++ b/entc/integration/migrate/entv1/example_test.go @@ -38,6 +38,7 @@ func ExampleUser() { SetAge(1). SetName("string"). SetAddress("string"). + SetBlob([]byte{}). SaveX(ctx) log.Println("user created:", u) diff --git a/entc/integration/migrate/entv1/migrate/schema.go b/entc/integration/migrate/entv1/migrate/schema.go index e1e0570e3..86b38a7e8 100644 --- a/entc/integration/migrate/entv1/migrate/schema.go +++ b/entc/integration/migrate/entv1/migrate/schema.go @@ -18,6 +18,7 @@ var ( {Name: "age", Type: field.TypeInt32}, {Name: "name", Type: field.TypeString, Size: 10}, {Name: "address", Type: field.TypeString, Nullable: true}, + {Name: "blob", Type: field.TypeBytes, Nullable: true, Size: 255}, } // UsersTable holds the schema information for the "users" table. UsersTable = &schema.Table{ diff --git a/entc/integration/migrate/entv1/schema/user.go b/entc/integration/migrate/entv1/schema/user.go index c64d57a1e..7e69f7bf8 100644 --- a/entc/integration/migrate/entv1/schema/user.go +++ b/entc/integration/migrate/entv1/schema/user.go @@ -21,6 +21,9 @@ func (User) Fields() []ent.Field { field.Int32("age"), field.String("name").MaxLen(10), field.String("address").Optional(), + field.Bytes("blob"). + Optional(). + MaxLen(255), } } diff --git a/entc/integration/migrate/entv1/user.go b/entc/integration/migrate/entv1/user.go index 5a355b07e..8ed0140fd 100644 --- a/entc/integration/migrate/entv1/user.go +++ b/entc/integration/migrate/entv1/user.go @@ -24,6 +24,8 @@ type User struct { Name string `json:"name,omitempty"` // Address holds the value of the "address" field. Address string `json:"address,omitempty"` + // Blob holds the value of the "blob" field. + Blob []byte `json:"blob,omitempty"` } // FromRows scans the sql response data into User. @@ -33,6 +35,7 @@ func (u *User) FromRows(rows *sql.Rows) error { Age sql.NullInt64 Name sql.NullString Address sql.NullString + Blob []byte } // the order here should be the same as in the `user.Columns`. if err := rows.Scan( @@ -40,6 +43,7 @@ func (u *User) FromRows(rows *sql.Rows) error { &vu.Age, &vu.Name, &vu.Address, + &vu.Blob, ); err != nil { return err } @@ -47,6 +51,7 @@ func (u *User) FromRows(rows *sql.Rows) error { u.Age = int32(vu.Age.Int64) u.Name = vu.Name.String u.Address = vu.Address.String + u.Blob = vu.Blob return nil } @@ -76,6 +81,7 @@ func (u *User) String() string { buf.WriteString(fmt.Sprintf(", age=%v", u.Age)) buf.WriteString(fmt.Sprintf(", name=%v", u.Name)) buf.WriteString(fmt.Sprintf(", address=%v", u.Address)) + buf.WriteString(fmt.Sprintf(", blob=%v", u.Blob)) buf.WriteString(")") return buf.String() } diff --git a/entc/integration/migrate/entv1/user/user.go b/entc/integration/migrate/entv1/user/user.go index 6671de338..be9df46bb 100644 --- a/entc/integration/migrate/entv1/user/user.go +++ b/entc/integration/migrate/entv1/user/user.go @@ -21,6 +21,8 @@ const ( FieldName = "name" // FieldAddress holds the string denoting the address vertex property in the database. FieldAddress = "address" + // FieldBlob holds the string denoting the blob vertex property in the database. + FieldBlob = "blob" // Table holds the table name of the user in the database. Table = "users" @@ -32,6 +34,7 @@ var Columns = []string{ FieldAge, FieldName, FieldAddress, + FieldBlob, } var ( diff --git a/entc/integration/migrate/entv1/user/where.go b/entc/integration/migrate/entv1/user/where.go index e02521b6e..a6c9497d5 100644 --- a/entc/integration/migrate/entv1/user/where.go +++ b/entc/integration/migrate/entv1/user/where.go @@ -140,6 +140,15 @@ func Address(v string) predicate.User { ) } +// Blob applies equality check predicate on the "blob" field. It's identical to BlobEQ. +func Blob(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldBlob), v)) + }, + ) +} + // AgeEQ applies the EQ predicate on the "age" field. func AgeEQ(v int32) predicate.User { return predicate.User( @@ -524,6 +533,116 @@ func AddressContainsFold(v string) predicate.User { ) } +// BlobEQ applies the EQ predicate on the "blob" field. +func BlobEQ(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldBlob), v)) + }, + ) +} + +// BlobNEQ applies the NEQ predicate on the "blob" field. +func BlobNEQ(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldBlob), v)) + }, + ) +} + +// BlobGT applies the GT predicate on the "blob" field. +func BlobGT(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldBlob), v)) + }, + ) +} + +// BlobGTE applies the GTE predicate on the "blob" field. +func BlobGTE(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldBlob), v)) + }, + ) +} + +// BlobLT applies the LT predicate on the "blob" field. +func BlobLT(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldBlob), v)) + }, + ) +} + +// BlobLTE applies the LTE predicate on the "blob" field. +func BlobLTE(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldBlob), v)) + }, + ) +} + +// BlobIn applies the In predicate on the "blob" field. +func BlobIn(vs ...[]byte) predicate.User { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.User( + func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(vs) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldBlob), v...)) + }, + ) +} + +// BlobNotIn applies the NotIn predicate on the "blob" field. +func BlobNotIn(vs ...[]byte) predicate.User { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.User( + func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(vs) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldBlob), v...)) + }, + ) +} + +// BlobIsNil applies the IsNil predicate on the "blob" field. +func BlobIsNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldBlob))) + }, + ) +} + +// BlobNotNil applies the NotNil predicate on the "blob" field. +func BlobNotNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldBlob))) + }, + ) +} + // And groups list of predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User( diff --git a/entc/integration/migrate/entv1/user_create.go b/entc/integration/migrate/entv1/user_create.go index 8848a1137..e2f40a053 100644 --- a/entc/integration/migrate/entv1/user_create.go +++ b/entc/integration/migrate/entv1/user_create.go @@ -22,6 +22,7 @@ type UserCreate struct { age *int32 name *string address *string + blob *[]byte } // SetAge sets the age field. @@ -50,6 +51,12 @@ func (uc *UserCreate) SetNillableAddress(s *string) *UserCreate { return uc } +// SetBlob sets the blob field. +func (uc *UserCreate) SetBlob(b []byte) *UserCreate { + uc.blob = &b + return uc +} + // Save creates the User in the database. func (uc *UserCreate) Save(ctx context.Context) (*User, error) { if uc.age == nil { @@ -95,6 +102,10 @@ func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { builder.Set(user.FieldAddress, *uc.address) u.Address = *uc.address } + if uc.blob != nil { + builder.Set(user.FieldBlob, *uc.blob) + u.Blob = *uc.blob + } query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { return nil, rollback(tx, err) diff --git a/entc/integration/migrate/entv1/user_update.go b/entc/integration/migrate/entv1/user_update.go index 5797eb01d..9bd16cd9f 100644 --- a/entc/integration/migrate/entv1/user_update.go +++ b/entc/integration/migrate/entv1/user_update.go @@ -22,6 +22,7 @@ type UserUpdate struct { age *int32 name *string address *string + blob *[]byte predicates []predicate.User } @@ -57,6 +58,12 @@ func (uu *UserUpdate) SetNillableAddress(s *string) *UserUpdate { return uu } +// SetBlob sets the blob field. +func (uu *UserUpdate) SetBlob(b []byte) *UserUpdate { + uu.blob = &b + return uu +} + // Save executes the query and returns the number of rows/vertices matched by this operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { if uu.name != nil { @@ -133,6 +140,10 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { update = true builder.Set(user.FieldAddress, *value) } + if value := uu.blob; value != nil { + update = true + builder.Set(user.FieldBlob, *value) + } if update { query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { @@ -152,6 +163,7 @@ type UserUpdateOne struct { age *int32 name *string address *string + blob *[]byte } // SetAge sets the age field. @@ -180,6 +192,12 @@ func (uuo *UserUpdateOne) SetNillableAddress(s *string) *UserUpdateOne { return uuo } +// SetBlob sets the blob field. +func (uuo *UserUpdateOne) SetBlob(b []byte) *UserUpdateOne { + uuo.blob = &b + return uuo +} + // Save executes the query and returns the updated entity. func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { if uuo.name != nil { @@ -262,6 +280,11 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (u *User, err error) { builder.Set(user.FieldAddress, *value) u.Address = *value } + if value := uuo.blob; value != nil { + update = true + builder.Set(user.FieldBlob, *value) + u.Blob = *value + } if update { query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { diff --git a/entc/integration/migrate/entv2/example_test.go b/entc/integration/migrate/entv2/example_test.go index fa94acd13..c27dee3fd 100644 --- a/entc/integration/migrate/entv2/example_test.go +++ b/entc/integration/migrate/entv2/example_test.go @@ -86,6 +86,7 @@ func ExampleUser() { SetPhone("string"). SetBuffer([]byte{}). SetTitle("string"). + SetBlob([]byte{}). SaveX(ctx) log.Println("user created:", u) diff --git a/entc/integration/migrate/entv2/migrate/schema.go b/entc/integration/migrate/entv2/migrate/schema.go index f9ba2ea81..7715901a9 100644 --- a/entc/integration/migrate/entv2/migrate/schema.go +++ b/entc/integration/migrate/entv2/migrate/schema.go @@ -43,6 +43,7 @@ var ( {Name: "phone", Type: field.TypeString}, {Name: "buffer", Type: field.TypeBytes, Default: user.DefaultBuffer}, {Name: "title", Type: field.TypeString, Default: user.DefaultTitle}, + {Name: "blob", Type: field.TypeBytes, Nullable: true, Size: 1000}, } // UsersTable holds the schema information for the "users" table. UsersTable = &schema.Table{ diff --git a/entc/integration/migrate/entv2/schema/user.go b/entc/integration/migrate/entv2/schema/user.go index 28ebb8083..81bd94758 100644 --- a/entc/integration/migrate/entv2/schema/user.go +++ b/entc/integration/migrate/entv2/schema/user.go @@ -31,6 +31,10 @@ func (User) Fields() []ent.Field { // all existing rows. field.String("title"). Default("SWE"), + // extending the blob size. + field.Bytes("blob"). + Optional(). + MaxLen(1000), // deleting the `address` column. } } diff --git a/entc/integration/migrate/entv2/user.go b/entc/integration/migrate/entv2/user.go index 2c2338190..9b721539b 100644 --- a/entc/integration/migrate/entv2/user.go +++ b/entc/integration/migrate/entv2/user.go @@ -28,6 +28,8 @@ type User struct { Buffer []byte `json:"buffer,omitempty"` // Title holds the value of the "title" field. Title string `json:"title,omitempty"` + // Blob holds the value of the "blob" field. + Blob []byte `json:"blob,omitempty"` } // FromRows scans the sql response data into User. @@ -39,6 +41,7 @@ func (u *User) FromRows(rows *sql.Rows) error { Phone sql.NullString Buffer []byte Title sql.NullString + Blob []byte } // the order here should be the same as in the `user.Columns`. if err := rows.Scan( @@ -48,6 +51,7 @@ func (u *User) FromRows(rows *sql.Rows) error { &vu.Phone, &vu.Buffer, &vu.Title, + &vu.Blob, ); err != nil { return err } @@ -57,6 +61,7 @@ func (u *User) FromRows(rows *sql.Rows) error { u.Phone = vu.Phone.String u.Buffer = vu.Buffer u.Title = vu.Title.String + u.Blob = vu.Blob return nil } @@ -88,6 +93,7 @@ func (u *User) String() string { buf.WriteString(fmt.Sprintf(", phone=%v", u.Phone)) buf.WriteString(fmt.Sprintf(", buffer=%v", u.Buffer)) buf.WriteString(fmt.Sprintf(", title=%v", u.Title)) + buf.WriteString(fmt.Sprintf(", blob=%v", u.Blob)) buf.WriteString(")") return buf.String() } diff --git a/entc/integration/migrate/entv2/user/user.go b/entc/integration/migrate/entv2/user/user.go index 93f0eead3..82c303e51 100644 --- a/entc/integration/migrate/entv2/user/user.go +++ b/entc/integration/migrate/entv2/user/user.go @@ -25,6 +25,8 @@ const ( FieldBuffer = "buffer" // FieldTitle holds the string denoting the title vertex property in the database. FieldTitle = "title" + // FieldBlob holds the string denoting the blob vertex property in the database. + FieldBlob = "blob" // Table holds the table name of the user in the database. Table = "users" @@ -38,6 +40,7 @@ var Columns = []string{ FieldPhone, FieldBuffer, FieldTitle, + FieldBlob, } var ( diff --git a/entc/integration/migrate/entv2/user/where.go b/entc/integration/migrate/entv2/user/where.go index cd63ebde0..352342336 100644 --- a/entc/integration/migrate/entv2/user/where.go +++ b/entc/integration/migrate/entv2/user/where.go @@ -158,6 +158,15 @@ func Title(v string) predicate.User { ) } +// Blob applies equality check predicate on the "blob" field. It's identical to BlobEQ. +func Blob(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldBlob), v)) + }, + ) +} + // AgeEQ applies the EQ predicate on the "age" field. func AgeEQ(v int) predicate.User { return predicate.User( @@ -753,6 +762,116 @@ func TitleContainsFold(v string) predicate.User { ) } +// BlobEQ applies the EQ predicate on the "blob" field. +func BlobEQ(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldBlob), v)) + }, + ) +} + +// BlobNEQ applies the NEQ predicate on the "blob" field. +func BlobNEQ(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldBlob), v)) + }, + ) +} + +// BlobGT applies the GT predicate on the "blob" field. +func BlobGT(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldBlob), v)) + }, + ) +} + +// BlobGTE applies the GTE predicate on the "blob" field. +func BlobGTE(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldBlob), v)) + }, + ) +} + +// BlobLT applies the LT predicate on the "blob" field. +func BlobLT(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldBlob), v)) + }, + ) +} + +// BlobLTE applies the LTE predicate on the "blob" field. +func BlobLTE(v []byte) predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldBlob), v)) + }, + ) +} + +// BlobIn applies the In predicate on the "blob" field. +func BlobIn(vs ...[]byte) predicate.User { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.User( + func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(vs) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldBlob), v...)) + }, + ) +} + +// BlobNotIn applies the NotIn predicate on the "blob" field. +func BlobNotIn(vs ...[]byte) predicate.User { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.User( + func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(vs) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldBlob), v...)) + }, + ) +} + +// BlobIsNil applies the IsNil predicate on the "blob" field. +func BlobIsNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldBlob))) + }, + ) +} + +// BlobNotNil applies the NotNil predicate on the "blob" field. +func BlobNotNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldBlob))) + }, + ) +} + // And groups list of predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User( diff --git a/entc/integration/migrate/entv2/user_create.go b/entc/integration/migrate/entv2/user_create.go index ba836d43c..7ca33e342 100644 --- a/entc/integration/migrate/entv2/user_create.go +++ b/entc/integration/migrate/entv2/user_create.go @@ -23,6 +23,7 @@ type UserCreate struct { phone *string buffer *[]byte title *string + blob *[]byte } // SetAge sets the age field. @@ -63,6 +64,12 @@ func (uc *UserCreate) SetNillableTitle(s *string) *UserCreate { return uc } +// SetBlob sets the blob field. +func (uc *UserCreate) SetBlob(b []byte) *UserCreate { + uc.blob = &b + return uc +} + // Save creates the User in the database. func (uc *UserCreate) Save(ctx context.Context) (*User, error) { if uc.age == nil { @@ -124,6 +131,10 @@ func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { builder.Set(user.FieldTitle, *uc.title) u.Title = *uc.title } + if uc.blob != nil { + builder.Set(user.FieldBlob, *uc.blob) + u.Blob = *uc.blob + } query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { return nil, rollback(tx, err) diff --git a/entc/integration/migrate/entv2/user_update.go b/entc/integration/migrate/entv2/user_update.go index b6195dd88..00a5e646a 100644 --- a/entc/integration/migrate/entv2/user_update.go +++ b/entc/integration/migrate/entv2/user_update.go @@ -25,6 +25,7 @@ type UserUpdate struct { phone *string buffer *[]byte title *string + blob *[]byte predicates []predicate.User } @@ -78,6 +79,12 @@ func (uu *UserUpdate) SetNillableTitle(s *string) *UserUpdate { return uu } +// SetBlob sets the blob field. +func (uu *UserUpdate) SetBlob(b []byte) *UserUpdate { + uu.blob = &b + return uu +} + // Save executes the query and returns the number of rows/vertices matched by this operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { return uu.sqlSave(ctx) @@ -161,6 +168,10 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { update = true builder.Set(user.FieldTitle, *value) } + if value := uu.blob; value != nil { + update = true + builder.Set(user.FieldBlob, *value) + } if update { query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { @@ -183,6 +194,7 @@ type UserUpdateOne struct { phone *string buffer *[]byte title *string + blob *[]byte } // SetAge sets the age field. @@ -229,6 +241,12 @@ func (uuo *UserUpdateOne) SetNillableTitle(s *string) *UserUpdateOne { return uuo } +// SetBlob sets the blob field. +func (uuo *UserUpdateOne) SetBlob(b []byte) *UserUpdateOne { + uuo.blob = &b + return uuo +} + // Save executes the query and returns the updated entity. func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { return uuo.sqlSave(ctx) @@ -321,6 +339,11 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (u *User, err error) { builder.Set(user.FieldTitle, *value) u.Title = *value } + if value := uuo.blob; value != nil { + update = true + builder.Set(user.FieldBlob, *value) + u.Blob = *value + } if update { query, args := builder.Query() if err := tx.Exec(ctx, query, args, &res); err != nil { diff --git a/entc/integration/migrate/migrate_test.go b/entc/integration/migrate/migrate_test.go index 9fcb5894d..b1986a17d 100644 --- a/entc/integration/migrate/migrate_test.go +++ b/entc/integration/migrate/migrate_test.go @@ -92,6 +92,12 @@ func SanityV1(t *testing.T, client *entv1.Client) { client.User.Create().SetAge(3).SetName("foo").SetAddress("tlv").SaveX(ctx) _, err = client.User.Create().SetAge(4).SetName("foo").SetAddress("tlv").Save(ctx) require.Error(t, err) + + // blob type limited to 255. + u = u.Update().SetBlob([]byte("hello")).SaveX(ctx) + require.Equal(t, "hello", string(u.Blob)) + u, err = u.Update().SetBlob(make([]byte, 256)).Save(ctx) + require.Error(t, err, "data too long for column 'blob' error") } func SanityV2(t *testing.T, client *entv2.Client) { @@ -117,6 +123,11 @@ func SanityV2(t *testing.T, client *entv2.Client) { client.User.Query().CountX(ctx), client.User.Query().Where(user.Title(user.DefaultTitle)).CountX(ctx), ) + + // blob type was extended. + u, err = u.Update().SetBlob(make([]byte, 256)).Save(ctx) + require.NoError(t, err, "data type blob was extended in v2") + require.Equal(t, make([]byte, 256), u.Blob) } func EqualFold(t *testing.T, client *entv2.Client) { diff --git a/schema/field/field.go b/schema/field/field.go index f285754d6..52159a910 100644 --- a/schema/field/field.go +++ b/schema/field/field.go @@ -401,6 +401,14 @@ func (b *bytesBuilder) StructTag(s string) *bytesBuilder { return b } +// MaxLen sets the max-length of the bytes type in the database. +// In MySQL, this affects the BLOB type (tiny 2^8-1, regular 2^16-1, medium 2^24-1, long 2^32-1). +// In SQLite, it does not have any effect on the type size, which is default to 1B bytes. +func (b *bytesBuilder) MaxLen(i int) *bytesBuilder { + b.desc.Size = i + return b +} + // Descriptor implements the ent.Field interface by returning its descriptor. func (b *bytesBuilder) Descriptor() *Descriptor { return b.desc