From 9e809635b2e5815e259b4ab1ca08357341576a38 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Tue, 5 Oct 2021 22:27:58 +0300 Subject: [PATCH] entc/gen: ignore immutable fields on Upsert.UpdateNewValues Also, for some reason, the TimeMixin.UpdateTime was an immutable field, but this was incorrent, because the codegen just skip generating update setters to it. Removing the Immutable modifier allows users to set this field explicitly. --- dialect/sql/builder_test.go | 9 +++-- entc/gen/bench_test.go | 1 - .../template/dialect/sql/feature/upsert.tmpl | 38 +++++++++++++------ entc/gen/type.go | 13 ++++++- entc/integration/customid/ent/blob_create.go | 4 +- entc/integration/customid/ent/car_create.go | 4 +- .../integration/customid/ent/device_create.go | 4 +- entc/integration/customid/ent/doc_create.go | 4 +- entc/integration/customid/ent/group_create.go | 4 +- .../customid/ent/mixinid_create.go | 4 +- entc/integration/customid/ent/note_create.go | 4 +- entc/integration/customid/ent/pet_create.go | 4 +- .../customid/ent/session_create.go | 4 +- entc/integration/customid/ent/user_create.go | 4 +- entc/integration/ent/card_create.go | 22 ++++++++++- entc/integration/ent/card_update.go | 13 +++++++ entc/integration/ent/comment_create.go | 4 +- entc/integration/ent/fieldtype_create.go | 4 +- entc/integration/ent/file_create.go | 4 +- entc/integration/ent/filetype_create.go | 4 +- entc/integration/ent/goods_create.go | 4 +- entc/integration/ent/group_create.go | 4 +- entc/integration/ent/groupinfo_create.go | 4 +- entc/integration/ent/item_create.go | 4 +- entc/integration/ent/migrate/schema.go | 2 +- entc/integration/ent/node_create.go | 4 +- entc/integration/ent/pet_create.go | 4 +- entc/integration/ent/schema/card.go | 3 +- entc/integration/ent/spec_create.go | 4 +- entc/integration/ent/task_create.go | 4 +- entc/integration/ent/user_create.go | 4 +- entc/integration/gremlin/ent/card_update.go | 13 +++++++ entc/integration/integration_test.go | 15 ++++++++ schema/field/field.go | 3 +- schema/mixin/mixin.go | 3 +- schema/mixin/mixin_test.go | 1 - 36 files changed, 156 insertions(+), 72 deletions(-) diff --git a/dialect/sql/builder_test.go b/dialect/sql/builder_test.go index e0545e852..f640fe5a0 100644 --- a/dialect/sql/builder_test.go +++ b/dialect/sql/builder_test.go @@ -1711,8 +1711,8 @@ func TestInsert_OnConflict(t *testing.T) { t.Run("Postgres", func(t *testing.T) { // And SQLite. query, args := Dialect(dialect.Postgres). Insert("users"). - Columns("id", "email"). - Values("1", "user@example.com"). + Columns("id", "email", "creation_time"). + Values("1", "user@example.com", 1633279231). OnConflict( ConflictColumns("email"), ConflictWhere(EQ("name", "Ariel")), @@ -1720,13 +1720,14 @@ func TestInsert_OnConflict(t *testing.T) { // Update all new values excepts id field. ResolveWith(func(u *UpdateSet) { u.SetIgnore("id") + u.SetIgnore("creation_time") u.Add("version", 1) }), UpdateWhere(NEQ("updated_at", 0)), ). Query() - require.Equal(t, `INSERT INTO "users" ("id", "email") VALUES ($1, $2) ON CONFLICT ("email") WHERE "name" = $3 DO UPDATE SET "id" = "users"."id", "email" = "excluded"."email", "version" = COALESCE("users"."version", 0) + $4 WHERE "updated_at" <> $5`, query) - require.Equal(t, []interface{}{"1", "user@example.com", "Ariel", 1, 0}, args) + require.Equal(t, `INSERT INTO "users" ("id", "email", "creation_time") VALUES ($1, $2, $3) ON CONFLICT ("email") WHERE "name" = $4 DO UPDATE SET "id" = "users"."id", "email" = "excluded"."email", "creation_time" = "users"."creation_time", "version" = COALESCE("users"."version", 0) + $5 WHERE "updated_at" <> $6`, query) + require.Equal(t, []interface{}{"1", "user@example.com", 1633279231, "Ariel", 1, 0}, args) query, args = Dialect(dialect.Postgres). Insert("users"). diff --git a/entc/gen/bench_test.go b/entc/gen/bench_test.go index bf3cd9bce..cafd6e983 100644 --- a/entc/gen/bench_test.go +++ b/entc/gen/bench_test.go @@ -16,7 +16,6 @@ import ( ) func BenchmarkGraph_Gen(b *testing.B) { - b.ReportAllocs() target := filepath.Join(os.TempDir(), "ent") require.NoError(b, os.MkdirAll(target, os.ModePerm), "creating tmpdir") defer os.RemoveAll(target) diff --git a/entc/gen/template/dialect/sql/feature/upsert.tmpl b/entc/gen/template/dialect/sql/feature/upsert.tmpl index 12bf12b3f..210ccd17e 100644 --- a/entc/gen/template/dialect/sql/feature/upsert.tmpl +++ b/entc/gen/template/dialect/sql/feature/upsert.tmpl @@ -143,7 +143,7 @@ type ( {{ end }} -// UpdateNewValues updates the fields using the new values that were set on create{{ if $.ID.UserDefined }} except the ID field{{ end }}. +// UpdateNewValues updates the mutable fields using the new values that were set on create{{ if $.ID.UserDefined }} except the ID field{{ end }}. // Using this option is equivalent to using: // // client.{{ $.Name }}.Create(). @@ -159,11 +159,18 @@ type ( // func (u *{{ $upsertOne }}) UpdateNewValues() *{{ $upsertOne }} { u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) - {{- if $.ID.UserDefined }} + {{- if or $.ID.UserDefined $.ImmutableFields }} u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { - if _, exists := u.create.mutation.ID(); exists { - s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }}) - } + {{- if $.ID.UserDefined }} + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }}) + } + {{- end }} + {{- range $f := $.ImmutableFields }} + if _, exists := u.create.mutation.{{ $f.MutationGet }}(); exists { + s.SetIgnore({{ $.Package }}.{{ $f.Constant }}) + } + {{- end }} })) {{- end }} return u @@ -306,7 +313,7 @@ type {{ $upsertBulk }} struct { } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.{{ $.Name }}.Create(). @@ -322,13 +329,20 @@ type {{ $upsertBulk }} struct { // func (u *{{ $upsertBulk }}) UpdateNewValues() *{{ $upsertBulk }} { u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) - {{- if $.ID.UserDefined }} + {{- if or $.ID.UserDefined $.ImmutableFields }} u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { for _, b := range u.create.builders { - if _, exists := b.mutation.ID(); exists { - s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }}) - return - } + {{- if $.ID.UserDefined }} + if _, exists := b.mutation.ID(); exists { + s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }}) + return + } + {{- end }} + {{- range $f := $.ImmutableFields }} + if _, exists := b.mutation.{{ $f.MutationGet }}(); exists { + s.SetIgnore({{ $.Package }}.{{ $f.Constant }}) + } + {{- end }} } })) {{- end }} @@ -429,4 +443,4 @@ func (u *{{ $upsertBulk }}) ExecX(ctx context.Context) { } {{ end }} {{ end }} -{{ end }} \ No newline at end of file +{{ end }} diff --git a/entc/gen/type.go b/entc/gen/type.go index cd8d51493..97ca9e1d5 100644 --- a/entc/gen/type.go +++ b/entc/gen/type.go @@ -459,6 +459,17 @@ func (t Type) MutableFields() []*Field { return fields } +// ImmutableFields returns all type fields that are immutable (for update). +func (t Type) ImmutableFields() []*Field { + fields := make([]*Field, 0, len(t.Fields)) + for _, f := range t.Fields { + if f.Immutable { + fields = append(fields, f) + } + } + return fields +} + // MutationFields returns all the fields that are available on the typed-mutation. func (t Type) MutationFields() []*Field { fields := make([]*Field, 0, len(t.Fields)) @@ -864,7 +875,7 @@ func (f Field) UpdateDefaultName() string { return "Update" + f.DefaultName() } func (f Field) DefaultValue() interface{} { return f.def.DefaultValue } // DefaultFunc returns a bool stating if the default value is a func. Invoked by the template. -func (f Field) DefaultFunc() interface{} { return f.def.DefaultKind == reflect.Func } +func (f Field) DefaultFunc() bool { return f.def.DefaultKind == reflect.Func } // BuilderField returns the struct member of the field in the builder. func (f Field) BuilderField() string { diff --git a/entc/integration/customid/ent/blob_create.go b/entc/integration/customid/ent/blob_create.go index 1cd183f65..e7d1ccfbd 100644 --- a/entc/integration/customid/ent/blob_create.go +++ b/entc/integration/customid/ent/blob_create.go @@ -352,7 +352,7 @@ func (u *BlobUpsert) AddCount(v int) *BlobUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Blob.Create(). @@ -600,7 +600,7 @@ type BlobUpsertBulk struct { create *BlobCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Blob.Create(). diff --git a/entc/integration/customid/ent/car_create.go b/entc/integration/customid/ent/car_create.go index 8e3200707..0e6255ec6 100644 --- a/entc/integration/customid/ent/car_create.go +++ b/entc/integration/customid/ent/car_create.go @@ -365,7 +365,7 @@ func (u *CarUpsert) UpdateModel() *CarUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Car.Create(). @@ -646,7 +646,7 @@ type CarUpsertBulk struct { create *CarCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Car.Create(). diff --git a/entc/integration/customid/ent/device_create.go b/entc/integration/customid/ent/device_create.go index 964f06965..de2cc85e8 100644 --- a/entc/integration/customid/ent/device_create.go +++ b/entc/integration/customid/ent/device_create.go @@ -272,7 +272,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Device.Create(). @@ -480,7 +480,7 @@ type DeviceUpsertBulk struct { create *DeviceCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Device.Create(). diff --git a/entc/integration/customid/ent/doc_create.go b/entc/integration/customid/ent/doc_create.go index 284fdc836..429523fd4 100644 --- a/entc/integration/customid/ent/doc_create.go +++ b/entc/integration/customid/ent/doc_create.go @@ -325,7 +325,7 @@ func (u *DocUpsert) ClearText() *DocUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Doc.Create(). @@ -559,7 +559,7 @@ type DocUpsertBulk struct { create *DocCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Doc.Create(). diff --git a/entc/integration/customid/ent/group_create.go b/entc/integration/customid/ent/group_create.go index 767095fca..39fa4a172 100644 --- a/entc/integration/customid/ent/group_create.go +++ b/entc/integration/customid/ent/group_create.go @@ -218,7 +218,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Group.Create(). @@ -424,7 +424,7 @@ type GroupUpsertBulk struct { create *GroupCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Group.Create(). diff --git a/entc/integration/customid/ent/mixinid_create.go b/entc/integration/customid/ent/mixinid_create.go index 56d5d6012..6ba4117d0 100644 --- a/entc/integration/customid/ent/mixinid_create.go +++ b/entc/integration/customid/ent/mixinid_create.go @@ -257,7 +257,7 @@ func (u *MixinIDUpsert) UpdateMixinField() *MixinIDUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.MixinID.Create(). @@ -498,7 +498,7 @@ type MixinIDUpsertBulk struct { create *MixinIDCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.MixinID.Create(). diff --git a/entc/integration/customid/ent/note_create.go b/entc/integration/customid/ent/note_create.go index d5e09dde2..1fc2018ad 100644 --- a/entc/integration/customid/ent/note_create.go +++ b/entc/integration/customid/ent/note_create.go @@ -325,7 +325,7 @@ func (u *NoteUpsert) ClearText() *NoteUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Note.Create(). @@ -559,7 +559,7 @@ type NoteUpsertBulk struct { create *NoteCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Note.Create(). diff --git a/entc/integration/customid/ent/pet_create.go b/entc/integration/customid/ent/pet_create.go index f6df07b9a..ebf9360c1 100644 --- a/entc/integration/customid/ent/pet_create.go +++ b/entc/integration/customid/ent/pet_create.go @@ -353,7 +353,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Pet.Create(). @@ -561,7 +561,7 @@ type PetUpsertBulk struct { create *PetCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Pet.Create(). diff --git a/entc/integration/customid/ent/session_create.go b/entc/integration/customid/ent/session_create.go index 288eb4223..063bbb19b 100644 --- a/entc/integration/customid/ent/session_create.go +++ b/entc/integration/customid/ent/session_create.go @@ -238,7 +238,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Session.Create(). @@ -446,7 +446,7 @@ type SessionUpsertBulk struct { create *SessionCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Session.Create(). diff --git a/entc/integration/customid/ent/user_create.go b/entc/integration/customid/ent/user_create.go index 9d1cd9127..44a65336a 100644 --- a/entc/integration/customid/ent/user_create.go +++ b/entc/integration/customid/ent/user_create.go @@ -326,7 +326,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.User.Create(). @@ -532,7 +532,7 @@ type UserUpsertBulk struct { create *UserCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.User.Create(). diff --git a/entc/integration/ent/card_create.go b/entc/integration/ent/card_create.go index cf0ea4713..5d193bd6e 100644 --- a/entc/integration/ent/card_create.go +++ b/entc/integration/ent/card_create.go @@ -466,7 +466,7 @@ func (u *CardUpsert) ClearName() *CardUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Card.Create(). @@ -477,6 +477,14 @@ func (u *CardUpsert) ClearName() *CardUpsert { // func (u *CardUpsertOne) UpdateNewValues() *CardUpsertOne { u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.CreateTime(); exists { + s.SetIgnore(card.FieldCreateTime) + } + if _, exists := u.create.mutation.Number(); exists { + s.SetIgnore(card.FieldNumber) + } + })) return u } @@ -754,7 +762,7 @@ type CardUpsertBulk struct { create *CardCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Card.Create(). @@ -765,6 +773,16 @@ type CardUpsertBulk struct { // func (u *CardUpsertBulk) UpdateNewValues() *CardUpsertBulk { u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.CreateTime(); exists { + s.SetIgnore(card.FieldCreateTime) + } + if _, exists := b.mutation.Number(); exists { + s.SetIgnore(card.FieldNumber) + } + } + })) return u } diff --git a/entc/integration/ent/card_update.go b/entc/integration/ent/card_update.go index 6045e00d6..27c28a9ae 100644 --- a/entc/integration/ent/card_update.go +++ b/entc/integration/ent/card_update.go @@ -10,6 +10,7 @@ import ( "context" "errors" "fmt" + "time" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" @@ -33,6 +34,12 @@ func (cu *CardUpdate) Where(ps ...predicate.Card) *CardUpdate { return cu } +// SetUpdateTime sets the "update_time" field. +func (cu *CardUpdate) SetUpdateTime(t time.Time) *CardUpdate { + cu.mutation.SetUpdateTime(t) + return cu +} + // SetBalance sets the "balance" field. func (cu *CardUpdate) SetBalance(f float64) *CardUpdate { cu.mutation.ResetBalance() @@ -379,6 +386,12 @@ type CardUpdateOne struct { mutation *CardMutation } +// SetUpdateTime sets the "update_time" field. +func (cuo *CardUpdateOne) SetUpdateTime(t time.Time) *CardUpdateOne { + cuo.mutation.SetUpdateTime(t) + return cuo +} + // SetBalance sets the "balance" field. func (cuo *CardUpdateOne) SetBalance(f float64) *CardUpdateOne { cuo.mutation.ResetBalance() diff --git a/entc/integration/ent/comment_create.go b/entc/integration/ent/comment_create.go index 39ddcadde..b4cb9b907 100644 --- a/entc/integration/ent/comment_create.go +++ b/entc/integration/ent/comment_create.go @@ -293,7 +293,7 @@ func (u *CommentUpsert) ClearNillableInt() *CommentUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Comment.Create(). @@ -566,7 +566,7 @@ type CommentUpsertBulk struct { create *CommentCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Comment.Create(). diff --git a/entc/integration/ent/fieldtype_create.go b/entc/integration/ent/fieldtype_create.go index d32314658..02f271c34 100644 --- a/entc/integration/ent/fieldtype_create.go +++ b/entc/integration/ent/fieldtype_create.go @@ -2781,7 +2781,7 @@ func (u *FieldTypeUpsert) ClearPasswordOther() *FieldTypeUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.FieldType.Create(). @@ -4455,7 +4455,7 @@ type FieldTypeUpsertBulk struct { create *FieldTypeCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.FieldType.Create(). diff --git a/entc/integration/ent/file_create.go b/entc/integration/ent/file_create.go index 459e848e7..a500a928f 100644 --- a/entc/integration/ent/file_create.go +++ b/entc/integration/ent/file_create.go @@ -498,7 +498,7 @@ func (u *FileUpsert) ClearOp() *FileUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.File.Create(). @@ -800,7 +800,7 @@ type FileUpsertBulk struct { create *FileCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.File.Create(). diff --git a/entc/integration/ent/filetype_create.go b/entc/integration/ent/filetype_create.go index 5fee89ecd..3b3f1a8b4 100644 --- a/entc/integration/ent/filetype_create.go +++ b/entc/integration/ent/filetype_create.go @@ -338,7 +338,7 @@ func (u *FileTypeUpsert) UpdateState() *FileTypeUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.FileType.Create(). @@ -584,7 +584,7 @@ type FileTypeUpsertBulk struct { create *FileTypeCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.FileType.Create(). diff --git a/entc/integration/ent/goods_create.go b/entc/integration/ent/goods_create.go index 34c3e67f6..d8945b849 100644 --- a/entc/integration/ent/goods_create.go +++ b/entc/integration/ent/goods_create.go @@ -171,7 +171,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Goods.Create(). @@ -369,7 +369,7 @@ type GoodsUpsertBulk struct { create *GoodsCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Goods.Create(). diff --git a/entc/integration/ent/group_create.go b/entc/integration/ent/group_create.go index a160b8649..b295a3771 100644 --- a/entc/integration/ent/group_create.go +++ b/entc/integration/ent/group_create.go @@ -526,7 +526,7 @@ func (u *GroupUpsert) UpdateName() *GroupUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Group.Create(). @@ -821,7 +821,7 @@ type GroupUpsertBulk struct { create *GroupCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Group.Create(). diff --git a/entc/integration/ent/groupinfo_create.go b/entc/integration/ent/groupinfo_create.go index 57f52740a..ba5a43530 100644 --- a/entc/integration/ent/groupinfo_create.go +++ b/entc/integration/ent/groupinfo_create.go @@ -293,7 +293,7 @@ func (u *GroupInfoUpsert) AddMaxUsers(v int) *GroupInfoUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.GroupInfo.Create(). @@ -532,7 +532,7 @@ type GroupInfoUpsertBulk struct { create *GroupInfoCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.GroupInfo.Create(). diff --git a/entc/integration/ent/item_create.go b/entc/integration/ent/item_create.go index a4c777732..c5c23be3a 100644 --- a/entc/integration/ent/item_create.go +++ b/entc/integration/ent/item_create.go @@ -256,7 +256,7 @@ func (u *ItemUpsert) ClearText() *ItemUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // // client.Item.Create(). @@ -490,7 +490,7 @@ type ItemUpsertBulk struct { create *ItemCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Item.Create(). diff --git a/entc/integration/ent/migrate/schema.go b/entc/integration/ent/migrate/schema.go index 68198cbb0..863b0fc18 100644 --- a/entc/integration/ent/migrate/schema.go +++ b/entc/integration/ent/migrate/schema.go @@ -44,7 +44,7 @@ var ( }, { Name: "card_number", - Unique: false, + Unique: true, Columns: []*schema.Column{CardsColumns[4]}, }, { diff --git a/entc/integration/ent/node_create.go b/entc/integration/ent/node_create.go index 2f7a074e6..823d99d7f 100644 --- a/entc/integration/ent/node_create.go +++ b/entc/integration/ent/node_create.go @@ -300,7 +300,7 @@ func (u *NodeUpsert) ClearValue() *NodeUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Node.Create(). @@ -531,7 +531,7 @@ type NodeUpsertBulk struct { create *NodeCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Node.Create(). diff --git a/entc/integration/ent/pet_create.go b/entc/integration/ent/pet_create.go index c5869fd7f..986ae236d 100644 --- a/entc/integration/ent/pet_create.go +++ b/entc/integration/ent/pet_create.go @@ -410,7 +410,7 @@ func (u *PetUpsert) ClearNickname() *PetUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Pet.Create(). @@ -691,7 +691,7 @@ type PetUpsertBulk struct { create *PetCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Pet.Create(). diff --git a/entc/integration/ent/schema/card.go b/entc/integration/ent/schema/card.go index 7228a05b6..3952aaeea 100644 --- a/entc/integration/ent/schema/card.go +++ b/entc/integration/ent/schema/card.go @@ -94,7 +94,8 @@ func (Card) Edges() []ent.Edge { func (Card) Indexes() []ent.Index { return []ent.Index{ index.Fields("id"), - index.Fields("number"), + index.Fields("number"). + Unique(), index.Fields("id", "name", "number"), } } diff --git a/entc/integration/ent/spec_create.go b/entc/integration/ent/spec_create.go index 032d9b693..cecc5dc8d 100644 --- a/entc/integration/ent/spec_create.go +++ b/entc/integration/ent/spec_create.go @@ -206,7 +206,7 @@ type ( } ) -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Spec.Create(). @@ -404,7 +404,7 @@ type SpecUpsertBulk struct { create *SpecCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Spec.Create(). diff --git a/entc/integration/ent/task_create.go b/entc/integration/ent/task_create.go index c4d9a2b23..80aa322b2 100644 --- a/entc/integration/ent/task_create.go +++ b/entc/integration/ent/task_create.go @@ -235,7 +235,7 @@ func (u *TaskUpsert) AddPriority(v schema.Priority) *TaskUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.Task.Create(). @@ -460,7 +460,7 @@ type TaskUpsertBulk struct { create *TaskCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.Task.Create(). diff --git a/entc/integration/ent/user_create.go b/entc/integration/ent/user_create.go index 195af5e68..4d6d472fb 100644 --- a/entc/integration/ent/user_create.go +++ b/entc/integration/ent/user_create.go @@ -1030,7 +1030,7 @@ func (u *UserUpsert) ClearSSOCert() *UserUpsert { return u } -// UpdateNewValues updates the fields using the new values that were set on create. +// UpdateNewValues updates the mutable fields using the new values that were set on create. // Using this option is equivalent to using: // // client.User.Create(). @@ -1444,7 +1444,7 @@ type UserUpsertBulk struct { create *UserCreateBulk } -// UpdateNewValues updates the fields using the new values that +// UpdateNewValues updates the mutable fields using the new values that // were set on create. Using this option is equivalent to using: // // client.User.Create(). diff --git a/entc/integration/gremlin/ent/card_update.go b/entc/integration/gremlin/ent/card_update.go index bac38b81b..08e0b622b 100644 --- a/entc/integration/gremlin/ent/card_update.go +++ b/entc/integration/gremlin/ent/card_update.go @@ -10,6 +10,7 @@ import ( "context" "errors" "fmt" + "time" "entgo.io/ent/dialect/gremlin" "entgo.io/ent/dialect/gremlin/graph/dsl" @@ -35,6 +36,12 @@ func (cu *CardUpdate) Where(ps ...predicate.Card) *CardUpdate { return cu } +// SetUpdateTime sets the "update_time" field. +func (cu *CardUpdate) SetUpdateTime(t time.Time) *CardUpdate { + cu.mutation.SetUpdateTime(t) + return cu +} + // SetBalance sets the "balance" field. func (cu *CardUpdate) SetBalance(f float64) *CardUpdate { cu.mutation.ResetBalance() @@ -309,6 +316,12 @@ type CardUpdateOne struct { mutation *CardMutation } +// SetUpdateTime sets the "update_time" field. +func (cuo *CardUpdateOne) SetUpdateTime(t time.Time) *CardUpdateOne { + cuo.mutation.SetUpdateTime(t) + return cuo +} + // SetBalance sets the "balance" field. func (cuo *CardUpdateOne) SetBalance(f float64) *CardUpdateOne { cuo.mutation.ResetBalance() diff --git a/entc/integration/integration_test.go b/entc/integration/integration_test.go index 4c0c51106..3a0b3ec18 100644 --- a/entc/integration/integration_test.go +++ b/entc/integration/integration_test.go @@ -23,6 +23,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/ent" + "entgo.io/ent/entc/integration/ent/card" "entgo.io/ent/entc/integration/ent/enttest" "entgo.io/ent/entc/integration/ent/file" "entgo.io/ent/entc/integration/ent/filetype" @@ -367,6 +368,20 @@ func Upsert(t *testing.T, client *ent.Client) { ExecX(ctx) require.Equal(t, bid, client.Item.Query().OnlyIDX(ctx)) } + + c1 := client.Card.Create(). + SetNumber("102030"). + SetCreateTime(time.Unix(1623279251, 0)). + SetUpdateTime(time.Unix(1623279251, 0)). + SaveX(ctx) + id = client.Card.Create(). + SetNumber(c1.Number). + OnConflictColumns(card.FieldNumber). + UpdateNewValues(). + IDX(ctx) + c2 := client.Card.GetX(ctx, id) + require.Equal(t, c1.CreateTime.Unix(), c2.CreateTime.Unix()) + require.NotEqual(t, c1.UpdateTime.Unix(), c2.UpdateTime.Unix()) } func Clone(t *testing.T, client *ent.Client) { diff --git a/schema/field/field.go b/schema/field/field.go index 113613b31..f61ab140e 100644 --- a/schema/field/field.go +++ b/schema/field/field.go @@ -345,7 +345,8 @@ func (b *timeBuilder) Optional() *timeBuilder { return b } -// Immutable indicates that this field cannot be updated. +// Immutable fields are fields that can be set only in the creation of the entity. +// i.e., no setters will be generated for the entity updaters (one and many). func (b *timeBuilder) Immutable() *timeBuilder { b.desc.Immutable = true return b diff --git a/schema/mixin/mixin.go b/schema/mixin/mixin.go index 42535f58e..f9c5fa150 100644 --- a/schema/mixin/mixin.go +++ b/schema/mixin/mixin.go @@ -65,8 +65,7 @@ func (UpdateTime) Fields() []ent.Field { return []ent.Field{ field.Time("update_time"). Default(time.Now). - UpdateDefault(time.Now). - Immutable(), + UpdateDefault(time.Now), } } diff --git a/schema/mixin/mixin_test.go b/schema/mixin/mixin_test.go index 5b1722afb..59ab6d60a 100644 --- a/schema/mixin/mixin_test.go +++ b/schema/mixin/mixin_test.go @@ -33,7 +33,6 @@ func TestTimeMixin(t *testing.T) { require.Len(t, fields, 1) desc := fields[0].Descriptor() assert.Equal(t, "update_time", desc.Name) - assert.True(t, desc.Immutable) assert.NotNil(t, desc.Default) assert.NotNil(t, desc.UpdateDefault) })