From 4306643d16b3bfc7b0beadc46fd96e21959b8e1d Mon Sep 17 00:00:00 2001 From: Ariel Mashraki <7413593+a8m@users.noreply.github.com> Date: Mon, 27 Sep 2021 17:49:57 +0300 Subject: [PATCH] dialect/sql/sqlgraph: support sql.Scanner types when scanning IDs (#1987) Fixed https://github.com/ent/ent/issues/1985 --- dialect/sql/sqlgraph/graph.go | 24 +- entc/gen/template/dialect/sql/create.tmpl | 13 +- entc/integration/customid/customid_test.go | 29 ++ entc/integration/customid/ent/blob.go | 12 + entc/integration/customid/ent/blob/blob.go | 5 + entc/integration/customid/ent/blob/where.go | 83 ++++ entc/integration/customid/ent/blob_create.go | 394 ++++++++++++++- entc/integration/customid/ent/blob_update.go | 70 +++ entc/integration/customid/ent/car_create.go | 455 ++++++++++++++++++ entc/integration/customid/ent/doc_create.go | 326 ++++++++++++- entc/integration/customid/ent/generate.go | 2 +- entc/integration/customid/ent/group_create.go | 245 ++++++++++ .../customid/ent/migrate/schema.go | 3 +- .../customid/ent/mixinid_create.go | 345 ++++++++++++- entc/integration/customid/ent/mutation.go | 94 +++- entc/integration/customid/ent/note_create.go | 322 +++++++++++++ entc/integration/customid/ent/pet_create.go | 251 ++++++++++ entc/integration/customid/ent/runtime.go | 4 + entc/integration/customid/ent/schema/blob.go | 2 + entc/integration/customid/ent/user_create.go | 245 ++++++++++ entc/integration/edgefield/ent/car_create.go | 4 +- 21 files changed, 2903 insertions(+), 25 deletions(-) diff --git a/dialect/sql/sqlgraph/graph.go b/dialect/sql/sqlgraph/graph.go index dede376cc..0505a81b0 100644 --- a/dialect/sql/sqlgraph/graph.go +++ b/dialect/sql/sqlgraph/graph.go @@ -1300,17 +1300,23 @@ func (c *creator) insertLastID(ctx context.Context, insert *sql.InsertBuilder) e return err } defer rows.Close() - if !c.ID.Type.Numeric() { + switch _, ok := c.ID.Value.(field.ValueScanner); { + case ok: + // If the ID implements the sql.Scanner + // interface it should be a pointer type. + return sql.ScanOne(rows, c.ID.Value) + case c.ID.Type.Numeric(): + // Normalize the type to int64 to make it + // looks like LastInsertId. + id, err := sql.ScanInt64(rows) + if err != nil { + return err + } + c.ID.Value = id + return nil + default: return sql.ScanOne(rows, &c.ID.Value) } - // Normalize the type to int64 to make it looks - // like LastInsertId. - id, err := sql.ScanInt64(rows) - if err != nil { - return err - } - c.ID.Value = id - return nil } // MySQL. var res sql.Result diff --git a/entc/gen/template/dialect/sql/create.tmpl b/entc/gen/template/dialect/sql/create.tmpl index aacca3288..58b39292b 100644 --- a/entc/gen/template/dialect/sql/create.tmpl +++ b/entc/gen/template/dialect/sql/create.tmpl @@ -4,7 +4,7 @@ This source code is licensed under the Apache 2.0 license found in the LICENSE file in the root directory of this source tree. */}} -{{/* gotype: entgo.io/ent/entc/gen.typeScope */}} +{{/* gotype: entgo.io/ent/entc/gen.Type */}} {{ define "dialect/sql/create" }} {{ $builder := pascal $.Scope.Builder }} @@ -19,9 +19,14 @@ func ({{ $receiver }} *{{ $builder }}) sqlSave(ctx context.Context) (*{{ $.Name } return nil, err } - {{- if and $.ID.UserDefined (not $.ID.Type.Numeric) }} + {{- if and $.ID.UserDefined (or $.ID.Type.ValueScanner (not $.ID.Type.Numeric)) }} if _spec.ID.Value != nil { - _node.ID = _spec.ID.Value.({{ $.ID.Type }}) + {{- /* If the ID type is not a pointer, but implements the ValueScanner interface (e.g. UUID fields). */}} + {{- if and $.ID.Type.ValueScanner (not $.ID.Type.RType.IsPtr) }} + _node.ID = *_spec.ID.Value.(*{{ $.ID.Type }}) + {{- else }} + _node.ID = _spec.ID.Value.({{ $.ID.Type }}) + {{- end }} } {{- else }} {{- if $.ID.UserDefined }} @@ -56,7 +61,7 @@ func ({{ $receiver }} *{{ $builder }}) createSpec() (*{{ $.Name }}, *sqlgraph.Cr {{- if $.ID.UserDefined }} if id, ok := {{ $mutation }}.{{ $.ID.MutationGet }}(); ok { _node.ID = id - _spec.ID.Value = id + _spec.ID.Value = {{ if and $.ID.Type.ValueScanner (not $.ID.Type.RType.IsPtr) }}&{{ end }}id } {{- end }} {{- range $f := $.MutationFields }} diff --git a/entc/integration/customid/customid_test.go b/entc/integration/customid/customid_test.go index 3f6e5347a..b8b7d5903 100644 --- a/entc/integration/customid/customid_test.go +++ b/entc/integration/customid/customid_test.go @@ -15,6 +15,8 @@ import ( "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql/schema" "entgo.io/ent/entc/integration/customid/ent" + "entgo.io/ent/entc/integration/customid/ent/blob" + "entgo.io/ent/entc/integration/customid/ent/doc" "entgo.io/ent/entc/integration/customid/ent/pet" "entgo.io/ent/entc/integration/customid/ent/user" "github.com/go-sql-driver/mysql" @@ -164,6 +166,33 @@ func CustomID(t *testing.T, client *ent.Client) { require.NotEmpty(t, pdoc.Text) cdoc := client.Doc.Create().SetText("child").SetParent(pdoc).SaveX(ctx) require.NotEmpty(t, cdoc.QueryParent().OnlyIDX(ctx)) + + t.Run("Upsert", func(t *testing.T) { + id := uuid.New() + client.Blob.Create(). + SetID(id). + OnConflictColumns(blob.FieldID). + UpdateNewValues(). + ExecX(ctx) + require.Zero(t, client.Blob.GetX(ctx, id).Count) + client.Blob.Create(). + SetID(id). + OnConflictColumns(blob.FieldID). + Update(func(set *ent.BlobUpsert) { + set.AddCount(1) + }). + ExecX(ctx) + require.Equal(t, 1, client.Blob.GetX(ctx, id).Count) + + d := client.Doc.Create().SaveX(ctx) + client.Doc.Create(). + SetID(d.ID). + OnConflictColumns(doc.FieldID). + SetText("Hello World"). + UpdateNewValues(). + ExecX(ctx) + require.Equal(t, "Hello World", client.Doc.GetX(ctx, d.ID).Text) + }) } // clearDefault clears the id's default for non-postgres dialects. diff --git a/entc/integration/customid/ent/blob.go b/entc/integration/customid/ent/blob.go index 2d65810b1..3d4c5aa9e 100644 --- a/entc/integration/customid/ent/blob.go +++ b/entc/integration/customid/ent/blob.go @@ -22,6 +22,8 @@ type Blob struct { ID uuid.UUID `json:"id,omitempty"` // UUID holds the value of the "uuid" field. UUID uuid.UUID `json:"uuid,omitempty"` + // Count holds the value of the "count" field. + Count int `json:"count,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the BlobQuery when eager-loading is set. Edges BlobEdges `json:"edges"` @@ -67,6 +69,8 @@ func (*Blob) scanValues(columns []string) ([]interface{}, error) { values := make([]interface{}, len(columns)) for i := range columns { switch columns[i] { + case blob.FieldCount: + values[i] = new(sql.NullInt64) case blob.FieldID, blob.FieldUUID: values[i] = new(uuid.UUID) case blob.ForeignKeys[0]: // blob_parent @@ -98,6 +102,12 @@ func (b *Blob) assignValues(columns []string, values []interface{}) error { } else if value != nil { b.UUID = *value } + case blob.FieldCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field count", values[i]) + } else if value.Valid { + b.Count = int(value.Int64) + } case blob.ForeignKeys[0]: if value, ok := values[i].(*sql.NullScanner); !ok { return fmt.Errorf("unexpected type %T for field blob_parent", values[i]) @@ -145,6 +155,8 @@ func (b *Blob) String() string { builder.WriteString(fmt.Sprintf("id=%v", b.ID)) builder.WriteString(", uuid=") builder.WriteString(fmt.Sprintf("%v", b.UUID)) + builder.WriteString(", count=") + builder.WriteString(fmt.Sprintf("%v", b.Count)) builder.WriteByte(')') return builder.String() } diff --git a/entc/integration/customid/ent/blob/blob.go b/entc/integration/customid/ent/blob/blob.go index 2906bff2e..28a94d87b 100644 --- a/entc/integration/customid/ent/blob/blob.go +++ b/entc/integration/customid/ent/blob/blob.go @@ -17,6 +17,8 @@ const ( FieldID = "id" // FieldUUID holds the string denoting the uuid field in the database. FieldUUID = "uuid" + // FieldCount holds the string denoting the count field in the database. + FieldCount = "count" // EdgeParent holds the string denoting the parent edge name in mutations. EdgeParent = "parent" // EdgeLinks holds the string denoting the links edge name in mutations. @@ -35,6 +37,7 @@ const ( var Columns = []string{ FieldID, FieldUUID, + FieldCount, } // ForeignKeys holds the SQL foreign-keys that are owned by the "blobs" @@ -67,6 +70,8 @@ func ValidColumn(column string) bool { var ( // DefaultUUID holds the default value on creation for the "uuid" field. DefaultUUID func() uuid.UUID + // DefaultCount holds the default value on creation for the "count" field. + DefaultCount int // DefaultID holds the default value on creation for the "id" field. DefaultID func() uuid.UUID ) diff --git a/entc/integration/customid/ent/blob/where.go b/entc/integration/customid/ent/blob/where.go index 70cc532ac..57750c049 100644 --- a/entc/integration/customid/ent/blob/where.go +++ b/entc/integration/customid/ent/blob/where.go @@ -103,6 +103,13 @@ func UUID(v uuid.UUID) predicate.Blob { }) } +// Count applies equality check predicate on the "count" field. It's identical to CountEQ. +func Count(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCount), v)) + }) +} + // UUIDEQ applies the EQ predicate on the "uuid" field. func UUIDEQ(v uuid.UUID) predicate.Blob { return predicate.Blob(func(s *sql.Selector) { @@ -179,6 +186,82 @@ func UUIDLTE(v uuid.UUID) predicate.Blob { }) } +// CountEQ applies the EQ predicate on the "count" field. +func CountEQ(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldCount), v)) + }) +} + +// CountNEQ applies the NEQ predicate on the "count" field. +func CountNEQ(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldCount), v)) + }) +} + +// CountIn applies the In predicate on the "count" field. +func CountIn(vs ...int) predicate.Blob { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Blob(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(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldCount), v...)) + }) +} + +// CountNotIn applies the NotIn predicate on the "count" field. +func CountNotIn(vs ...int) predicate.Blob { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Blob(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(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldCount), v...)) + }) +} + +// CountGT applies the GT predicate on the "count" field. +func CountGT(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldCount), v)) + }) +} + +// CountGTE applies the GTE predicate on the "count" field. +func CountGTE(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldCount), v)) + }) +} + +// CountLT applies the LT predicate on the "count" field. +func CountLT(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldCount), v)) + }) +} + +// CountLTE applies the LTE predicate on the "count" field. +func CountLTE(v int) predicate.Blob { + return predicate.Blob(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldCount), v)) + }) +} + // HasParent applies the HasEdge predicate on the "parent" edge. func HasParent() predicate.Blob { return predicate.Blob(func(s *sql.Selector) { diff --git a/entc/integration/customid/ent/blob_create.go b/entc/integration/customid/ent/blob_create.go index 5de89087f..1cd183f65 100644 --- a/entc/integration/customid/ent/blob_create.go +++ b/entc/integration/customid/ent/blob_create.go @@ -11,6 +11,8 @@ import ( "errors" "fmt" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/blob" "entgo.io/ent/schema/field" @@ -22,6 +24,7 @@ type BlobCreate struct { config mutation *BlobMutation hooks []Hook + conflict []sql.ConflictOption } // SetUUID sets the "uuid" field. @@ -30,6 +33,20 @@ func (bc *BlobCreate) SetUUID(u uuid.UUID) *BlobCreate { return bc } +// SetCount sets the "count" field. +func (bc *BlobCreate) SetCount(i int) *BlobCreate { + bc.mutation.SetCount(i) + return bc +} + +// SetNillableCount sets the "count" field if the given value is not nil. +func (bc *BlobCreate) SetNillableCount(i *int) *BlobCreate { + if i != nil { + bc.SetCount(*i) + } + return bc +} + // SetID sets the "id" field. func (bc *BlobCreate) SetID(u uuid.UUID) *BlobCreate { bc.mutation.SetID(u) @@ -145,6 +162,10 @@ func (bc *BlobCreate) defaults() { v := blob.DefaultUUID() bc.mutation.SetUUID(v) } + if _, ok := bc.mutation.Count(); !ok { + v := blob.DefaultCount + bc.mutation.SetCount(v) + } if _, ok := bc.mutation.ID(); !ok { v := blob.DefaultID() bc.mutation.SetID(v) @@ -156,6 +177,9 @@ func (bc *BlobCreate) check() error { if _, ok := bc.mutation.UUID(); !ok { return &ValidationError{Name: "uuid", err: errors.New(`ent: missing required field "Blob.uuid"`)} } + if _, ok := bc.mutation.Count(); !ok { + return &ValidationError{Name: "count", err: errors.New(`ent: missing required field "Blob.count"`)} + } return nil } @@ -168,7 +192,7 @@ func (bc *BlobCreate) sqlSave(ctx context.Context) (*Blob, error) { return nil, err } if _spec.ID.Value != nil { - _node.ID = _spec.ID.Value.(uuid.UUID) + _node.ID = *_spec.ID.Value.(*uuid.UUID) } return _node, nil } @@ -184,9 +208,10 @@ func (bc *BlobCreate) createSpec() (*Blob, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = bc.conflict if id, ok := bc.mutation.ID(); ok { _node.ID = id - _spec.ID.Value = id + _spec.ID.Value = &id } if value, ok := bc.mutation.UUID(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ @@ -196,6 +221,14 @@ func (bc *BlobCreate) createSpec() (*Blob, *sqlgraph.CreateSpec) { }) _node.UUID = value } + if value, ok := bc.mutation.Count(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: blob.FieldCount, + }) + _node.Count = value + } if nodes := bc.mutation.ParentIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, @@ -238,10 +271,215 @@ func (bc *BlobCreate) createSpec() (*Blob, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Blob.Create(). +// SetUUID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BlobUpsert) { +// SetUUID(v+v). +// }). +// Exec(ctx) +// +func (bc *BlobCreate) OnConflict(opts ...sql.ConflictOption) *BlobUpsertOne { + bc.conflict = opts + return &BlobUpsertOne{ + create: bc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (bc *BlobCreate) OnConflictColumns(columns ...string) *BlobUpsertOne { + bc.conflict = append(bc.conflict, sql.ConflictColumns(columns...)) + return &BlobUpsertOne{ + create: bc, + } +} + +type ( + // BlobUpsertOne is the builder for "upsert"-ing + // one Blob node. + BlobUpsertOne struct { + create *BlobCreate + } + + // BlobUpsert is the "OnConflict" setter. + BlobUpsert struct { + *sql.UpdateSet + } +) + +// SetUUID sets the "uuid" field. +func (u *BlobUpsert) SetUUID(v uuid.UUID) *BlobUpsert { + u.Set(blob.FieldUUID, v) + return u +} + +// UpdateUUID sets the "uuid" field to the value that was provided on create. +func (u *BlobUpsert) UpdateUUID() *BlobUpsert { + u.SetExcluded(blob.FieldUUID) + return u +} + +// SetCount sets the "count" field. +func (u *BlobUpsert) SetCount(v int) *BlobUpsert { + u.Set(blob.FieldCount, v) + return u +} + +// UpdateCount sets the "count" field to the value that was provided on create. +func (u *BlobUpsert) UpdateCount() *BlobUpsert { + u.SetExcluded(blob.FieldCount) + return u +} + +// AddCount adds v to the "count" field. +func (u *BlobUpsert) AddCount(v int) *BlobUpsert { + u.Add(blob.FieldCount, v) + return u +} + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(blob.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *BlobUpsertOne) UpdateNewValues() *BlobUpsertOne { + 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.ID(); exists { + s.SetIgnore(blob.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *BlobUpsertOne) Ignore() *BlobUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BlobUpsertOne) DoNothing() *BlobUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BlobCreate.OnConflict +// documentation for more info. +func (u *BlobUpsertOne) Update(set func(*BlobUpsert)) *BlobUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BlobUpsert{UpdateSet: update}) + })) + return u +} + +// SetUUID sets the "uuid" field. +func (u *BlobUpsertOne) SetUUID(v uuid.UUID) *BlobUpsertOne { + return u.Update(func(s *BlobUpsert) { + s.SetUUID(v) + }) +} + +// UpdateUUID sets the "uuid" field to the value that was provided on create. +func (u *BlobUpsertOne) UpdateUUID() *BlobUpsertOne { + return u.Update(func(s *BlobUpsert) { + s.UpdateUUID() + }) +} + +// SetCount sets the "count" field. +func (u *BlobUpsertOne) SetCount(v int) *BlobUpsertOne { + return u.Update(func(s *BlobUpsert) { + s.SetCount(v) + }) +} + +// AddCount adds v to the "count" field. +func (u *BlobUpsertOne) AddCount(v int) *BlobUpsertOne { + return u.Update(func(s *BlobUpsert) { + s.AddCount(v) + }) +} + +// UpdateCount sets the "count" field to the value that was provided on create. +func (u *BlobUpsertOne) UpdateCount() *BlobUpsertOne { + return u.Update(func(s *BlobUpsert) { + s.UpdateCount() + }) +} + +// Exec executes the query. +func (u *BlobUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BlobCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BlobUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *BlobUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: BlobUpsertOne.ID is not supported by MySQL driver. Use BlobUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *BlobUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // BlobCreateBulk is the builder for creating many Blob entities in bulk. type BlobCreateBulk struct { config builders []*BlobCreate + conflict []sql.ConflictOption } // Save creates the Blob entities in the database. @@ -268,6 +506,7 @@ func (bcb *BlobCreateBulk) Save(ctx context.Context) ([]*Blob, error) { _, err = mutators[i+1].Mutate(root, bcb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = bcb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, bcb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -317,3 +556,154 @@ func (bcb *BlobCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Blob.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BlobUpsert) { +// SetUUID(v+v). +// }). +// Exec(ctx) +// +func (bcb *BlobCreateBulk) OnConflict(opts ...sql.ConflictOption) *BlobUpsertBulk { + bcb.conflict = opts + return &BlobUpsertBulk{ + create: bcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (bcb *BlobCreateBulk) OnConflictColumns(columns ...string) *BlobUpsertBulk { + bcb.conflict = append(bcb.conflict, sql.ConflictColumns(columns...)) + return &BlobUpsertBulk{ + create: bcb, + } +} + +// BlobUpsertBulk is the builder for "upsert"-ing +// a bulk of Blob nodes. +type BlobUpsertBulk struct { + create *BlobCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(blob.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *BlobUpsertBulk) UpdateNewValues() *BlobUpsertBulk { + 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.ID(); exists { + s.SetIgnore(blob.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Blob.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *BlobUpsertBulk) Ignore() *BlobUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BlobUpsertBulk) DoNothing() *BlobUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BlobCreateBulk.OnConflict +// documentation for more info. +func (u *BlobUpsertBulk) Update(set func(*BlobUpsert)) *BlobUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BlobUpsert{UpdateSet: update}) + })) + return u +} + +// SetUUID sets the "uuid" field. +func (u *BlobUpsertBulk) SetUUID(v uuid.UUID) *BlobUpsertBulk { + return u.Update(func(s *BlobUpsert) { + s.SetUUID(v) + }) +} + +// UpdateUUID sets the "uuid" field to the value that was provided on create. +func (u *BlobUpsertBulk) UpdateUUID() *BlobUpsertBulk { + return u.Update(func(s *BlobUpsert) { + s.UpdateUUID() + }) +} + +// SetCount sets the "count" field. +func (u *BlobUpsertBulk) SetCount(v int) *BlobUpsertBulk { + return u.Update(func(s *BlobUpsert) { + s.SetCount(v) + }) +} + +// AddCount adds v to the "count" field. +func (u *BlobUpsertBulk) AddCount(v int) *BlobUpsertBulk { + return u.Update(func(s *BlobUpsert) { + s.AddCount(v) + }) +} + +// UpdateCount sets the "count" field to the value that was provided on create. +func (u *BlobUpsertBulk) UpdateCount() *BlobUpsertBulk { + return u.Update(func(s *BlobUpsert) { + s.UpdateCount() + }) +} + +// Exec executes the query. +func (u *BlobUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the BlobCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BlobCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BlobUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/blob_update.go b/entc/integration/customid/ent/blob_update.go index e15f9c707..d0625aecd 100644 --- a/entc/integration/customid/ent/blob_update.go +++ b/entc/integration/customid/ent/blob_update.go @@ -38,6 +38,27 @@ func (bu *BlobUpdate) SetUUID(u uuid.UUID) *BlobUpdate { return bu } +// SetCount sets the "count" field. +func (bu *BlobUpdate) SetCount(i int) *BlobUpdate { + bu.mutation.ResetCount() + bu.mutation.SetCount(i) + return bu +} + +// SetNillableCount sets the "count" field if the given value is not nil. +func (bu *BlobUpdate) SetNillableCount(i *int) *BlobUpdate { + if i != nil { + bu.SetCount(*i) + } + return bu +} + +// AddCount adds i to the "count" field. +func (bu *BlobUpdate) AddCount(i int) *BlobUpdate { + bu.mutation.AddCount(i) + return bu +} + // SetParentID sets the "parent" edge to the Blob entity by ID. func (bu *BlobUpdate) SetParentID(id uuid.UUID) *BlobUpdate { bu.mutation.SetParentID(id) @@ -183,6 +204,20 @@ func (bu *BlobUpdate) sqlSave(ctx context.Context) (n int, err error) { Column: blob.FieldUUID, }) } + if value, ok := bu.mutation.Count(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: blob.FieldCount, + }) + } + if value, ok := bu.mutation.AddedCount(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: blob.FieldCount, + }) + } if bu.mutation.ParentCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, @@ -297,6 +332,27 @@ func (buo *BlobUpdateOne) SetUUID(u uuid.UUID) *BlobUpdateOne { return buo } +// SetCount sets the "count" field. +func (buo *BlobUpdateOne) SetCount(i int) *BlobUpdateOne { + buo.mutation.ResetCount() + buo.mutation.SetCount(i) + return buo +} + +// SetNillableCount sets the "count" field if the given value is not nil. +func (buo *BlobUpdateOne) SetNillableCount(i *int) *BlobUpdateOne { + if i != nil { + buo.SetCount(*i) + } + return buo +} + +// AddCount adds i to the "count" field. +func (buo *BlobUpdateOne) AddCount(i int) *BlobUpdateOne { + buo.mutation.AddCount(i) + return buo +} + // SetParentID sets the "parent" edge to the Blob entity by ID. func (buo *BlobUpdateOne) SetParentID(id uuid.UUID) *BlobUpdateOne { buo.mutation.SetParentID(id) @@ -466,6 +522,20 @@ func (buo *BlobUpdateOne) sqlSave(ctx context.Context) (_node *Blob, err error) Column: blob.FieldUUID, }) } + if value, ok := buo.mutation.Count(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: blob.FieldCount, + }) + } + if value, ok := buo.mutation.AddedCount(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Value: value, + Column: blob.FieldCount, + }) + } if buo.mutation.ParentCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2O, diff --git a/entc/integration/customid/ent/car_create.go b/entc/integration/customid/ent/car_create.go index 3e32e64ad..8e3200707 100644 --- a/entc/integration/customid/ent/car_create.go +++ b/entc/integration/customid/ent/car_create.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/car" "entgo.io/ent/entc/integration/customid/ent/pet" @@ -22,6 +23,7 @@ type CarCreate struct { config mutation *CarMutation hooks []Hook + conflict []sql.ConflictOption } // SetBeforeID sets the "before_id" field. @@ -200,6 +202,7 @@ func (cc *CarCreate) createSpec() (*Car, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = cc.conflict if id, ok := cc.mutation.ID(); ok { _node.ID = id _spec.ID.Value = id @@ -251,10 +254,275 @@ func (cc *CarCreate) createSpec() (*Car, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Car.Create(). +// SetBeforeID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.CarUpsert) { +// SetBeforeID(v+v). +// }). +// Exec(ctx) +// +func (cc *CarCreate) OnConflict(opts ...sql.ConflictOption) *CarUpsertOne { + cc.conflict = opts + return &CarUpsertOne{ + create: cc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (cc *CarCreate) OnConflictColumns(columns ...string) *CarUpsertOne { + cc.conflict = append(cc.conflict, sql.ConflictColumns(columns...)) + return &CarUpsertOne{ + create: cc, + } +} + +type ( + // CarUpsertOne is the builder for "upsert"-ing + // one Car node. + CarUpsertOne struct { + create *CarCreate + } + + // CarUpsert is the "OnConflict" setter. + CarUpsert struct { + *sql.UpdateSet + } +) + +// SetBeforeID sets the "before_id" field. +func (u *CarUpsert) SetBeforeID(v float64) *CarUpsert { + u.Set(car.FieldBeforeID, v) + return u +} + +// UpdateBeforeID sets the "before_id" field to the value that was provided on create. +func (u *CarUpsert) UpdateBeforeID() *CarUpsert { + u.SetExcluded(car.FieldBeforeID) + return u +} + +// AddBeforeID adds v to the "before_id" field. +func (u *CarUpsert) AddBeforeID(v float64) *CarUpsert { + u.Add(car.FieldBeforeID, v) + return u +} + +// ClearBeforeID clears the value of the "before_id" field. +func (u *CarUpsert) ClearBeforeID() *CarUpsert { + u.SetNull(car.FieldBeforeID) + return u +} + +// SetAfterID sets the "after_id" field. +func (u *CarUpsert) SetAfterID(v float64) *CarUpsert { + u.Set(car.FieldAfterID, v) + return u +} + +// UpdateAfterID sets the "after_id" field to the value that was provided on create. +func (u *CarUpsert) UpdateAfterID() *CarUpsert { + u.SetExcluded(car.FieldAfterID) + return u +} + +// AddAfterID adds v to the "after_id" field. +func (u *CarUpsert) AddAfterID(v float64) *CarUpsert { + u.Add(car.FieldAfterID, v) + return u +} + +// ClearAfterID clears the value of the "after_id" field. +func (u *CarUpsert) ClearAfterID() *CarUpsert { + u.SetNull(car.FieldAfterID) + return u +} + +// SetModel sets the "model" field. +func (u *CarUpsert) SetModel(v string) *CarUpsert { + u.Set(car.FieldModel, v) + return u +} + +// UpdateModel sets the "model" field to the value that was provided on create. +func (u *CarUpsert) UpdateModel() *CarUpsert { + u.SetExcluded(car.FieldModel) + return u +} + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(car.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *CarUpsertOne) UpdateNewValues() *CarUpsertOne { + 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.ID(); exists { + s.SetIgnore(car.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *CarUpsertOne) Ignore() *CarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *CarUpsertOne) DoNothing() *CarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the CarCreate.OnConflict +// documentation for more info. +func (u *CarUpsertOne) Update(set func(*CarUpsert)) *CarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&CarUpsert{UpdateSet: update}) + })) + return u +} + +// SetBeforeID sets the "before_id" field. +func (u *CarUpsertOne) SetBeforeID(v float64) *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.SetBeforeID(v) + }) +} + +// AddBeforeID adds v to the "before_id" field. +func (u *CarUpsertOne) AddBeforeID(v float64) *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.AddBeforeID(v) + }) +} + +// UpdateBeforeID sets the "before_id" field to the value that was provided on create. +func (u *CarUpsertOne) UpdateBeforeID() *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.UpdateBeforeID() + }) +} + +// ClearBeforeID clears the value of the "before_id" field. +func (u *CarUpsertOne) ClearBeforeID() *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.ClearBeforeID() + }) +} + +// SetAfterID sets the "after_id" field. +func (u *CarUpsertOne) SetAfterID(v float64) *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.SetAfterID(v) + }) +} + +// AddAfterID adds v to the "after_id" field. +func (u *CarUpsertOne) AddAfterID(v float64) *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.AddAfterID(v) + }) +} + +// UpdateAfterID sets the "after_id" field to the value that was provided on create. +func (u *CarUpsertOne) UpdateAfterID() *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.UpdateAfterID() + }) +} + +// ClearAfterID clears the value of the "after_id" field. +func (u *CarUpsertOne) ClearAfterID() *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.ClearAfterID() + }) +} + +// SetModel sets the "model" field. +func (u *CarUpsertOne) SetModel(v string) *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.SetModel(v) + }) +} + +// UpdateModel sets the "model" field to the value that was provided on create. +func (u *CarUpsertOne) UpdateModel() *CarUpsertOne { + return u.Update(func(s *CarUpsert) { + s.UpdateModel() + }) +} + +// Exec executes the query. +func (u *CarUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for CarCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *CarUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *CarUpsertOne) ID(ctx context.Context) (id int, err error) { + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *CarUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // CarCreateBulk is the builder for creating many Car entities in bulk. type CarCreateBulk struct { config builders []*CarCreate + conflict []sql.ConflictOption } // Save creates the Car entities in the database. @@ -280,6 +548,7 @@ func (ccb *CarCreateBulk) Save(ctx context.Context) ([]*Car, error) { _, err = mutators[i+1].Mutate(root, ccb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = ccb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, ccb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -333,3 +602,189 @@ func (ccb *CarCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Car.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.CarUpsert) { +// SetBeforeID(v+v). +// }). +// Exec(ctx) +// +func (ccb *CarCreateBulk) OnConflict(opts ...sql.ConflictOption) *CarUpsertBulk { + ccb.conflict = opts + return &CarUpsertBulk{ + create: ccb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (ccb *CarCreateBulk) OnConflictColumns(columns ...string) *CarUpsertBulk { + ccb.conflict = append(ccb.conflict, sql.ConflictColumns(columns...)) + return &CarUpsertBulk{ + create: ccb, + } +} + +// CarUpsertBulk is the builder for "upsert"-ing +// a bulk of Car nodes. +type CarUpsertBulk struct { + create *CarCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(car.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *CarUpsertBulk) UpdateNewValues() *CarUpsertBulk { + 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.ID(); exists { + s.SetIgnore(car.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Car.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *CarUpsertBulk) Ignore() *CarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *CarUpsertBulk) DoNothing() *CarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the CarCreateBulk.OnConflict +// documentation for more info. +func (u *CarUpsertBulk) Update(set func(*CarUpsert)) *CarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&CarUpsert{UpdateSet: update}) + })) + return u +} + +// SetBeforeID sets the "before_id" field. +func (u *CarUpsertBulk) SetBeforeID(v float64) *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.SetBeforeID(v) + }) +} + +// AddBeforeID adds v to the "before_id" field. +func (u *CarUpsertBulk) AddBeforeID(v float64) *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.AddBeforeID(v) + }) +} + +// UpdateBeforeID sets the "before_id" field to the value that was provided on create. +func (u *CarUpsertBulk) UpdateBeforeID() *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.UpdateBeforeID() + }) +} + +// ClearBeforeID clears the value of the "before_id" field. +func (u *CarUpsertBulk) ClearBeforeID() *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.ClearBeforeID() + }) +} + +// SetAfterID sets the "after_id" field. +func (u *CarUpsertBulk) SetAfterID(v float64) *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.SetAfterID(v) + }) +} + +// AddAfterID adds v to the "after_id" field. +func (u *CarUpsertBulk) AddAfterID(v float64) *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.AddAfterID(v) + }) +} + +// UpdateAfterID sets the "after_id" field to the value that was provided on create. +func (u *CarUpsertBulk) UpdateAfterID() *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.UpdateAfterID() + }) +} + +// ClearAfterID clears the value of the "after_id" field. +func (u *CarUpsertBulk) ClearAfterID() *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.ClearAfterID() + }) +} + +// SetModel sets the "model" field. +func (u *CarUpsertBulk) SetModel(v string) *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.SetModel(v) + }) +} + +// UpdateModel sets the "model" field to the value that was provided on create. +func (u *CarUpsertBulk) UpdateModel() *CarUpsertBulk { + return u.Update(func(s *CarUpsert) { + s.UpdateModel() + }) +} + +// Exec executes the query. +func (u *CarUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the CarCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for CarCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *CarUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/doc_create.go b/entc/integration/customid/ent/doc_create.go index 641055f21..284fdc836 100644 --- a/entc/integration/customid/ent/doc_create.go +++ b/entc/integration/customid/ent/doc_create.go @@ -8,8 +8,11 @@ package ent import ( "context" + "errors" "fmt" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/doc" "entgo.io/ent/entc/integration/customid/ent/schema" @@ -21,6 +24,7 @@ type DocCreate struct { config mutation *DocMutation hooks []Hook + conflict []sql.ConflictOption } // SetText sets the "text" field. @@ -181,7 +185,7 @@ func (dc *DocCreate) sqlSave(ctx context.Context) (*Doc, error) { return nil, err } if _spec.ID.Value != nil { - _node.ID = _spec.ID.Value.(schema.DocID) + _node.ID = *_spec.ID.Value.(*schema.DocID) } return _node, nil } @@ -197,9 +201,10 @@ func (dc *DocCreate) createSpec() (*Doc, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = dc.conflict if id, ok := dc.mutation.ID(); ok { _node.ID = id - _spec.ID.Value = id + _spec.ID.Value = &id } if value, ok := dc.mutation.Text(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ @@ -251,10 +256,189 @@ func (dc *DocCreate) createSpec() (*Doc, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Doc.Create(). +// SetText(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.DocUpsert) { +// SetText(v+v). +// }). +// Exec(ctx) +// +func (dc *DocCreate) OnConflict(opts ...sql.ConflictOption) *DocUpsertOne { + dc.conflict = opts + return &DocUpsertOne{ + create: dc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (dc *DocCreate) OnConflictColumns(columns ...string) *DocUpsertOne { + dc.conflict = append(dc.conflict, sql.ConflictColumns(columns...)) + return &DocUpsertOne{ + create: dc, + } +} + +type ( + // DocUpsertOne is the builder for "upsert"-ing + // one Doc node. + DocUpsertOne struct { + create *DocCreate + } + + // DocUpsert is the "OnConflict" setter. + DocUpsert struct { + *sql.UpdateSet + } +) + +// SetText sets the "text" field. +func (u *DocUpsert) SetText(v string) *DocUpsert { + u.Set(doc.FieldText, v) + return u +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *DocUpsert) UpdateText() *DocUpsert { + u.SetExcluded(doc.FieldText) + return u +} + +// ClearText clears the value of the "text" field. +func (u *DocUpsert) ClearText() *DocUpsert { + u.SetNull(doc.FieldText) + return u +} + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(doc.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *DocUpsertOne) UpdateNewValues() *DocUpsertOne { + 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.ID(); exists { + s.SetIgnore(doc.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *DocUpsertOne) Ignore() *DocUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *DocUpsertOne) DoNothing() *DocUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the DocCreate.OnConflict +// documentation for more info. +func (u *DocUpsertOne) Update(set func(*DocUpsert)) *DocUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&DocUpsert{UpdateSet: update}) + })) + return u +} + +// SetText sets the "text" field. +func (u *DocUpsertOne) SetText(v string) *DocUpsertOne { + return u.Update(func(s *DocUpsert) { + s.SetText(v) + }) +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *DocUpsertOne) UpdateText() *DocUpsertOne { + return u.Update(func(s *DocUpsert) { + s.UpdateText() + }) +} + +// ClearText clears the value of the "text" field. +func (u *DocUpsertOne) ClearText() *DocUpsertOne { + return u.Update(func(s *DocUpsert) { + s.ClearText() + }) +} + +// Exec executes the query. +func (u *DocUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for DocCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *DocUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *DocUpsertOne) ID(ctx context.Context) (id schema.DocID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: DocUpsertOne.ID is not supported by MySQL driver. Use DocUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *DocUpsertOne) IDX(ctx context.Context) schema.DocID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // DocCreateBulk is the builder for creating many Doc entities in bulk. type DocCreateBulk struct { config builders []*DocCreate + conflict []sql.ConflictOption } // Save creates the Doc entities in the database. @@ -281,6 +465,7 @@ func (dcb *DocCreateBulk) Save(ctx context.Context) ([]*Doc, error) { _, err = mutators[i+1].Mutate(root, dcb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = dcb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, dcb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -330,3 +515,140 @@ func (dcb *DocCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Doc.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.DocUpsert) { +// SetText(v+v). +// }). +// Exec(ctx) +// +func (dcb *DocCreateBulk) OnConflict(opts ...sql.ConflictOption) *DocUpsertBulk { + dcb.conflict = opts + return &DocUpsertBulk{ + create: dcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (dcb *DocCreateBulk) OnConflictColumns(columns ...string) *DocUpsertBulk { + dcb.conflict = append(dcb.conflict, sql.ConflictColumns(columns...)) + return &DocUpsertBulk{ + create: dcb, + } +} + +// DocUpsertBulk is the builder for "upsert"-ing +// a bulk of Doc nodes. +type DocUpsertBulk struct { + create *DocCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(doc.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *DocUpsertBulk) UpdateNewValues() *DocUpsertBulk { + 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.ID(); exists { + s.SetIgnore(doc.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Doc.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *DocUpsertBulk) Ignore() *DocUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *DocUpsertBulk) DoNothing() *DocUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the DocCreateBulk.OnConflict +// documentation for more info. +func (u *DocUpsertBulk) Update(set func(*DocUpsert)) *DocUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&DocUpsert{UpdateSet: update}) + })) + return u +} + +// SetText sets the "text" field. +func (u *DocUpsertBulk) SetText(v string) *DocUpsertBulk { + return u.Update(func(s *DocUpsert) { + s.SetText(v) + }) +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *DocUpsertBulk) UpdateText() *DocUpsertBulk { + return u.Update(func(s *DocUpsert) { + s.UpdateText() + }) +} + +// ClearText clears the value of the "text" field. +func (u *DocUpsertBulk) ClearText() *DocUpsertBulk { + return u.Update(func(s *DocUpsert) { + s.ClearText() + }) +} + +// Exec executes the query. +func (u *DocUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the DocCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for DocCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *DocUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/generate.go b/entc/integration/customid/ent/generate.go index fac82d5d2..9cc837d0b 100644 --- a/entc/integration/customid/ent/generate.go +++ b/entc/integration/customid/ent/generate.go @@ -4,4 +4,4 @@ package ent -//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --header "// Copyright 2019-present Facebook Inc. All rights reserved.\n// This source code is licensed under the Apache 2.0 license found\n// in the LICENSE file in the root directory of this source tree.\n\n// Code generated by entc, DO NOT EDIT." ./schema +//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/upsert --header "// Copyright 2019-present Facebook Inc. All rights reserved.\n// This source code is licensed under the Apache 2.0 license found\n// in the LICENSE file in the root directory of this source tree.\n\n// Code generated by entc, DO NOT EDIT." ./schema diff --git a/entc/integration/customid/ent/group_create.go b/entc/integration/customid/ent/group_create.go index 9831ee4e0..767095fca 100644 --- a/entc/integration/customid/ent/group_create.go +++ b/entc/integration/customid/ent/group_create.go @@ -8,8 +8,10 @@ package ent import ( "context" + "errors" "fmt" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/group" "entgo.io/ent/entc/integration/customid/ent/user" @@ -21,6 +23,7 @@ type GroupCreate struct { config mutation *GroupMutation hooks []Hook + conflict []sql.ConflictOption } // SetID sets the "id" field. @@ -143,6 +146,7 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = gc.conflict if id, ok := gc.mutation.ID(); ok { _node.ID = id _spec.ID.Value = id @@ -169,10 +173,139 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.Create(). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (gc *GroupCreate) OnConflict(opts ...sql.ConflictOption) *GroupUpsertOne { + gc.conflict = opts + return &GroupUpsertOne{ + create: gc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (gc *GroupCreate) OnConflictColumns(columns ...string) *GroupUpsertOne { + gc.conflict = append(gc.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertOne{ + create: gc, + } +} + +type ( + // GroupUpsertOne is the builder for "upsert"-ing + // one Group node. + GroupUpsertOne struct { + create *GroupCreate + } + + // GroupUpsert is the "OnConflict" setter. + GroupUpsert struct { + *sql.UpdateSet + } +) + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *GroupUpsertOne) UpdateNewValues() *GroupUpsertOne { + 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.ID(); exists { + s.SetIgnore(group.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *GroupUpsertOne) Ignore() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertOne) DoNothing() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreate.OnConflict +// documentation for more info. +func (u *GroupUpsertOne) Update(set func(*GroupUpsert)) *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *GroupUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GroupUpsertOne) ID(ctx context.Context) (id int, err error) { + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GroupUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // GroupCreateBulk is the builder for creating many Group entities in bulk. type GroupCreateBulk struct { config builders []*GroupCreate + conflict []sql.ConflictOption } // Save creates the Group entities in the database. @@ -198,6 +331,7 @@ func (gcb *GroupCreateBulk) Save(ctx context.Context) ([]*Group, error) { _, err = mutators[i+1].Mutate(root, gcb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = gcb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, gcb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -251,3 +385,114 @@ func (gcb *GroupCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (gcb *GroupCreateBulk) OnConflict(opts ...sql.ConflictOption) *GroupUpsertBulk { + gcb.conflict = opts + return &GroupUpsertBulk{ + create: gcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (gcb *GroupCreateBulk) OnConflictColumns(columns ...string) *GroupUpsertBulk { + gcb.conflict = append(gcb.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertBulk{ + create: gcb, + } +} + +// GroupUpsertBulk is the builder for "upsert"-ing +// a bulk of Group nodes. +type GroupUpsertBulk struct { + create *GroupCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *GroupUpsertBulk) UpdateNewValues() *GroupUpsertBulk { + 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.ID(); exists { + s.SetIgnore(group.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *GroupUpsertBulk) Ignore() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertBulk) DoNothing() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreateBulk.OnConflict +// documentation for more info. +func (u *GroupUpsertBulk) Update(set func(*GroupUpsert)) *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *GroupUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GroupCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/migrate/schema.go b/entc/integration/customid/ent/migrate/schema.go index 8a88607f3..c39891b63 100644 --- a/entc/integration/customid/ent/migrate/schema.go +++ b/entc/integration/customid/ent/migrate/schema.go @@ -16,6 +16,7 @@ var ( BlobsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true, Default: "uuid_generate_v4()"}, {Name: "uuid", Type: field.TypeUUID, Unique: true}, + {Name: "count", Type: field.TypeInt, Default: 0}, {Name: "blob_parent", Type: field.TypeUUID, Unique: true, Nullable: true}, } // BlobsTable holds the schema information for the "blobs" table. @@ -26,7 +27,7 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "blobs_blobs_parent", - Columns: []*schema.Column{BlobsColumns[2]}, + Columns: []*schema.Column{BlobsColumns[3]}, RefColumns: []*schema.Column{BlobsColumns[0]}, OnDelete: schema.SetNull, }, diff --git a/entc/integration/customid/ent/mixinid_create.go b/entc/integration/customid/ent/mixinid_create.go index f7bcf0123..56d5d6012 100644 --- a/entc/integration/customid/ent/mixinid_create.go +++ b/entc/integration/customid/ent/mixinid_create.go @@ -11,6 +11,8 @@ import ( "errors" "fmt" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/mixinid" "entgo.io/ent/schema/field" @@ -22,6 +24,7 @@ type MixinIDCreate struct { config mutation *MixinIDMutation hooks []Hook + conflict []sql.ConflictOption } // SetSomeField sets the "some_field" field. @@ -139,7 +142,7 @@ func (mic *MixinIDCreate) sqlSave(ctx context.Context) (*MixinID, error) { return nil, err } if _spec.ID.Value != nil { - _node.ID = _spec.ID.Value.(uuid.UUID) + _node.ID = *_spec.ID.Value.(*uuid.UUID) } return _node, nil } @@ -155,9 +158,10 @@ func (mic *MixinIDCreate) createSpec() (*MixinID, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = mic.conflict if id, ok := mic.mutation.ID(); ok { _node.ID = id - _spec.ID.Value = id + _spec.ID.Value = &id } if value, ok := mic.mutation.SomeField(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ @@ -178,10 +182,202 @@ func (mic *MixinIDCreate) createSpec() (*MixinID, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MixinID.Create(). +// SetSomeField(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MixinIDUpsert) { +// SetSomeField(v+v). +// }). +// Exec(ctx) +// +func (mic *MixinIDCreate) OnConflict(opts ...sql.ConflictOption) *MixinIDUpsertOne { + mic.conflict = opts + return &MixinIDUpsertOne{ + create: mic, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (mic *MixinIDCreate) OnConflictColumns(columns ...string) *MixinIDUpsertOne { + mic.conflict = append(mic.conflict, sql.ConflictColumns(columns...)) + return &MixinIDUpsertOne{ + create: mic, + } +} + +type ( + // MixinIDUpsertOne is the builder for "upsert"-ing + // one MixinID node. + MixinIDUpsertOne struct { + create *MixinIDCreate + } + + // MixinIDUpsert is the "OnConflict" setter. + MixinIDUpsert struct { + *sql.UpdateSet + } +) + +// SetSomeField sets the "some_field" field. +func (u *MixinIDUpsert) SetSomeField(v string) *MixinIDUpsert { + u.Set(mixinid.FieldSomeField, v) + return u +} + +// UpdateSomeField sets the "some_field" field to the value that was provided on create. +func (u *MixinIDUpsert) UpdateSomeField() *MixinIDUpsert { + u.SetExcluded(mixinid.FieldSomeField) + return u +} + +// SetMixinField sets the "mixin_field" field. +func (u *MixinIDUpsert) SetMixinField(v string) *MixinIDUpsert { + u.Set(mixinid.FieldMixinField, v) + return u +} + +// UpdateMixinField sets the "mixin_field" field to the value that was provided on create. +func (u *MixinIDUpsert) UpdateMixinField() *MixinIDUpsert { + u.SetExcluded(mixinid.FieldMixinField) + return u +} + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(mixinid.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *MixinIDUpsertOne) UpdateNewValues() *MixinIDUpsertOne { + 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.ID(); exists { + s.SetIgnore(mixinid.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *MixinIDUpsertOne) Ignore() *MixinIDUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MixinIDUpsertOne) DoNothing() *MixinIDUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MixinIDCreate.OnConflict +// documentation for more info. +func (u *MixinIDUpsertOne) Update(set func(*MixinIDUpsert)) *MixinIDUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MixinIDUpsert{UpdateSet: update}) + })) + return u +} + +// SetSomeField sets the "some_field" field. +func (u *MixinIDUpsertOne) SetSomeField(v string) *MixinIDUpsertOne { + return u.Update(func(s *MixinIDUpsert) { + s.SetSomeField(v) + }) +} + +// UpdateSomeField sets the "some_field" field to the value that was provided on create. +func (u *MixinIDUpsertOne) UpdateSomeField() *MixinIDUpsertOne { + return u.Update(func(s *MixinIDUpsert) { + s.UpdateSomeField() + }) +} + +// SetMixinField sets the "mixin_field" field. +func (u *MixinIDUpsertOne) SetMixinField(v string) *MixinIDUpsertOne { + return u.Update(func(s *MixinIDUpsert) { + s.SetMixinField(v) + }) +} + +// UpdateMixinField sets the "mixin_field" field to the value that was provided on create. +func (u *MixinIDUpsertOne) UpdateMixinField() *MixinIDUpsertOne { + return u.Update(func(s *MixinIDUpsert) { + s.UpdateMixinField() + }) +} + +// Exec executes the query. +func (u *MixinIDUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MixinIDCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MixinIDUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *MixinIDUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: MixinIDUpsertOne.ID is not supported by MySQL driver. Use MixinIDUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *MixinIDUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // MixinIDCreateBulk is the builder for creating many MixinID entities in bulk. type MixinIDCreateBulk struct { config builders []*MixinIDCreate + conflict []sql.ConflictOption } // Save creates the MixinID entities in the database. @@ -208,6 +404,7 @@ func (micb *MixinIDCreateBulk) Save(ctx context.Context) ([]*MixinID, error) { _, err = mutators[i+1].Mutate(root, micb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = micb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, micb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -257,3 +454,147 @@ func (micb *MixinIDCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MixinID.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MixinIDUpsert) { +// SetSomeField(v+v). +// }). +// Exec(ctx) +// +func (micb *MixinIDCreateBulk) OnConflict(opts ...sql.ConflictOption) *MixinIDUpsertBulk { + micb.conflict = opts + return &MixinIDUpsertBulk{ + create: micb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (micb *MixinIDCreateBulk) OnConflictColumns(columns ...string) *MixinIDUpsertBulk { + micb.conflict = append(micb.conflict, sql.ConflictColumns(columns...)) + return &MixinIDUpsertBulk{ + create: micb, + } +} + +// MixinIDUpsertBulk is the builder for "upsert"-ing +// a bulk of MixinID nodes. +type MixinIDUpsertBulk struct { + create *MixinIDCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(mixinid.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *MixinIDUpsertBulk) UpdateNewValues() *MixinIDUpsertBulk { + 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.ID(); exists { + s.SetIgnore(mixinid.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MixinID.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *MixinIDUpsertBulk) Ignore() *MixinIDUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MixinIDUpsertBulk) DoNothing() *MixinIDUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MixinIDCreateBulk.OnConflict +// documentation for more info. +func (u *MixinIDUpsertBulk) Update(set func(*MixinIDUpsert)) *MixinIDUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MixinIDUpsert{UpdateSet: update}) + })) + return u +} + +// SetSomeField sets the "some_field" field. +func (u *MixinIDUpsertBulk) SetSomeField(v string) *MixinIDUpsertBulk { + return u.Update(func(s *MixinIDUpsert) { + s.SetSomeField(v) + }) +} + +// UpdateSomeField sets the "some_field" field to the value that was provided on create. +func (u *MixinIDUpsertBulk) UpdateSomeField() *MixinIDUpsertBulk { + return u.Update(func(s *MixinIDUpsert) { + s.UpdateSomeField() + }) +} + +// SetMixinField sets the "mixin_field" field. +func (u *MixinIDUpsertBulk) SetMixinField(v string) *MixinIDUpsertBulk { + return u.Update(func(s *MixinIDUpsert) { + s.SetMixinField(v) + }) +} + +// UpdateMixinField sets the "mixin_field" field to the value that was provided on create. +func (u *MixinIDUpsertBulk) UpdateMixinField() *MixinIDUpsertBulk { + return u.Update(func(s *MixinIDUpsert) { + s.UpdateMixinField() + }) +} + +// Exec executes the query. +func (u *MixinIDUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the MixinIDCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MixinIDCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MixinIDUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/mutation.go b/entc/integration/customid/ent/mutation.go index e66795b9b..0a00201db 100644 --- a/entc/integration/customid/ent/mutation.go +++ b/entc/integration/customid/ent/mutation.go @@ -52,6 +52,8 @@ type BlobMutation struct { typ string id *uuid.UUID uuid *uuid.UUID + count *int + addcount *int clearedFields map[string]struct{} parent *uuid.UUID clearedparent bool @@ -184,6 +186,62 @@ func (m *BlobMutation) ResetUUID() { m.uuid = nil } +// SetCount sets the "count" field. +func (m *BlobMutation) SetCount(i int) { + m.count = &i + m.addcount = nil +} + +// Count returns the value of the "count" field in the mutation. +func (m *BlobMutation) Count() (r int, exists bool) { + v := m.count + if v == nil { + return + } + return *v, true +} + +// OldCount returns the old "count" field's value of the Blob entity. +// If the Blob object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BlobMutation) OldCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCount: %w", err) + } + return oldValue.Count, nil +} + +// AddCount adds i to the "count" field. +func (m *BlobMutation) AddCount(i int) { + if m.addcount != nil { + *m.addcount += i + } else { + m.addcount = &i + } +} + +// AddedCount returns the value that was added to the "count" field in this mutation. +func (m *BlobMutation) AddedCount() (r int, exists bool) { + v := m.addcount + if v == nil { + return + } + return *v, true +} + +// ResetCount resets all changes to the "count" field. +func (m *BlobMutation) ResetCount() { + m.count = nil + m.addcount = nil +} + // SetParentID sets the "parent" edge to the Blob entity by id. func (m *BlobMutation) SetParentID(id uuid.UUID) { m.parent = &id @@ -296,10 +354,13 @@ func (m *BlobMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *BlobMutation) Fields() []string { - fields := make([]string, 0, 1) + fields := make([]string, 0, 2) if m.uuid != nil { fields = append(fields, blob.FieldUUID) } + if m.count != nil { + fields = append(fields, blob.FieldCount) + } return fields } @@ -310,6 +371,8 @@ func (m *BlobMutation) Field(name string) (ent.Value, bool) { switch name { case blob.FieldUUID: return m.UUID() + case blob.FieldCount: + return m.Count() } return nil, false } @@ -321,6 +384,8 @@ func (m *BlobMutation) OldField(ctx context.Context, name string) (ent.Value, er switch name { case blob.FieldUUID: return m.OldUUID(ctx) + case blob.FieldCount: + return m.OldCount(ctx) } return nil, fmt.Errorf("unknown Blob field %s", name) } @@ -337,6 +402,13 @@ func (m *BlobMutation) SetField(name string, value ent.Value) error { } m.SetUUID(v) return nil + case blob.FieldCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCount(v) + return nil } return fmt.Errorf("unknown Blob field %s", name) } @@ -344,13 +416,21 @@ func (m *BlobMutation) SetField(name string, value ent.Value) error { // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. func (m *BlobMutation) AddedFields() []string { - return nil + var fields []string + if m.addcount != nil { + fields = append(fields, blob.FieldCount) + } + return fields } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. func (m *BlobMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case blob.FieldCount: + return m.AddedCount() + } return nil, false } @@ -359,6 +439,13 @@ func (m *BlobMutation) AddedField(name string) (ent.Value, bool) { // type. func (m *BlobMutation) AddField(name string, value ent.Value) error { switch name { + case blob.FieldCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddCount(v) + return nil } return fmt.Errorf("unknown Blob numeric field %s", name) } @@ -389,6 +476,9 @@ func (m *BlobMutation) ResetField(name string) error { case blob.FieldUUID: m.ResetUUID() return nil + case blob.FieldCount: + m.ResetCount() + return nil } return fmt.Errorf("unknown Blob field %s", name) } diff --git a/entc/integration/customid/ent/note_create.go b/entc/integration/customid/ent/note_create.go index 41d745dde..d5e09dde2 100644 --- a/entc/integration/customid/ent/note_create.go +++ b/entc/integration/customid/ent/note_create.go @@ -8,8 +8,11 @@ package ent import ( "context" + "errors" "fmt" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/note" "entgo.io/ent/entc/integration/customid/ent/schema" @@ -21,6 +24,7 @@ type NoteCreate struct { config mutation *NoteMutation hooks []Hook + conflict []sql.ConflictOption } // SetText sets the "text" field. @@ -197,6 +201,7 @@ func (nc *NoteCreate) createSpec() (*Note, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = nc.conflict if id, ok := nc.mutation.ID(); ok { _node.ID = id _spec.ID.Value = id @@ -251,10 +256,189 @@ func (nc *NoteCreate) createSpec() (*Note, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Note.Create(). +// SetText(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NoteUpsert) { +// SetText(v+v). +// }). +// Exec(ctx) +// +func (nc *NoteCreate) OnConflict(opts ...sql.ConflictOption) *NoteUpsertOne { + nc.conflict = opts + return &NoteUpsertOne{ + create: nc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (nc *NoteCreate) OnConflictColumns(columns ...string) *NoteUpsertOne { + nc.conflict = append(nc.conflict, sql.ConflictColumns(columns...)) + return &NoteUpsertOne{ + create: nc, + } +} + +type ( + // NoteUpsertOne is the builder for "upsert"-ing + // one Note node. + NoteUpsertOne struct { + create *NoteCreate + } + + // NoteUpsert is the "OnConflict" setter. + NoteUpsert struct { + *sql.UpdateSet + } +) + +// SetText sets the "text" field. +func (u *NoteUpsert) SetText(v string) *NoteUpsert { + u.Set(note.FieldText, v) + return u +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *NoteUpsert) UpdateText() *NoteUpsert { + u.SetExcluded(note.FieldText) + return u +} + +// ClearText clears the value of the "text" field. +func (u *NoteUpsert) ClearText() *NoteUpsert { + u.SetNull(note.FieldText) + return u +} + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(note.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *NoteUpsertOne) UpdateNewValues() *NoteUpsertOne { + 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.ID(); exists { + s.SetIgnore(note.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *NoteUpsertOne) Ignore() *NoteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NoteUpsertOne) DoNothing() *NoteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NoteCreate.OnConflict +// documentation for more info. +func (u *NoteUpsertOne) Update(set func(*NoteUpsert)) *NoteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NoteUpsert{UpdateSet: update}) + })) + return u +} + +// SetText sets the "text" field. +func (u *NoteUpsertOne) SetText(v string) *NoteUpsertOne { + return u.Update(func(s *NoteUpsert) { + s.SetText(v) + }) +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *NoteUpsertOne) UpdateText() *NoteUpsertOne { + return u.Update(func(s *NoteUpsert) { + s.UpdateText() + }) +} + +// ClearText clears the value of the "text" field. +func (u *NoteUpsertOne) ClearText() *NoteUpsertOne { + return u.Update(func(s *NoteUpsert) { + s.ClearText() + }) +} + +// Exec executes the query. +func (u *NoteUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NoteCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NoteUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *NoteUpsertOne) ID(ctx context.Context) (id schema.NoteID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: NoteUpsertOne.ID is not supported by MySQL driver. Use NoteUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *NoteUpsertOne) IDX(ctx context.Context) schema.NoteID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // NoteCreateBulk is the builder for creating many Note entities in bulk. type NoteCreateBulk struct { config builders []*NoteCreate + conflict []sql.ConflictOption } // Save creates the Note entities in the database. @@ -281,6 +465,7 @@ func (ncb *NoteCreateBulk) Save(ctx context.Context) ([]*Note, error) { _, err = mutators[i+1].Mutate(root, ncb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = ncb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, ncb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -330,3 +515,140 @@ func (ncb *NoteCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Note.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NoteUpsert) { +// SetText(v+v). +// }). +// Exec(ctx) +// +func (ncb *NoteCreateBulk) OnConflict(opts ...sql.ConflictOption) *NoteUpsertBulk { + ncb.conflict = opts + return &NoteUpsertBulk{ + create: ncb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (ncb *NoteCreateBulk) OnConflictColumns(columns ...string) *NoteUpsertBulk { + ncb.conflict = append(ncb.conflict, sql.ConflictColumns(columns...)) + return &NoteUpsertBulk{ + create: ncb, + } +} + +// NoteUpsertBulk is the builder for "upsert"-ing +// a bulk of Note nodes. +type NoteUpsertBulk struct { + create *NoteCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(note.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *NoteUpsertBulk) UpdateNewValues() *NoteUpsertBulk { + 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.ID(); exists { + s.SetIgnore(note.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Note.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *NoteUpsertBulk) Ignore() *NoteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NoteUpsertBulk) DoNothing() *NoteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NoteCreateBulk.OnConflict +// documentation for more info. +func (u *NoteUpsertBulk) Update(set func(*NoteUpsert)) *NoteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NoteUpsert{UpdateSet: update}) + })) + return u +} + +// SetText sets the "text" field. +func (u *NoteUpsertBulk) SetText(v string) *NoteUpsertBulk { + return u.Update(func(s *NoteUpsert) { + s.SetText(v) + }) +} + +// UpdateText sets the "text" field to the value that was provided on create. +func (u *NoteUpsertBulk) UpdateText() *NoteUpsertBulk { + return u.Update(func(s *NoteUpsert) { + s.UpdateText() + }) +} + +// ClearText clears the value of the "text" field. +func (u *NoteUpsertBulk) ClearText() *NoteUpsertBulk { + return u.Update(func(s *NoteUpsert) { + s.ClearText() + }) +} + +// Exec executes the query. +func (u *NoteUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the NoteCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NoteCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NoteUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/pet_create.go b/entc/integration/customid/ent/pet_create.go index e6769afaf..f6df07b9a 100644 --- a/entc/integration/customid/ent/pet_create.go +++ b/entc/integration/customid/ent/pet_create.go @@ -8,8 +8,11 @@ package ent import ( "context" + "errors" "fmt" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/car" "entgo.io/ent/entc/integration/customid/ent/pet" @@ -22,6 +25,7 @@ type PetCreate struct { config mutation *PetMutation hooks []Hook + conflict []sql.ConflictOption } // SetID sets the "id" field. @@ -218,6 +222,7 @@ func (pc *PetCreate) createSpec() (*Pet, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = pc.conflict if id, ok := pc.mutation.ID(); ok { _node.ID = id _spec.ID.Value = id @@ -303,10 +308,144 @@ func (pc *PetCreate) createSpec() (*Pet, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Pet.Create(). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (pc *PetCreate) OnConflict(opts ...sql.ConflictOption) *PetUpsertOne { + pc.conflict = opts + return &PetUpsertOne{ + create: pc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (pc *PetCreate) OnConflictColumns(columns ...string) *PetUpsertOne { + pc.conflict = append(pc.conflict, sql.ConflictColumns(columns...)) + return &PetUpsertOne{ + create: pc, + } +} + +type ( + // PetUpsertOne is the builder for "upsert"-ing + // one Pet node. + PetUpsertOne struct { + create *PetCreate + } + + // PetUpsert is the "OnConflict" setter. + PetUpsert struct { + *sql.UpdateSet + } +) + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(pet.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *PetUpsertOne) UpdateNewValues() *PetUpsertOne { + 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.ID(); exists { + s.SetIgnore(pet.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *PetUpsertOne) Ignore() *PetUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *PetUpsertOne) DoNothing() *PetUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the PetCreate.OnConflict +// documentation for more info. +func (u *PetUpsertOne) Update(set func(*PetUpsert)) *PetUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&PetUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *PetUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for PetCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *PetUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *PetUpsertOne) ID(ctx context.Context) (id string, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: PetUpsertOne.ID is not supported by MySQL driver. Use PetUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *PetUpsertOne) IDX(ctx context.Context) string { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // PetCreateBulk is the builder for creating many Pet entities in bulk. type PetCreateBulk struct { config builders []*PetCreate + conflict []sql.ConflictOption } // Save creates the Pet entities in the database. @@ -333,6 +472,7 @@ func (pcb *PetCreateBulk) Save(ctx context.Context) ([]*Pet, error) { _, err = mutators[i+1].Mutate(root, pcb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = pcb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, pcb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -382,3 +522,114 @@ func (pcb *PetCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Pet.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (pcb *PetCreateBulk) OnConflict(opts ...sql.ConflictOption) *PetUpsertBulk { + pcb.conflict = opts + return &PetUpsertBulk{ + create: pcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (pcb *PetCreateBulk) OnConflictColumns(columns ...string) *PetUpsertBulk { + pcb.conflict = append(pcb.conflict, sql.ConflictColumns(columns...)) + return &PetUpsertBulk{ + create: pcb, + } +} + +// PetUpsertBulk is the builder for "upsert"-ing +// a bulk of Pet nodes. +type PetUpsertBulk struct { + create *PetCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(pet.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *PetUpsertBulk) UpdateNewValues() *PetUpsertBulk { + 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.ID(); exists { + s.SetIgnore(pet.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Pet.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *PetUpsertBulk) Ignore() *PetUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *PetUpsertBulk) DoNothing() *PetUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the PetCreateBulk.OnConflict +// documentation for more info. +func (u *PetUpsertBulk) Update(set func(*PetUpsert)) *PetUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&PetUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *PetUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the PetCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for PetCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *PetUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/customid/ent/runtime.go b/entc/integration/customid/ent/runtime.go index 430cf2ff1..8fab2cb1e 100644 --- a/entc/integration/customid/ent/runtime.go +++ b/entc/integration/customid/ent/runtime.go @@ -27,6 +27,10 @@ func init() { blobDescUUID := blobFields[1].Descriptor() // blob.DefaultUUID holds the default value on creation for the uuid field. blob.DefaultUUID = blobDescUUID.Default.(func() uuid.UUID) + // blobDescCount is the schema descriptor for count field. + blobDescCount := blobFields[2].Descriptor() + // blob.DefaultCount holds the default value on creation for the count field. + blob.DefaultCount = blobDescCount.Default.(int) // blobDescID is the schema descriptor for id field. blobDescID := blobFields[0].Descriptor() // blob.DefaultID holds the default value on creation for the id field. diff --git a/entc/integration/customid/ent/schema/blob.go b/entc/integration/customid/ent/schema/blob.go index aa39d87c1..3bf940448 100644 --- a/entc/integration/customid/ent/schema/blob.go +++ b/entc/integration/customid/ent/schema/blob.go @@ -30,6 +30,8 @@ func (Blob) Fields() []ent.Field { field.UUID("uuid", uuid.UUID{}). Default(uuid.New). Unique(), + field.Int("count"). + Default(0), } } diff --git a/entc/integration/customid/ent/user_create.go b/entc/integration/customid/ent/user_create.go index 8b7b56894..9d1cd9127 100644 --- a/entc/integration/customid/ent/user_create.go +++ b/entc/integration/customid/ent/user_create.go @@ -8,8 +8,10 @@ package ent import ( "context" + "errors" "fmt" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/entc/integration/customid/ent/group" "entgo.io/ent/entc/integration/customid/ent/pet" @@ -22,6 +24,7 @@ type UserCreate struct { config mutation *UserMutation hooks []Hook + conflict []sql.ConflictOption } // SetID sets the "id" field. @@ -193,6 +196,7 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { }, } ) + _spec.OnConflict = uc.conflict if id, ok := uc.mutation.ID(); ok { _node.ID = id _spec.ID.Value = id @@ -277,10 +281,139 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.User.Create(). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (uc *UserCreate) OnConflict(opts ...sql.ConflictOption) *UserUpsertOne { + uc.conflict = opts + return &UserUpsertOne{ + create: uc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (uc *UserCreate) OnConflictColumns(columns ...string) *UserUpsertOne { + uc.conflict = append(uc.conflict, sql.ConflictColumns(columns...)) + return &UserUpsertOne{ + create: uc, + } +} + +type ( + // UserUpsertOne is the builder for "upsert"-ing + // one User node. + UserUpsertOne struct { + create *UserCreate + } + + // UserUpsert is the "OnConflict" setter. + UserUpsert struct { + *sql.UpdateSet + } +) + +// UpdateNewValues updates the fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(user.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *UserUpsertOne) UpdateNewValues() *UserUpsertOne { + 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.ID(); exists { + s.SetIgnore(user.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *UserUpsertOne) Ignore() *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserUpsertOne) DoNothing() *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserCreate.OnConflict +// documentation for more info. +func (u *UserUpsertOne) Update(set func(*UserUpsert)) *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *UserUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *UserUpsertOne) ID(ctx context.Context) (id int, err error) { + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *UserUpsertOne) IDX(ctx context.Context) int { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // UserCreateBulk is the builder for creating many User entities in bulk. type UserCreateBulk struct { config builders []*UserCreate + conflict []sql.ConflictOption } // Save creates the User entities in the database. @@ -306,6 +439,7 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) { _, err = mutators[i+1].Mutate(root, ucb.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = ucb.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, ucb.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -359,3 +493,114 @@ func (ucb *UserCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.User.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +// +func (ucb *UserCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserUpsertBulk { + ucb.conflict = opts + return &UserUpsertBulk{ + create: ucb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +// +func (ucb *UserCreateBulk) OnConflictColumns(columns ...string) *UserUpsertBulk { + ucb.conflict = append(ucb.conflict, sql.ConflictColumns(columns...)) + return &UserUpsertBulk{ + create: ucb, + } +} + +// UserUpsertBulk is the builder for "upsert"-ing +// a bulk of User nodes. +type UserUpsertBulk struct { + create *UserCreateBulk +} + +// UpdateNewValues updates the fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(user.FieldID) +// }), +// ). +// Exec(ctx) +// +func (u *UserUpsertBulk) UpdateNewValues() *UserUpsertBulk { + 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.ID(); exists { + s.SetIgnore(user.FieldID) + return + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +// +func (u *UserUpsertBulk) Ignore() *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserUpsertBulk) DoNothing() *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserCreateBulk.OnConflict +// documentation for more info. +func (u *UserUpsertBulk) Update(set func(*UserUpsert)) *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserUpsert{UpdateSet: update}) + })) + return u +} + +// Exec executes the query. +func (u *UserUpsertBulk) Exec(ctx context.Context) error { + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/entc/integration/edgefield/ent/car_create.go b/entc/integration/edgefield/ent/car_create.go index 20b163e63..c1208f99a 100644 --- a/entc/integration/edgefield/ent/car_create.go +++ b/entc/integration/edgefield/ent/car_create.go @@ -150,7 +150,7 @@ func (cc *CarCreate) sqlSave(ctx context.Context) (*Car, error) { return nil, err } if _spec.ID.Value != nil { - _node.ID = _spec.ID.Value.(uuid.UUID) + _node.ID = *_spec.ID.Value.(*uuid.UUID) } return _node, nil } @@ -168,7 +168,7 @@ func (cc *CarCreate) createSpec() (*Car, *sqlgraph.CreateSpec) { ) if id, ok := cc.mutation.ID(); ok { _node.ID = id - _spec.ID.Value = id + _spec.ID.Value = &id } if value, ok := cc.mutation.Number(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{