From 38fcf995d007b523d07196a62cd6a36422884112 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Tue, 29 Oct 2019 10:15:01 -0700 Subject: [PATCH] entc/integration/migrate: enable postgres 10,11 in migrate test Summary: Pull Request resolved: https://github.com/facebookincubator/ent/pull/132 Reviewed By: alexsn Differential Revision: D18203398 fbshipit-source-id: a261ed3934d2e4262d7177b41546b2239e798ef5 --- .../migrate/entv2/migrate/schema.go | 4 +- entc/integration/migrate/entv2/schema/user.go | 7 +- entc/integration/migrate/entv2/user/user.go | 8 +- entc/integration/migrate/entv2/user/where.go | 18 +++++ entc/integration/migrate/entv2/user_create.go | 15 ++-- entc/integration/migrate/entv2/user_update.go | 40 ++++++++++ entc/integration/migrate/migrate_test.go | 75 ++++++++++++++----- 7 files changed, 133 insertions(+), 34 deletions(-) diff --git a/entc/integration/migrate/entv2/migrate/schema.go b/entc/integration/migrate/entv2/migrate/schema.go index b37332c66..ed43ba3d7 100644 --- a/entc/integration/migrate/entv2/migrate/schema.go +++ b/entc/integration/migrate/entv2/migrate/schema.go @@ -41,8 +41,8 @@ var ( {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "age", Type: field.TypeInt}, {Name: "name", Type: field.TypeString, Size: 2147483647}, - {Name: "phone", Type: field.TypeString}, - {Name: "buffer", Type: field.TypeBytes, Default: user.DefaultBuffer}, + {Name: "phone", Type: field.TypeString, Default: user.DefaultPhone}, + {Name: "buffer", Type: field.TypeBytes, Nullable: true}, {Name: "title", Type: field.TypeString, Default: user.DefaultTitle}, {Name: "renamed", Type: field.TypeString, Nullable: true}, {Name: "blob", Type: field.TypeBytes, Nullable: true, Size: 1000}, diff --git a/entc/integration/migrate/entv2/schema/user.go b/entc/integration/migrate/entv2/schema/user.go index 410714840..f5493de4e 100644 --- a/entc/integration/migrate/entv2/schema/user.go +++ b/entc/integration/migrate/entv2/schema/user.go @@ -22,10 +22,11 @@ func (User) Fields() []ent.Field { field.Int("age"), // extending name field to longtext. field.Text("name"), - // adding new columns. - field.String("phone"), + // adding new columns (must be either optional, or with a default value). + field.String("phone"). + Default("unknown"), field.Bytes("buffer"). - Default([]byte("{}")), + Optional(), // adding new column with supported default value // in the database side, will append this value to // all existing rows. diff --git a/entc/integration/migrate/entv2/user/user.go b/entc/integration/migrate/entv2/user/user.go index 4102190b7..6773f720f 100644 --- a/entc/integration/migrate/entv2/user/user.go +++ b/entc/integration/migrate/entv2/user/user.go @@ -54,10 +54,10 @@ var Columns = []string{ var ( fields = schema.User{}.Fields() - // descBuffer is the schema descriptor for buffer field. - descBuffer = fields[3].Descriptor() - // DefaultBuffer holds the default value on creation for the buffer field. - DefaultBuffer = descBuffer.Default.([]byte) + // descPhone is the schema descriptor for phone field. + descPhone = fields[2].Descriptor() + // DefaultPhone holds the default value on creation for the phone field. + DefaultPhone = descPhone.Default.(string) // descTitle is the schema descriptor for title field. descTitle = fields[4].Descriptor() diff --git a/entc/integration/migrate/entv2/user/where.go b/entc/integration/migrate/entv2/user/where.go index ccf7fd839..b9d3369ac 100644 --- a/entc/integration/migrate/entv2/user/where.go +++ b/entc/integration/migrate/entv2/user/where.go @@ -633,6 +633,24 @@ func BufferLTE(v []byte) predicate.User { ) } +// BufferIsNil applies the IsNil predicate on the "buffer" field. +func BufferIsNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.IsNull(s.C(FieldBuffer))) + }, + ) +} + +// BufferNotNil applies the NotNil predicate on the "buffer" field. +func BufferNotNil() predicate.User { + return predicate.User( + func(s *sql.Selector) { + s.Where(sql.NotNull(s.C(FieldBuffer))) + }, + ) +} + // TitleEQ applies the EQ predicate on the "title" field. func TitleEQ(v string) predicate.User { return predicate.User( diff --git a/entc/integration/migrate/entv2/user_create.go b/entc/integration/migrate/entv2/user_create.go index afe15c139..111278482 100644 --- a/entc/integration/migrate/entv2/user_create.go +++ b/entc/integration/migrate/entv2/user_create.go @@ -46,6 +46,14 @@ func (uc *UserCreate) SetPhone(s string) *UserCreate { return uc } +// SetNillablePhone sets the phone field if the given value is not nil. +func (uc *UserCreate) SetNillablePhone(s *string) *UserCreate { + if s != nil { + uc.SetPhone(*s) + } + return uc +} + // SetBuffer sets the buffer field. func (uc *UserCreate) SetBuffer(b []byte) *UserCreate { uc.buffer = &b @@ -109,11 +117,8 @@ func (uc *UserCreate) Save(ctx context.Context) (*User, error) { return nil, errors.New("entv2: missing required field \"name\"") } if uc.phone == nil { - return nil, errors.New("entv2: missing required field \"phone\"") - } - if uc.buffer == nil { - v := user.DefaultBuffer - uc.buffer = &v + v := user.DefaultPhone + uc.phone = &v } if uc.title == nil { v := user.DefaultTitle diff --git a/entc/integration/migrate/entv2/user_update.go b/entc/integration/migrate/entv2/user_update.go index 57f200524..e40a3e59c 100644 --- a/entc/integration/migrate/entv2/user_update.go +++ b/entc/integration/migrate/entv2/user_update.go @@ -23,6 +23,7 @@ type UserUpdate struct { name *string phone *string buffer *[]byte + clearbuffer bool title *string new_name *string clearnew_name bool @@ -68,12 +69,27 @@ func (uu *UserUpdate) SetPhone(s string) *UserUpdate { return uu } +// SetNillablePhone sets the phone field if the given value is not nil. +func (uu *UserUpdate) SetNillablePhone(s *string) *UserUpdate { + if s != nil { + uu.SetPhone(*s) + } + return uu +} + // SetBuffer sets the buffer field. func (uu *UserUpdate) SetBuffer(b []byte) *UserUpdate { uu.buffer = &b return uu } +// ClearBuffer clears the value of buffer. +func (uu *UserUpdate) ClearBuffer() *UserUpdate { + uu.buffer = nil + uu.clearbuffer = true + return uu +} + // SetTitle sets the title field. func (uu *UserUpdate) SetTitle(s string) *UserUpdate { uu.title = &s @@ -224,6 +240,9 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { if value := uu.buffer; value != nil { updater.Set(user.FieldBuffer, *value) } + if uu.clearbuffer { + updater.SetNull(user.FieldBuffer) + } if value := uu.title; value != nil { updater.Set(user.FieldTitle, *value) } @@ -266,6 +285,7 @@ type UserUpdateOne struct { name *string phone *string buffer *[]byte + clearbuffer bool title *string new_name *string clearnew_name bool @@ -304,12 +324,27 @@ func (uuo *UserUpdateOne) SetPhone(s string) *UserUpdateOne { return uuo } +// SetNillablePhone sets the phone field if the given value is not nil. +func (uuo *UserUpdateOne) SetNillablePhone(s *string) *UserUpdateOne { + if s != nil { + uuo.SetPhone(*s) + } + return uuo +} + // SetBuffer sets the buffer field. func (uuo *UserUpdateOne) SetBuffer(b []byte) *UserUpdateOne { uuo.buffer = &b return uuo } +// ClearBuffer clears the value of buffer. +func (uuo *UserUpdateOne) ClearBuffer() *UserUpdateOne { + uuo.buffer = nil + uuo.clearbuffer = true + return uuo +} + // SetTitle sets the title field. func (uuo *UserUpdateOne) SetTitle(s string) *UserUpdateOne { uuo.title = &s @@ -468,6 +503,11 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (u *User, err error) { updater.Set(user.FieldBuffer, *value) u.Buffer = *value } + if uuo.clearbuffer { + var value []byte + u.Buffer = value + updater.SetNull(user.FieldBuffer) + } if value := uuo.title; value != nil { updater.Set(user.FieldTitle, *value) u.Title = *value diff --git a/entc/integration/migrate/migrate_test.go b/entc/integration/migrate/migrate_test.go index 6b8a0a13a..be24d1705 100644 --- a/entc/integration/migrate/migrate_test.go +++ b/entc/integration/migrate/migrate_test.go @@ -7,8 +7,10 @@ package migrate import ( "context" "fmt" + "strings" "testing" + "github.com/facebookincubator/ent/dialect" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/entc/integration/migrate/entv1" migratev1 "github.com/facebookincubator/ent/entc/integration/migrate/entv1/migrate" @@ -16,7 +18,9 @@ import ( "github.com/facebookincubator/ent/entc/integration/migrate/entv2" migratev2 "github.com/facebookincubator/ent/entc/integration/migrate/entv2/migrate" "github.com/facebookincubator/ent/entc/integration/migrate/entv2/user" + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/require" ) @@ -35,29 +39,34 @@ func TestMySQL(t *testing.T) { drv, err := sql.Open("mysql", fmt.Sprintf("root:pass@tcp(localhost:%d)/migrate?parseTime=True", port)) require.NoError(t, err, "connecting to migrate database") - // run migration and execute queries on v1. clientv1 := entv1.NewClient(entv1.Driver(drv)) - require.NoError(t, clientv1.Schema.Create(ctx, migratev1.WithGlobalUniqueID(true))) - SanityV1(t, clientv1) - - // run migration and execute queries on v2. clientv2 := entv2.NewClient(entv2.Driver(drv)) - require.NoError(t, clientv2.Schema.Create(ctx, migratev2.WithGlobalUniqueID(true), migratev2.WithDropIndex(true), migratev2.WithDropColumn(true))) - SanityV2(t, clientv2) + V1ToV2(t, clientv1, clientv2) + }) + } +} - // since "users" created in the migration of v1, it will occupy the range of 0 ... 1<<32-1, - // even though they are ordered differently in the migration of v2 (groups, pets, users). - idRange(t, clientv2.User.Create().SetAge(1).SetName("foo").SetPhone("phone").SaveX(ctx).ID, 0, 1<<32) - idRange(t, clientv2.Group.Create().SaveX(ctx).ID, 1<<32-1, 2<<32) - idRange(t, clientv2.Pet.Create().SaveX(ctx).ID, 2<<32-1, 3<<32) +func TestPostgres(t *testing.T) { + // Version 12 is disabled here due to segfault on migration. It will be re-enabled on its next release. + // More info can be found here: https://www.postgresql.org/message-id/23031.1572362774%40sss.pgh.pa.us + for version, port := range map[string]int{"10": 5430, "11": 5431} { + t.Run(version, func(t *testing.T) { + dsn := fmt.Sprintf("host=localhost port=%d user=postgres password=pass sslmode=disable", port) + root, err := sql.Open(dialect.Postgres, dsn) + require.NoError(t, err) + defer root.Close() + ctx := context.Background() + err = root.Exec(ctx, "CREATE DATABASE migrate", []interface{}{}, new(sql.Result)) + require.NoError(t, err, "creating database") + defer root.Exec(ctx, "DROP DATABASE migrate", []interface{}{}, new(sql.Result)) - // sql specific predicates. - EqualFold(t, clientv2) - ContainsFold(t, clientv2) + drv, err := sql.Open(dialect.Postgres, dsn+" dbname=migrate") + require.NoError(t, err, "connecting to migrate database") + defer drv.Close() - // "renamed" field was renamed to "new_name". - exist := clientv2.User.Query().Where(user.NewName("renamed")).ExistX(ctx) - require.True(t, exist, "expect renamed column to have previous values") + clientv1 := entv1.NewClient(entv1.Driver(drv)) + clientv2 := entv2.NewClient(entv2.Driver(drv)) + V1ToV2(t, clientv1, clientv2) }) } } @@ -84,6 +93,32 @@ func TestSQLite(t *testing.T) { ContainsFold(t, client) } +func V1ToV2(t *testing.T, clientv1 *entv1.Client, clientv2 *entv2.Client) { + ctx := context.Background() + + // run migration and execute queries on v1. + require.NoError(t, clientv1.Schema.Create(ctx, migratev1.WithGlobalUniqueID(true))) + SanityV1(t, clientv1) + + // run migration and execute queries on v2. + require.NoError(t, clientv2.Schema.Create(ctx, migratev2.WithGlobalUniqueID(true), migratev2.WithDropIndex(true), migratev2.WithDropColumn(true))) + SanityV2(t, clientv2) + + // since "users" created in the migration of v1, it will occupy the range of 0 ... 1<<32-1, + // even though they are ordered differently in the migration of v2 (groups, pets, users). + idRange(t, clientv2.User.Create().SetAge(1).SetName("foo").SetPhone("phone").SaveX(ctx).ID, 0, 1<<32) + idRange(t, clientv2.Group.Create().SaveX(ctx).ID, 1<<32-1, 2<<32) + idRange(t, clientv2.Pet.Create().SaveX(ctx).ID, 2<<32-1, 3<<32) + + // sql specific predicates. + EqualFold(t, clientv2) + ContainsFold(t, clientv2) + + // "renamed" field was renamed to "new_name". + exist := clientv2.User.Query().Where(user.NewName("renamed")).ExistX(ctx) + require.True(t, exist, "expect renamed column to have previous values") +} + func SanityV1(t *testing.T, client *entv1.Client) { ctx := context.Background() u := client.User.Create().SetAge(1).SetName("foo").SetRenamed("renamed").SaveX(ctx) @@ -102,7 +137,7 @@ func SanityV1(t *testing.T, client *entv1.Client) { u = u.Update().SetBlob([]byte("hello")).SaveX(ctx) require.Equal(t, "hello", string(u.Blob)) _, err = u.Update().SetBlob(make([]byte, 256)).Save(ctx) - require.Error(t, err, "data too long for column 'blob' error") + require.True(t, strings.Contains(t.Name(), "Postgres") || err != nil, "blob should be limited on SQLite and MySQL") // invalid enum value. _, err = client.User.Create().SetAge(1).SetName("bar").SetState("unknown").Save(ctx) @@ -111,7 +146,7 @@ func SanityV1(t *testing.T, client *entv1.Client) { func SanityV2(t *testing.T, client *entv2.Client) { ctx := context.Background() - u := client.User.Create().SetAge(1).SetName("bar").SetPhone("100").SetState(user.StateLoggedOut).SaveX(ctx) + u := client.User.Create().SetAge(1).SetName("bar").SetPhone("100").SetBuffer([]byte("{}")).SetState(user.StateLoggedOut).SaveX(ctx) require.Equal(t, 1, u.Age) require.Equal(t, "bar", u.Name) require.Equal(t, []byte("{}"), u.Buffer)