snapshot of https://github.com/ent/ent.git (master, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
67
entc/gen/template/dialect/sql/by.tmpl
Normal file
67
entc/gen/template/dialect/sql/by.tmpl
Normal file
@@ -0,0 +1,67 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{ define "dialect/sql/order/signature" -}}
|
||||
// OrderFunc applies an ordering on the sql selector.
|
||||
// Deprecated: Use Asc/Desc functions or the package builders instead.
|
||||
type OrderFunc func(*sql.Selector)
|
||||
|
||||
var (
|
||||
initCheck sync.Once
|
||||
columnCheck sql.ColumnCheck
|
||||
)
|
||||
|
||||
// checkColumn checks if the column exists in the given table.
|
||||
func checkColumn(t, c string) error {
|
||||
initCheck.Do(func() {
|
||||
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.Package }}.Table: {{ $n.Package }}.ValidColumn,
|
||||
{{- end }}
|
||||
})
|
||||
})
|
||||
return columnCheck(t, c)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/order/func" -}}
|
||||
{{- $f := $.Scope.Func -}}
|
||||
func(s *sql.Selector) {
|
||||
for _, f := range fields {
|
||||
if err := checkColumn(s.TableName(), f); err != nil {
|
||||
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("{{ base $.Config.Package }}: %w", err)})
|
||||
}
|
||||
s.OrderBy(sql.{{ $f }}(s.C(f)))
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{/* custom signature for group-by function */}}
|
||||
{{ define "dialect/sql/group/signature" -}}
|
||||
type AggregateFunc func(*sql.Selector) string
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/group/as" -}}
|
||||
func(s *sql.Selector) string {
|
||||
return sql.As(fn(s), end)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/group/func" -}}
|
||||
{{- $fn := $.Scope.Func -}}
|
||||
{{- $withField := $.Scope.WithField -}}
|
||||
func(s *sql.Selector) string {
|
||||
{{- if $withField }}
|
||||
if err := checkColumn(s.TableName(), field); err != nil {
|
||||
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("{{ base $.Config.Package }}: %w", err)})
|
||||
return ""
|
||||
}
|
||||
{{- end }}
|
||||
return sql.{{ if eq $fn "Mean" }}Avg{{ else }}{{ $fn }}{{ end }}({{ if $withField }}s.C(field){{ else }}"*"{{ end }})
|
||||
}
|
||||
{{- end }}
|
||||
492
entc/gen/template/dialect/sql/create.tmpl
Normal file
492
entc/gen/template/dialect/sql/create.tmpl
Normal file
@@ -0,0 +1,492 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{ define "dialect/sql/create" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlSave(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
if err := {{ $receiver }}.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_node, _spec {{ if $.HasValueScanner }}, err {{ end }} := {{ $receiver }}.createSpec()
|
||||
{{- if $.HasValueScanner }}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $.HasBlobFields }}
|
||||
_blobs := ent.NewBlobs({{ $mutation }}.blobOpeners.{{ $.Name }})
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
_blobDV, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: %w", err)
|
||||
}
|
||||
var _blobData []byte
|
||||
switch v := _blobDV.(type) {
|
||||
case []byte:
|
||||
_blobData = v
|
||||
case string:
|
||||
_blobData = []byte(v)
|
||||
default:
|
||||
return nil, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: expected []byte or string, got %T", _blobDV)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_node.{{ $f.BlobKeyColumn }} = &k
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, []byte(value),
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_node.{{ $f.BlobKeyColumn }} = &k
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, value,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_node.{{ $f.BlobKeyColumn }} = &k
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- end }}
|
||||
_node.{{ $f.StructField }} = value
|
||||
}
|
||||
{{- else }}
|
||||
if r, ok := {{ $mutation }}.{{ $f.StructField }}(); ok {
|
||||
_blobData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: reading {{ $f.Name }}: %w", err)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_node.{{ $f.BlobKeyColumn }} = &k
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
_blobCleanup, err := _blobs.Create(ctx, {{ if $.HasBlobRefCheckedFields }}&sqlgraph.BlobSpec{
|
||||
Driver: {{ $receiver }}.driver,
|
||||
Table: {{ $.Package }}.Table,
|
||||
Columns: map[string]string{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: "{{ $f.BlobKeyColumn }}",
|
||||
{{- end }}
|
||||
},
|
||||
CheckRefs: map[string]bool{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: true,
|
||||
{{- end }}
|
||||
},
|
||||
}{{ else }}nil{{ end }})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
if err := sqlgraph.CreateNode(ctx, {{ $receiver }}.driver, _spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
return nil, errors.Join(err, _blobCleanup(ctx))
|
||||
{{- else }}
|
||||
return nil, err
|
||||
{{- end }}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
if txd, ok := {{ $receiver }}.driver.(*txDriver); ok {
|
||||
txd.mu.Lock()
|
||||
txd.onRollback = append(txd.onRollback, func(next Rollbacker) Rollbacker {
|
||||
return RollbackFunc(func(ctx context.Context, tx *Tx) error {
|
||||
err := next.Rollback(ctx, tx)
|
||||
return errors.Join(err, _blobCleanup(ctx))
|
||||
})
|
||||
})
|
||||
txd.mu.Unlock()
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $.HasCompositeID }}
|
||||
{{- else if or $.ID.HasValueScanner $.ID.Type.ValueScanner (not $.ID.Type.Numeric) }}
|
||||
if _spec.ID.Value != nil {
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
sv, ok := _spec.ID.Value.(field.ValueScanner)
|
||||
if !ok {
|
||||
sv = {{ $.ID.ScanValueFunc }}()
|
||||
if err := sv.Scan(_spec.ID.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if value, err := {{ $.ID.FromValueFunc }}(sv); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
_node.ID = value
|
||||
}
|
||||
{{- else }}
|
||||
{{- /* 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) }}
|
||||
if id, ok := _spec.ID.Value.(*{{ $.ID.Type }}); ok {
|
||||
_node.ID = *id
|
||||
{{- else }}
|
||||
if id, ok := _spec.ID.Value.({{ $.ID.Type }}); ok {
|
||||
_node.ID = id
|
||||
{{- end }}
|
||||
{{- if $.ID.Type.ValueScanner }}
|
||||
} else if err := _node.ID.Scan(_spec.ID.Value); err != nil {
|
||||
return nil, err
|
||||
{{- else }}
|
||||
} else {
|
||||
return nil, fmt.Errorf("unexpected {{ $.Name }}.ID type: %T", _spec.ID.Value)
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
{{- else }}
|
||||
{{- if $.ID.UserDefined }}
|
||||
if _spec.ID.Value != _node.ID {
|
||||
{{- end }}
|
||||
id := _spec.ID.Value.(int64)
|
||||
_node.ID = {{ $.ID.Type }}(id)
|
||||
{{- if $.ID.UserDefined }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.HasOneFieldID }}
|
||||
{{ $mutation }}.{{ $.ID.BuilderField }} = &_node.{{ $.ID.StructField }}
|
||||
{{ $mutation }}.done = true
|
||||
{{- end }}
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) createSpec() (*{{ $.Name }}, *sqlgraph.CreateSpec{{ if $.HasValueScanner }}, error{{ end }}) {
|
||||
var (
|
||||
_node = &{{ $.Name }}{config: {{ $receiver }}.config}
|
||||
_spec = sqlgraph.NewCreateSpec({{ $.Package }}.Table, {{ if $.HasOneFieldID }}sqlgraph.NewFieldSpec({{ $.Package }}.{{ $.ID.Constant }}, field.{{ $.ID.Type.ConstName }}){{ else }}nil{{ end }})
|
||||
)
|
||||
{{- /* Allow mutating the sqlgraph.CreateSpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and (not $.HasCompositeID) $.ID.UserDefined }}
|
||||
if id, ok := {{ $mutation }}.{{ $.ID.MutationGet }}(); ok {
|
||||
_node.ID = id
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
vv, err := {{ $.ID.ValueFunc }}(id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_spec.ID.Value = vv
|
||||
{{- else }}
|
||||
_spec.ID.Value = {{ if and $.ID.Type.ValueScanner (not $.ID.Type.RType.IsPtr) }}&{{ end }}id
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{- if $f.IsBlobNoColumn }}{{ continue }}{{ end }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
vv, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_spec.SetField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, vv)
|
||||
{{- else }}
|
||||
_spec.SetField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, value)
|
||||
{{- end }}
|
||||
_node.{{ $f.StructField }} = {{ if $f.NillableValue }}&{{ end }}value
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
if nodes := {{ $mutation }}.{{ $e.StructField }}IDs(); len(nodes) > 0 {
|
||||
{{- with extend $ "Edge" $e "Nodes" true "Zero" "nil" }}
|
||||
{{ template "dialect/sql/defedge" . }}{{/* defined in sql/update.tmpl */}}
|
||||
{{- end }}
|
||||
{{- if $e.OwnFK }}
|
||||
{{- $fk := $e.ForeignKey }}
|
||||
_node.{{ $fk.StructField }} = {{ if $fk.Field.NillableValue }}&{{ end }}nodes[0]
|
||||
{{- end }}
|
||||
_spec.Edges = append(_spec.Edges, edge)
|
||||
}
|
||||
{{- end }}
|
||||
return _node, _spec{{ if $.HasValueScanner }}, nil{{ end }}
|
||||
}
|
||||
|
||||
{{- /* Allow adding methods to the create-builder by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{/* Additional fields for the create builder. */}}
|
||||
{{ define "dialect/sql/create/fields" }}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Additional fields for the create_bulk builder. */}}
|
||||
{{ define "dialect/sql/create_bulk/fields" }}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create_bulk/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/create_bulk" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
// Save creates the {{ $.Name }} entities in the database.
|
||||
func ({{ $receiver }} *{{ $builder }}) Save(ctx context.Context) ([]*{{ $.Name }}, error) {
|
||||
{{- /* Initialization error was set by MapCreateBulk. */}}
|
||||
if {{ $receiver }}.err != nil {
|
||||
return nil, {{ $receiver }}.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len({{ $receiver }}.builders))
|
||||
nodes := make([]*{{ $.Name }}, len({{ $receiver }}.builders))
|
||||
mutators := make([]Mutator, len({{ $receiver }}.builders))
|
||||
{{- if $.HasBlobFields }}
|
||||
_blobs := ent.NewBlobs({{ $receiver }}.blobOpeners.{{ $.Name }})
|
||||
{{- end }}
|
||||
for i := range {{ $receiver }}.builders {
|
||||
func(i int, root context.Context) {
|
||||
builder := {{ $receiver }}.builders[i]
|
||||
{{- if $.HasDefault }}
|
||||
builder.defaults()
|
||||
{{- end }}
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
mutation, ok := m.(*{{ $.MutationName }})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
if err := builder.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
var err error
|
||||
nodes[i], specs[i] {{ if $.HasValueScanner }}, err {{ end }}= builder.createSpec()
|
||||
{{- if $.HasValueScanner }}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $.HasBlobFields }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
if value, ok := mutation.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
_blobDV, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: %w", err)
|
||||
}
|
||||
var _blobData []byte
|
||||
switch v := _blobDV.(type) {
|
||||
case []byte:
|
||||
_blobData = v
|
||||
case string:
|
||||
_blobData = []byte(v)
|
||||
default:
|
||||
return nil, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: expected []byte or string, got %T", _blobDV)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
nodes[i].{{ $f.BlobKeyColumn }} = &k
|
||||
specs[i].SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, []byte(value),
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
nodes[i].{{ $f.BlobKeyColumn }} = &k
|
||||
specs[i].SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, value,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
nodes[i].{{ $f.BlobKeyColumn }} = &k
|
||||
specs[i].SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- end }}
|
||||
nodes[i].{{ $f.StructField }} = value
|
||||
}
|
||||
{{- else }}
|
||||
if r, ok := mutation.{{ $f.StructField }}(); ok {
|
||||
_blobData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: reading {{ $f.Name }}: %w", err)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
nodes[i].{{ $f.BlobKeyColumn }} = &k
|
||||
specs[i].SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, {{ $receiver }}.builders[i+1].mutation)
|
||||
} else {
|
||||
{{- if $.HasBlobFields }}
|
||||
// Write blobs before creating SQL rows so insert failures can clean up written objects.
|
||||
_blobCleanup, err := _blobs.Create(ctx, {{ if $.HasBlobRefCheckedFields }}&sqlgraph.BlobSpec{
|
||||
Driver: {{ $receiver }}.driver,
|
||||
Table: {{ $.Package }}.Table,
|
||||
Columns: map[string]string{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: "{{ $f.BlobKeyColumn }}",
|
||||
{{- end }}
|
||||
},
|
||||
CheckRefs: map[string]bool{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: true,
|
||||
{{- end }}
|
||||
},
|
||||
}{{ else }}nil{{ end }})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
|
||||
{{- /* Allow mutating the sqlgraph.BatchCreateSpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create_bulk/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
// Invoke the actual operation on the latest mutation in the chain.
|
||||
if err = sqlgraph.BatchCreate(ctx, {{ $receiver }}.driver, spec); err != nil {
|
||||
if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
return nil, errors.Join(err, _blobCleanup(ctx))
|
||||
{{- end }}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
if txd, ok := {{ $receiver }}.driver.(*txDriver); ok {
|
||||
txd.mu.Lock()
|
||||
txd.onRollback = append(txd.onRollback, func(next Rollbacker) Rollbacker {
|
||||
return RollbackFunc(func(ctx context.Context, tx *Tx) error {
|
||||
err := next.Rollback(ctx, tx)
|
||||
return errors.Join(err, _blobCleanup(ctx))
|
||||
})
|
||||
})
|
||||
txd.mu.Unlock()
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- if $.HasOneFieldID }}
|
||||
mutation.{{ $.ID.BuilderField }} = &nodes[i].{{ $.ID.StructField }}
|
||||
{{- if and (or $.ID.IsString $.ID.IsUUID $.ID.IsBytes $.ID.IsOther) (not $.ID.HasValueScanner) }}
|
||||
{{- /* Do nothing, because these 4 types must be supplied by the user. */ -}}
|
||||
{{- else if $.ID.HasValueScanner }}
|
||||
if specs[i].ID.Value != nil {
|
||||
sv, ok := specs[i].ID.Value.(field.ValueScanner)
|
||||
if !ok {
|
||||
sv = {{ $.ID.ScanValueFunc }}()
|
||||
if err := sv.Scan(specs[i].ID.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if id, err := {{ $.ID.FromValueFunc }}(sv); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
nodes[i].ID = id
|
||||
}
|
||||
}
|
||||
{{- else if or $.ID.Type.ValueScanner }}
|
||||
if specs[i].ID.Value != nil {
|
||||
if err := nodes[i].ID.Scan(specs[i].ID.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
if specs[i].ID.Value != nil {{ if $.ID.UserDefined }}&& nodes[i].ID == 0{{ end }} {
|
||||
id := specs[i].ID.Value.(int64)
|
||||
nodes[i].ID = {{ $.ID.Type }}(id)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
mutation.done = true
|
||||
return nodes[i], nil
|
||||
})
|
||||
for i := len(builder.hooks) - 1; i >= 0; i-- {
|
||||
mut = builder.hooks[i](mut)
|
||||
}
|
||||
mutators[i] = mut
|
||||
}(i, ctx)
|
||||
}
|
||||
if len(mutators) > 0 {
|
||||
if _, err := mutators[0].Mutate(ctx, {{ $receiver }}.builders[0].mutation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) SaveX(ctx context.Context) []*{{ $.Name }} {
|
||||
v, err := {{ $receiver }}.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func ({{ $receiver }} *{{ $builder }}) Exec(ctx context.Context) error {
|
||||
_, err := {{ $receiver }}.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) ExecX(ctx context.Context) {
|
||||
if err := {{ $receiver }}.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{- /* Allow adding methods to the create_bulk builder by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/create_bulk/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
181
entc/gen/template/dialect/sql/decode.tmpl
Normal file
181
entc/gen/template/dialect/sql/decode.tmpl
Normal file
@@ -0,0 +1,181 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{ define "dialect/sql/decode/one" }}
|
||||
{{ $receiver := $.Receiver }}
|
||||
|
||||
{{ $ctypes := dict }}
|
||||
{{ if $.HasOneFieldID }}
|
||||
{{- if not $.ID.HasValueScanner }}
|
||||
{{ $idscantype := $.ID.NewScanType }}
|
||||
{{ $ctypes = set $ctypes $idscantype (list $.ID.Constant) }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ range $f := $.Fields }}
|
||||
{{- if $f.HasValueScanner }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
{{- if or $f.IsBlobNoColumn $f.IsBlobLazy }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
{{ $names := list }}
|
||||
{{ if hasKey $ctypes $f.NewScanType }}
|
||||
{{ $names = get $ctypes $f.NewScanType }}
|
||||
{{ end }}
|
||||
{{ $names = append $names $f.Constant }}
|
||||
{{ $ctypes = set $ctypes $f.NewScanType $names }}
|
||||
{{ end }}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*{{ $.Name }}) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
{{- if and $.HasOneFieldID $.ID.HasValueScanner }}
|
||||
case {{ $.Package }}.{{ $.ID.Constant }}:
|
||||
values[i] = {{ $.ID.ScanValueFunc }}()
|
||||
{{- end }}
|
||||
{{- range $type, $columns := $ctypes }}
|
||||
case {{ range $i, $c := $columns }}{{ if ne $i 0 }},{{ end }}{{ $.Package }}.{{ $c }}{{ end }}:
|
||||
values[i] = {{ $type }}
|
||||
{{- end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.IsBlobNoColumn }}{{ continue }}{{ end }}
|
||||
{{- if $f.HasValueScanner }}
|
||||
case {{ $.Package }}.{{ $f.Constant }}:
|
||||
values[i] = {{ $f.ScanValueFunc }}()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $i, $fk := $.UnexportedForeignKeys }}
|
||||
{{- $f := $fk.Field }}
|
||||
case {{ $.Package }}.ForeignKeys[{{ $i }}]: // {{ $f.Name }}
|
||||
values[i] = {{ if not $f.UserDefined }}new(sql.NullInt64){{ else }}{{ $f.NewScanType }}{{ end }}
|
||||
{{- end }}
|
||||
{{- range $i, $bk := $.BlobKeys }}
|
||||
case {{ $.Package }}.BlobKeys[{{ $i }}]: // {{ $bk.Field.Name }}
|
||||
values[i] = new(sql.NullString)
|
||||
{{- end }}
|
||||
default:
|
||||
{{- /* In case of unknown column that was added by a modifier, predicate, etc., fallback to any. */}}
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||
// to the {{ $.Name }} fields.
|
||||
func ({{ $receiver }} *{{ $.Name }}) assignValues(columns []string, values []any) error {
|
||||
if m, n := len(values), len(columns); m < n {
|
||||
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||
}
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
{{- if $.HasOneFieldID }}
|
||||
case {{ $.Package }}.{{ $.ID.Constant }}:
|
||||
{{- if or $.ID.IsString $.ID.IsBytes $.ID.HasGoType $.ID.HasValueScanner }}
|
||||
{{- with extend $ "Idx" "i" "Field" $.ID "Rec" $receiver }}
|
||||
{{ template "dialect/sql/decode/field" . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
value, ok := values[i].(*sql.NullInt64)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field id", value)
|
||||
}
|
||||
{{ $receiver }}.ID = {{ $.ID.Type }}(value.Int64)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if or $f.IsBlobNoColumn $f.IsBlobLazy }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
case {{ $.Package }}.{{ $f.Constant }}:
|
||||
{{- with extend $ "Idx" "i" "Field" $f "Rec" $receiver }}
|
||||
{{ template "dialect/sql/decode/field" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $i, $fk := $.UnexportedForeignKeys }}
|
||||
{{- $f := $fk.Field }}
|
||||
case {{ if $fk.UserDefined }}{{ $.Package }}.{{ $.ID.Constant }}{{ else }}{{ $.Package }}.ForeignKeys[{{ $i }}]{{ end }}:
|
||||
{{- if or $fk.UserDefined (and $f.UserDefined (or $f.IsString $f.IsBytes $f.HasGoType)) }}
|
||||
{{- with extend $ "Idx" "i" "Field" $f "Rec" $receiver "StructField" $fk.StructField }}
|
||||
{{ template "dialect/sql/decode/field" . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||
return fmt.Errorf("unexpected type %T for edge-field {{ $f.Name}}", value)
|
||||
} else if value.Valid {
|
||||
{{ $receiver }}.{{ $fk.StructField }} = new({{ $f.Type }})
|
||||
{{ if and $f.Nillable (not $f.Type.Nillable) }}*{{ end }}{{ $receiver }}.{{ $fk.StructField }} = {{ $f.Type }}(value.Int64)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $i, $bk := $.BlobKeys }}
|
||||
case {{ $.Package }}.BlobKeys[{{ $i }}]:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field {{ $bk.Field.Name }}", values[i])
|
||||
} else if value.Valid {
|
||||
{{ $receiver }}.{{ $bk.StructField }} = &value.String
|
||||
}
|
||||
{{- end }}
|
||||
default:
|
||||
{{- /* In case of no match, allow getting this value by its name. */}}
|
||||
{{ $receiver }}.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// {{ $.ValueName }} returns the ent.Value that was dynamically selected and assigned to the {{ $.Name }}.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func ({{ $receiver }} *{{ $.Name }}) {{ $.ValueName }}(name string) (ent.Value, error) {
|
||||
return {{ $receiver }}.selectValues.Get(name)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/sql/decode/field" }}
|
||||
{{- $i := $.Scope.Idx -}}
|
||||
{{- $f := $.Scope.Field -}}
|
||||
{{- $ret := $.Scope.Rec -}}
|
||||
{{- $field := $f.StructField }}{{ with $.Scope.StructField }}{{ $field = . }}{{ end -}}
|
||||
{{- if $f.HasValueScanner -}}
|
||||
if value, err := {{ $f.FromValueFunc }}(values[{{ $i }}]); err != nil {
|
||||
return err
|
||||
} else {
|
||||
{{ $ret }}.{{ $field }} = value
|
||||
}
|
||||
{{- else if $f.IsJSON -}}
|
||||
if value, ok := values[{{ $i }}].(*{{ $f.ScanType }}); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field {{ $f.Name }}", values[{{ $i }}])
|
||||
} else if value != nil && len(*value) > 0 {
|
||||
if err := json.Unmarshal(*value, &{{ $ret }}.{{ $field }}); err != nil {
|
||||
return fmt.Errorf("unmarshal field {{ $f.Name }}: %w", err)
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
{{- $scantype := $f.ScanType -}}
|
||||
if value, ok := values[{{ $i }}].(*{{ $scantype }}); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field {{ $f.Name }}", values[{{ $i }}])
|
||||
{{- if hasPrefix $scantype "sql.Null" }}
|
||||
} else if value.Valid {
|
||||
{{- if $f.NillableValue }}
|
||||
{{ $ret }}.{{ $field }} = new({{ $f.Type }})
|
||||
*{{ $ret }}.{{ $field }} = {{ $f.ScanTypeField "value" }}
|
||||
{{- else }}
|
||||
{{ $ret }}.{{ $field }} = {{ $f.ScanTypeField "value" }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
} else if value != nil {
|
||||
{{ $ret }}.{{ $field }} = {{ if and (not $f.Nillable) (not $f.Type.RType.IsPtr) }}*{{ end }}value
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/decode/many" }}
|
||||
{{ end }}
|
||||
84
entc/gen/template/dialect/sql/delete.tmpl
Normal file
84
entc/gen/template/dialect/sql/delete.tmpl
Normal file
@@ -0,0 +1,84 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{ define "dialect/sql/delete" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
func ({{ $receiver}} *{{ $builder }}) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := sqlgraph.NewDeleteSpec({{ $.Package }}.Table, {{ if $.HasOneFieldID }}sqlgraph.NewFieldSpec({{ $.Package }}.{{ $.ID.Constant }}, field.{{ $.ID.Type.ConstName }}){{ else }}nil{{ end }})
|
||||
{{- /* Allow mutating the sqlgraph.DeleteSpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/delete/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if ps := {{ $mutation }}.Predicates(); len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
// Collect blob keys before deleting rows so we can remove blobs from storage afterward.
|
||||
_blobKeys, _blobErr := ent.NewBlobs({{ $mutation }}.blobOpeners.{{ $.Name }}).Delete(ctx, &sqlgraph.BlobSpec{
|
||||
Driver: {{ $receiver }}.driver,
|
||||
Predicate: _spec.Predicate,
|
||||
Table: {{ $.Package }}.Table,
|
||||
Columns: map[string]string{
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: "{{ $f.BlobKeyColumn }}",
|
||||
{{- end }}
|
||||
},
|
||||
{{- if $.HasBlobRefCheckedFields }}
|
||||
CheckRefs: map[string]bool{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: true,
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
})
|
||||
if _blobErr != nil {
|
||||
return 0, _blobErr
|
||||
}
|
||||
{{- end }}
|
||||
affected, err := sqlgraph.DeleteNodes(ctx, {{ $receiver}}.driver, _spec)
|
||||
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
{{- if $.HasBlobFields }}
|
||||
if err == nil {
|
||||
// Resolve which blobs are unreferenced now that the rows are gone, while the
|
||||
// transaction is still open, and remove them from storage once it commits.
|
||||
_blobCleanup, _blobErr := _blobKeys.Cleanup(ctx)
|
||||
if _blobErr != nil {
|
||||
return 0, _blobErr
|
||||
}
|
||||
if txd, ok := {{ $receiver }}.driver.(*txDriver); ok {
|
||||
txd.mu.Lock()
|
||||
txd.onCommit = append(txd.onCommit, func(next Committer) Committer {
|
||||
return CommitFunc(func(ctx context.Context, tx *Tx) error {
|
||||
if err := next.Commit(ctx, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return _blobCleanup(ctx)
|
||||
})
|
||||
})
|
||||
txd.mu.Unlock()
|
||||
} else {
|
||||
err = _blobCleanup(ctx)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{ $mutation }}.done = true
|
||||
return affected, err
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
25
entc/gen/template/dialect/sql/ent.tmpl
Normal file
25
entc/gen/template/dialect/sql/ent.tmpl
Normal file
@@ -0,0 +1,25 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{/* Additional fields for the generated model for holding the foreign-keys */}}
|
||||
{{ define "dialect/sql/model/fields" }}
|
||||
{{- range $fk := $.UnexportedForeignKeys }}
|
||||
{{- $f := $fk.Field }}
|
||||
{{ $fk.StructField }} {{ if $f.Nillable }}*{{ end }}{{ $f.Type }}
|
||||
{{- end }}
|
||||
{{- range $bk := $.BlobKeys }}
|
||||
{{ $bk.StructField }} *string
|
||||
{{- end }}
|
||||
selectValues sql.SelectValues
|
||||
{{- /* Allow adding struct fields by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/model/fields/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
177
entc/gen/template/dialect/sql/entql.tmpl
Normal file
177
entc/gen/template/dialect/sql/entql.tmpl
Normal file
@@ -0,0 +1,177 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{ define "dialect/sql/entql" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ template "header" $ }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"{{ $.Config.Package }}/predicate"
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- end }}
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/entql"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
// schemaGraph holds a representation of ent/schema at runtime.
|
||||
var schemaGraph = func() *sqlgraph.Schema {
|
||||
graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, {{ len $.Nodes }})}
|
||||
{{- range $i, $n := $.Nodes }}
|
||||
graph.Nodes[{{ $i }}] = &sqlgraph.Node{
|
||||
NodeSpec: sqlgraph.NodeSpec{
|
||||
Table: {{ $n.Package }}.Table,
|
||||
Columns: {{ $n.Package }}.Columns,
|
||||
{{- if $n.HasOneFieldID }}
|
||||
ID: &sqlgraph.FieldSpec{
|
||||
Type: field.{{ $n.ID.Type.ConstName }},
|
||||
Column: {{ $n.Package }}.{{ $n.ID.Constant }},
|
||||
},
|
||||
{{- else }}
|
||||
CompositeID: []*sqlgraph.FieldSpec{
|
||||
{{- range $id := $n.EdgeSchema.ID }}
|
||||
{
|
||||
Type: field.{{ $id.Type.ConstName }},
|
||||
Column: {{ $n.Package }}.{{ $id.Constant }},
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
Type: "{{ $n.Name }}",
|
||||
Fields: map[string]*sqlgraph.FieldSpec{
|
||||
{{- range $f := $n.Fields }}
|
||||
{{- if not (or $f.IsBlobNoColumn $f.IsBlobLazy) }}
|
||||
{{ $n.Package }}.{{ $f.Constant }}: {Type: field.{{ $f.Type.ConstName }}, Column: {{ $n.Package }}.{{ $f.Constant }}},
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $n := $.Nodes }}
|
||||
{{- range $e := $n.Edges }}
|
||||
graph.MustAddE(
|
||||
"{{ $e.Name }}",
|
||||
&sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.{{ $e.Rel.Type }},
|
||||
Inverse: {{ $e.IsInverse }},
|
||||
Table: {{ $n.Package }}.{{ $e.TableConstant }},
|
||||
Columns: {{ if $e.M2M }}{{ $n.Package }}.{{ $e.PKConstant }}{{ else }}[]string{ {{ $n.Package }}.{{ $e.ColumnConstant }} }{{ end }},
|
||||
Bidi: {{ $e.Bidi }},
|
||||
},
|
||||
"{{ $n.Name }}",
|
||||
"{{ $e.Type.Name }}",
|
||||
)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return graph
|
||||
}()
|
||||
|
||||
// predicateAdder wraps the addPredicate method.
|
||||
// All update, update-one and query builders implement this interface.
|
||||
type predicateAdder interface {
|
||||
addPredicate(func(s *sql.Selector))
|
||||
}
|
||||
|
||||
{{ range $i, $n := $.Nodes }}
|
||||
{{ $builder := $n.QueryName }}
|
||||
{{ $receiver := $n.QueryReceiver }}
|
||||
{{ $mutation := $n.MutationName }}
|
||||
{{ $filter := print $n.FilterName }}
|
||||
|
||||
// addPredicate implements the predicateAdder interface.
|
||||
func ({{ $receiver }} *{{ $builder }}) addPredicate(pred func(s *sql.Selector)) {
|
||||
{{ $receiver }}.predicates = append({{ $receiver }}.predicates, pred)
|
||||
}
|
||||
|
||||
// Filter returns a Filter implementation to apply filters on the {{ $builder }} builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) Filter() *{{ $filter }} {
|
||||
return &{{ $filter }}{config: {{ $receiver }}.config, predicateAdder: {{ $receiver}} }
|
||||
}
|
||||
|
||||
{{- if not $n.IsView }}
|
||||
// addPredicate implements the predicateAdder interface.
|
||||
func (m *{{ $mutation }}) addPredicate(pred func(s *sql.Selector)) {
|
||||
m.Where(pred)
|
||||
}
|
||||
|
||||
// Filter returns an entql.Where implementation to apply filters on the {{ $mutation }} builder.
|
||||
func (m *{{ $mutation }}) Filter() *{{ $filter }} {
|
||||
return &{{ $filter }}{config: m.config, predicateAdder: m}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// {{ $filter }} provides a generic filtering capability at runtime for {{ $builder }}.
|
||||
type {{ $filter }} struct {
|
||||
predicateAdder
|
||||
config
|
||||
}
|
||||
|
||||
// Where applies the entql predicate on the query filter.
|
||||
func (f *{{ $filter }}) Where(p entql.P) {
|
||||
f.addPredicate(func(s *sql.Selector) {
|
||||
if err := schemaGraph.EvalP(schemaGraph.Nodes[{{ $i }}].Type, p, s); err != nil {
|
||||
s.AddError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
{{- if $n.HasOneFieldID }}
|
||||
{{ $type := $n.ID.Type.Type.String }}
|
||||
{{ $iface := print (pascal $type) "P" }}
|
||||
{{- if $n.ID.IsTime }}{{ $iface = "TimeP" }}
|
||||
{{- else if or $n.ID.IsBytes $n.ID.IsJSON }}{{ $iface = "BytesP" }}
|
||||
{{- else if $n.ID.IsUUID }}{{ $iface = "ValueP" }}
|
||||
{{- end }}
|
||||
// WhereID applies the entql {{ $type }} predicate on the id field.
|
||||
func (f *{{ $filter }}) WhereID(p entql.{{ $iface }}) {
|
||||
f.Where(p.Field({{ $n.Package }}.{{ $n.ID.Constant }}))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ range $f := $n.Fields }}
|
||||
{{- if or $f.IsBlobNoColumn $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $type := $f.Type.Type.String }}
|
||||
{{ $iface := print (pascal $type) "P" }}
|
||||
{{- if $f.IsTime }}{{ $iface = "TimeP" }}
|
||||
{{- else if or $f.IsBytes $f.IsJSON }}{{ $iface = "BytesP" }}
|
||||
{{- else if $f.IsUUID }}{{ $iface = "ValueP" }}
|
||||
{{- end }}
|
||||
// Where{{ $f.StructField }} applies the entql {{ $type }} predicate on the {{ $f.Name }} field.
|
||||
func (f *{{ $filter }}) Where{{ $f.StructField }}(p entql.{{ $iface }}) {
|
||||
f.Where(p.Field({{ $n.Package }}.{{ $f.Constant }}))
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range $e := $n.Edges }}
|
||||
{{ $func := print "WhereHas" $e.StructField }}
|
||||
// {{ $func }} applies a predicate to check if query has an edge {{ $e.Name }}.
|
||||
func (f *{{ $filter }}) {{ $func }}() {
|
||||
f.Where(entql.HasEdge("{{ $e.Name }}"))
|
||||
}
|
||||
|
||||
{{ $func = print "WhereHas" $e.StructField "With" }}
|
||||
// {{ $func }} applies a predicate to check if query has an edge {{ $e.Name }} with a given conditions (other predicates).
|
||||
func (f *{{ $filter }}) {{ $func }}(preds ...predicate.{{ $e.Type.Name }}) {
|
||||
f.Where(entql.HasEdgeWith("{{ $e.Name }}", sqlgraph.WrapFunc(func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})))
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
11
entc/gen/template/dialect/sql/errors.tmpl
Normal file
11
entc/gen/template/dialect/sql/errors.tmpl
Normal file
@@ -0,0 +1,11 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{/* custom errors and errors handlers for sql dialects */}}
|
||||
{{ define "dialect/sql/errors" }}
|
||||
{{ end }}
|
||||
71
entc/gen/template/dialect/sql/feature/execquery.tmpl
Normal file
71
entc/gen/template/dialect/sql/feature/execquery.tmpl
Normal file
@@ -0,0 +1,71 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph*/}}
|
||||
|
||||
{{- define "import/additional/stdsql" -}}
|
||||
{{- if $.FeatureEnabled "sql/execquery" }}
|
||||
stdsql "database/sql"
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Template for adding "ExecContext"/"QueryContext" methods to the config. */}}
|
||||
{{ define "config/additional/sql/execquery" }}
|
||||
{{- if $.FeatureEnabled "sql/execquery" }}
|
||||
// ExecContext allows calling the underlying ExecContext method of the driver if it is supported by it.
|
||||
// See, database/sql#DB.ExecContext for more information.
|
||||
func (c *config) ExecContext(ctx context.Context, query string, args ...any) (stdsql.Result, error) {
|
||||
ex, ok := c.driver.(interface {
|
||||
ExecContext(context.Context, string, ...any) (stdsql.Result, error)
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Driver.ExecContext is not supported")
|
||||
}
|
||||
return ex.ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// QueryContext allows calling the underlying QueryContext method of the driver if it is supported by it.
|
||||
// See, database/sql#DB.QueryContext for more information.
|
||||
func (c *config) QueryContext(ctx context.Context, query string, args ...any) (*stdsql.Rows, error) {
|
||||
q, ok := c.driver.(interface {
|
||||
QueryContext(context.Context, string, ...any) (*stdsql.Rows, error)
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Driver.QueryContext is not supported")
|
||||
}
|
||||
return q.QueryContext(ctx, query, args...)
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Template for adding "ExecContext"/"QueryContext" methods to the client. */}}
|
||||
{{ define "tx/additional/sql/execquery" }}
|
||||
{{- if $.FeatureEnabled "sql/execquery" }}
|
||||
// ExecContext allows calling the underlying ExecContext method of the transaction if it is supported by it.
|
||||
// See, database/sql#Tx.ExecContext for more information.
|
||||
func (tx *txDriver) ExecContext(ctx context.Context, query string, args ...any) (stdsql.Result, error) {
|
||||
ex, ok := tx.tx.(interface {
|
||||
ExecContext(context.Context, string, ...any) (stdsql.Result, error)
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Tx.ExecContext is not supported")
|
||||
}
|
||||
return ex.ExecContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
// QueryContext allows calling the underlying QueryContext method of the transaction if it is supported by it.
|
||||
// See, database/sql#Tx.QueryContext for more information.
|
||||
func (tx *txDriver) QueryContext(ctx context.Context, query string, args ...any) (*stdsql.Rows, error) {
|
||||
q, ok := tx.tx.(interface {
|
||||
QueryContext(context.Context, string, ...any) (*stdsql.Rows, error)
|
||||
})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Tx.QueryContext is not supported")
|
||||
}
|
||||
return q.QueryContext(ctx, query, args...)
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
46
entc/gen/template/dialect/sql/feature/lock.tmpl
Normal file
46
entc/gen/template/dialect/sql/feature/lock.tmpl
Normal file
@@ -0,0 +1,46 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{/* Templates used by the "sql/lock" feature-flag to add "SELECT ... FOR UPDATE/SHARE" capabilities. */}}
|
||||
|
||||
{{ define "dialect/sql/query/additional/locking" }}
|
||||
{{ if $.FeatureEnabled "sql/lock" }}
|
||||
{{ template "helper/sqlock" $ }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "helper/sqlock" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
|
||||
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
|
||||
// either committed or rolled-back.
|
||||
func ({{ $receiver }} *{{ $builder }}) ForUpdate(opts ...sql.LockOption) *{{ $builder }} {
|
||||
if {{ $receiver }}.driver.Dialect() == dialect.Postgres {
|
||||
{{ $receiver }}.Unique(false)
|
||||
}
|
||||
{{ $receiver }}.modifiers = append({{ $receiver }}.modifiers, func(s *sql.Selector) {
|
||||
s.ForUpdate(opts...)
|
||||
})
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
|
||||
// on any rows that are read. Other sessions can read the rows, but cannot modify them
|
||||
// until your transaction commits.
|
||||
func ({{ $receiver }} *{{ $builder }}) ForShare(opts ...sql.LockOption) *{{ $builder }} {
|
||||
if {{ $receiver }}.driver.Dialect() == dialect.Postgres {
|
||||
{{ $receiver }}.Unique(false)
|
||||
}
|
||||
{{ $receiver }}.modifiers = append({{ $receiver }}.modifiers, func(s *sql.Selector) {
|
||||
s.ForShare(opts...)
|
||||
})
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
40
entc/gen/template/dialect/sql/feature/migratediff.tmpl
Normal file
40
entc/gen/template/dialect/sql/feature/migratediff.tmpl
Normal file
@@ -0,0 +1,40 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{ define "migrate/diff" }}
|
||||
// Diff compares the state read from a database connection or migration directory with
|
||||
// the state defined by the Ent schema. Changes will be written to new migration files.
|
||||
func Diff(ctx context.Context, url string, opts ...schema.MigrateOption) error {
|
||||
return NamedDiff(ctx, url, "changes", opts...)
|
||||
}
|
||||
|
||||
// NamedDiff compares the state read from a database connection or migration directory with
|
||||
// the state defined by the Ent schema. Changes will be written to new named migration files.
|
||||
func NamedDiff(ctx context.Context, url, name string, opts ...schema.MigrateOption) error {
|
||||
return schema.Diff(ctx, url, name, Tables, opts...)
|
||||
}
|
||||
// Diff creates a migration file containing the statements to resolve the diff
|
||||
// between the Ent schema and the connected database.
|
||||
func (s *Schema) Diff(ctx context.Context, opts ...schema.MigrateOption) error {
|
||||
migrate, err := schema.NewMigrate(s.drv, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ent/migrate: %w", err)
|
||||
}
|
||||
return migrate.Diff(ctx, Tables...)
|
||||
}
|
||||
|
||||
// NamedDiff creates a named migration file containing the statements to resolve the diff
|
||||
// between the Ent schema and the connected database.
|
||||
func (s *Schema) NamedDiff(ctx context.Context, name string, opts ...schema.MigrateOption) error {
|
||||
migrate, err := schema.NewMigrate(s.drv, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ent/migrate: %w", err)
|
||||
}
|
||||
return migrate.NamedDiff(ctx, name, Tables...)
|
||||
}
|
||||
{{ end }}
|
||||
92
entc/gen/template/dialect/sql/feature/modifier.tmpl
Normal file
92
entc/gen/template/dialect/sql/feature/modifier.tmpl
Normal file
@@ -0,0 +1,92 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{/* Templates used by the "sql/modifier" feature-flag to add custom modifiers to the builders. */}}
|
||||
|
||||
{{/* Template for adding the "modifiers" field to the query builder. */}}
|
||||
{{ define "dialect/sql/query/fields/additional/modify" -}}
|
||||
{{- if or ($.FeatureEnabled "sql/lock") ($.FeatureEnabled "sql/modifier") }}
|
||||
modifiers []func(*sql.Selector)
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Template for adding the "executing" the list of modifiers on the sql.Selector. */}}
|
||||
{{ define "dialect/sql/query/selector/modify" }}
|
||||
{{- if or ($.FeatureEnabled "sql/lock") ($.FeatureEnabled "sql/modifier") }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
for _, m := range {{ $receiver }}.modifiers {
|
||||
m(selector)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Template for passing the modifiers to the sqlgraph.QuerySpec. */}}
|
||||
{{ define "dialect/sql/query/spec/modify" }}
|
||||
{{- if or ($.FeatureEnabled "sql/lock") ($.FeatureEnabled "sql/modifier") }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
if len({{ $receiver }}.modifiers) > 0 {
|
||||
_spec.Modifiers = {{ $receiver }}.modifiers
|
||||
}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* A template for adding the Modify method to the query-builder. */}}
|
||||
{{ define "dialect/sql/query/additional/modify" }}
|
||||
{{ if $.FeatureEnabled "sql/modifier" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $selectBuilder := pascal $.Name | printf "%sSelect" }}
|
||||
// Modify adds a query modifier for attaching custom logic to queries.
|
||||
func ({{ $receiver }} *{{ $builder }}) Modify(modifiers ...func(s *sql.Selector)) *{{ $selectBuilder }} {
|
||||
{{ $receiver }}.modifiers = append({{ $receiver }}.modifiers, modifiers...)
|
||||
return {{ $receiver }}.Select()
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* A template for adding the Modify method to the select-builder. */}}
|
||||
{{ define "dialect/sql/select/additional/modify" }}
|
||||
{{ if $.FeatureEnabled "sql/modifier" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
// Modify adds a query modifier for attaching custom logic to queries.
|
||||
func ({{ $receiver }} *{{ $builder }}) Modify(modifiers ...func(s *sql.Selector)) *{{ $builder }} {
|
||||
{{ $receiver }}.modifiers = append({{ $receiver }}.modifiers, modifiers...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Template for adding the "modifiers" field to the update builder. */}}
|
||||
{{ define "dialect/sql/update/fields/additional/modify" -}}
|
||||
{{- if $.FeatureEnabled "sql/modifier" }}
|
||||
modifiers []func(*sql.UpdateBuilder)
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* A template for adding the Modify method to the update and updateone builders. */}}
|
||||
{{ define "dialect/sql/update/additional/modify" }}
|
||||
{{ if $.FeatureEnabled "sql/modifier" }}
|
||||
{{ $pkg := $.Scope.Package }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
// Modify adds a statement modifier for attaching custom logic to the UPDATE statement.
|
||||
func ({{ $receiver }} *{{ $builder }}) Modify(modifiers ...func(u *sql.UpdateBuilder)) *{{ $builder }} {
|
||||
{{ $receiver }}.modifiers = append({{ $receiver }}.modifiers, modifiers...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Template for passing the modifiers to the sqlgraph.UpdateSpec. */}}
|
||||
{{ define "dialect/sql/update/spec/modify" }}
|
||||
{{- if $.FeatureEnabled "sql/modifier" }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
_spec.AddModifiers({{ $receiver }}.modifiers...)
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
121
entc/gen/template/dialect/sql/feature/namedges.tmpl
Normal file
121
entc/gen/template/dialect/sql/feature/namedges.tmpl
Normal file
@@ -0,0 +1,121 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{/* Templates used by the "namedges" feature-flag to allow eager-loading edges with dynamic names. */}}
|
||||
|
||||
{{ define "dialect/sql/model/edges/fields/additional/namedges" }}
|
||||
{{- if $.FeatureEnabled "namedges" }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if not $e.Unique }}
|
||||
named{{ $e.StructField }} map[string][]*{{ $e.Type.Name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/sql/model/additional/namedges" }}
|
||||
{{- if $.FeatureEnabled "namedges" }}
|
||||
{{ $receiver := $.Receiver }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if not $e.Unique }}
|
||||
{{ $func := print "Named" $e.StructField }}
|
||||
// {{ $func }} returns the {{ $e.StructField }} named value or an error if the edge was not
|
||||
// loaded in eager-loading with this name.
|
||||
func ({{ $receiver }} *{{ $.Name }}) Named{{ $e.StructField }}(name string) ([]*{{ $e.Type.Name }}, error) {
|
||||
if {{ $receiver }}.Edges.named{{ $e.StructField }} == nil {
|
||||
return nil, &NotLoadedError{edge: name}
|
||||
}
|
||||
nodes, ok := {{ $receiver }}.Edges.named{{ $e.StructField }}[name]
|
||||
if !ok {
|
||||
return nil, &NotLoadedError{edge: name}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $.Name }}) appendNamed{{ $e.StructField }}(name string, edges ...*{{ $e.Type.Name }}) {
|
||||
if {{ $receiver }}.Edges.named{{ $e.StructField }} == nil {
|
||||
{{ $receiver }}.Edges.named{{ $e.StructField }} = make(map[string][]*{{ $e.Type.Name }})
|
||||
}
|
||||
if len(edges) == 0 {
|
||||
{{- /* Prefer empty array over nil to stay consistent with the standard eager-loading API. */}}
|
||||
{{ $receiver }}.Edges.named{{ $e.StructField }}[name] = []*{{ $e.Type.Name }}{}
|
||||
} else {
|
||||
{{ $receiver }}.Edges.named{{ $e.StructField }}[name] = append({{ $receiver }}.Edges.named{{ $e.StructField }}[name], edges...)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{- define "dialect/sql/query/fields/additional/namedges" }}
|
||||
{{- if $.FeatureEnabled "namedges" }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if not $e.Unique }}
|
||||
{{ $e.EagerLoadNamedField }} map[string]*{{ $e.Type.QueryName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/query/additional/namedges" }}
|
||||
{{- if $.FeatureEnabled "namedges" }}
|
||||
{{ $builder := $.QueryName }}
|
||||
{{ $receiver := $.QueryReceiver }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if not $e.Unique }}
|
||||
{{ $ebuilder := $e.Type.QueryName }}
|
||||
{{ $func := print "WithNamed" $e.StructField }}
|
||||
// {{ $func }} tells the query-builder to eager-load the nodes that are connected to the "{{ $e.Name }}"
|
||||
// edge with the given name. The optional arguments are used to configure the query builder of the edge.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(name string, opts ...func(*{{ $ebuilder }})) *{{ $builder }} {
|
||||
query := (&{{ $e.Type.ClientName }}{config: {{ $receiver }}.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
if {{ $receiver }}.{{ $e.EagerLoadNamedField }} == nil {
|
||||
{{ $receiver }}.{{ $e.EagerLoadNamedField }} = make(map[string]*{{ $e.Type.QueryName }})
|
||||
}
|
||||
{{ $receiver }}.{{ $e.EagerLoadNamedField }}[name] = query
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Process nodes before they are returned and resolve named-edges. */}}
|
||||
{{- define "dialect/sql/query/all/nodes/namedges" }}
|
||||
{{- if $.FeatureEnabled "namedges" }}
|
||||
{{- $builder := pascal $.Scope.Builder }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if not $e.Unique }}
|
||||
for name, query := range {{ $receiver }}.{{ $e.EagerLoadNamedField }} {
|
||||
if err := {{ $receiver }}.load{{ $e.StructField }}(ctx, query, nodes,
|
||||
func(n *{{ $.Name }}) { n.appendNamed{{ $e.StructField }}(name) },
|
||||
{{- if and ($.FeatureEnabled "bidiedges") $e.Ref $e.Ref.Unique }}
|
||||
func(n *{{ $.Name }}, e *{{ $e.Type.Name }}){
|
||||
n.appendNamed{{ $e.StructField }}(name, e)
|
||||
{{- $idx := $e.Ref.Index }}
|
||||
{{- /* Set only in case this type was not loaded explicitly (without custom options). */}}
|
||||
if !e.Edges.loadedTypes[{{ $idx }}] {
|
||||
e.Edges.{{ $e.Ref.StructField }} = n
|
||||
}
|
||||
}); err != nil {
|
||||
{{- else }}
|
||||
{{- /* Keep it one-liner if there is not inverse-condition. */}}
|
||||
func(n *{{ $.Name }}, e *{{ $e.Type.Name }}){ n.appendNamed{{ $e.StructField }}(name, e) }); err != nil {
|
||||
{{- end }}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
220
entc/gen/template/dialect/sql/feature/schemaconfig.tmpl
Normal file
220
entc/gen/template/dialect/sql/feature/schemaconfig.tmpl
Normal file
@@ -0,0 +1,220 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{- define "dialect/sql/internal/schemaconfig" -}}
|
||||
{{ with extend $ "Package" "internal" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
import "context"
|
||||
|
||||
// SchemaConfig represents alternative schema names for all tables
|
||||
// that can be passed at runtime.
|
||||
type SchemaConfig struct {
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.Name }} string // {{ $n.Name }} table.
|
||||
{{- range $e := $n.Edges }}
|
||||
{{- /* Skip adding join-table in case the edge is inverse or already defined as an edge-schema. */}}
|
||||
{{- if and $e.M2M (not $e.Inverse) }}
|
||||
{{ $n.Name }}{{ $e.StructField }} string // {{ $n.Name }}-{{ $e.Name }}->{{ $e.Type.Name }} table.
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
type schemaCtxKey struct{}
|
||||
|
||||
// SchemaConfigFromContext returns a SchemaConfig stored inside a context, or empty if there isn't one.
|
||||
func SchemaConfigFromContext(ctx context.Context) SchemaConfig {
|
||||
config, _ := ctx.Value(schemaCtxKey{}).(SchemaConfig)
|
||||
return config
|
||||
}
|
||||
|
||||
// NewSchemaConfigContext returns a new context with the given SchemaConfig attached.
|
||||
func NewSchemaConfigContext(parent context.Context, config SchemaConfig) context.Context {
|
||||
return context.WithValue(parent, schemaCtxKey{}, config)
|
||||
}
|
||||
|
||||
{{- end }}
|
||||
|
||||
{{/* Additional imports by the schemaconfig feature. */}}
|
||||
{{- define "dialect/sql/import/additional/schemaconfig" -}}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
"{{ $.Config.Package }}/internal"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Additional fields to the config struct. */}}
|
||||
{{- define "dialect/sql/config/fields/schemaconfig" -}}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" -}}
|
||||
// schemaConfig contains alternative names for all tables.
|
||||
schemaConfig SchemaConfig
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Additional top-level code for the generated config.go file. */}}
|
||||
{{- define "dialect/sql/config/options/schemaconfig" }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
// SchemaConfig represents alternative schema names for all tables
|
||||
// that can be passed at runtime.
|
||||
type SchemaConfig = internal.SchemaConfig
|
||||
|
||||
// AlternateSchemas allows alternate schema names to be
|
||||
// passed into ent operations.
|
||||
func AlternateSchema(schemaConfig SchemaConfig) Option {
|
||||
return func(c *config) {
|
||||
c.schemaConfig = schemaConfig
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/delete/spec/ctxschemaconfig" }}
|
||||
{{- template "dialect/sql/spec/ctxschemaconfig" $ }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/update/spec/ctxschemaconfig" }}
|
||||
{{- template "dialect/sql/spec/ctxschemaconfig" $ }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/create/spec/ctxschemaconfig" }}
|
||||
{{- with extend $ "Ident" "_spec" "SkipContext" true }}
|
||||
{{- template "dialect/sql/spec/ctxschemaconfig" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/query/spec/ctxschemaconfig" }}
|
||||
{{- template "dialect/sql/spec/ctxschemaconfig" . }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/query/eagerloading/join/schemaconfig" }}
|
||||
{{- with extend $ "Ident" "joinT" "CallSet" true }}
|
||||
{{- template "dialect/sql/defedge/spec/schemaconfig" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/defedge/spec/schemaconfig" }}
|
||||
{{- $e := $.Scope.Edge }}
|
||||
{{- $builder := pascal $.Scope.Builder }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- $ident := "edge" }}{{ with $.Scope.Ident }}{{ $ident = . }}{{ end }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
{{- $schema := $e.Type.Name }}
|
||||
{{- if $e.OwnFK }}
|
||||
{{- $schema = $.Name }}
|
||||
{{- else if $e.M2M }}
|
||||
{{- if $e.Through }}
|
||||
{{- $schema = $e.Through.Name }}
|
||||
{{- else if $e.Inverse }}
|
||||
{{- $schema = print $e.Type.Name (pascal $e.Inverse) }}
|
||||
{{- else }}
|
||||
{{- $schema = print $.Name $e.StructField }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.Scope.CallSet }}
|
||||
{{ $ident }}.Schema({{ $receiver }}.schemaConfig.{{ $schema }})
|
||||
{{- else }}
|
||||
{{ $ident }}.Schema = {{ $receiver }}.schemaConfig.{{ $schema }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for injecting the SchemaConfig to the context. Should be executed before other templates. */}}
|
||||
{{- define "dialect/sql/spec/ctxschemaconfig" -}}
|
||||
{{- $builder := pascal $.Scope.Builder }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- $ident := "_spec.Node" }}{{ with $.Scope.Ident }}{{ $ident = . }}{{ end }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
{{ $ident }}.Schema = {{ $receiver }}.schemaConfig.{{ $.Name }}
|
||||
{{- if not $.Scope.SkipContext }}
|
||||
ctx = internal.NewSchemaConfigContext(ctx, {{ $receiver }}.schemaConfig)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/query/selector/ctxschemaconfig" -}}
|
||||
{{- $builder := pascal $.Scope.Builder }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
t1.Schema({{ $receiver }}.schemaConfig.{{ $.Name }})
|
||||
ctx = internal.NewSchemaConfigContext(ctx, {{ $receiver }}.schemaConfig)
|
||||
selector.WithContext(ctx)
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/query/path/ctxschemaconfig" }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
schemaConfig := {{ $.Scope.Receiver }}.schemaConfig
|
||||
{{- template "dialect/sql/query/step/ctxschemaconfig" . }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/query/from/ctxschemaconfig" }}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
schemaConfig := {{ $.Scope.Receiver }}.schemaConfig
|
||||
{{- template "dialect/sql/query/step/ctxschemaconfig" . }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/predicate/edge/has/ctxschemaconfig" -}}
|
||||
{{- template "dialect/sql/predicate/edge/ctxschemaconfig" . }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/predicate/edge/haswith/ctxschemaconfig" -}}
|
||||
{{- template "dialect/sql/predicate/edge/ctxschemaconfig" . }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/predicate/edge/ctxschemaconfig" -}}
|
||||
{{- if $.FeatureEnabled "sql/schemaconfig" }}
|
||||
schemaConfig := internal.SchemaConfigFromContext(s.Context())
|
||||
{{- template "dialect/sql/query/step/ctxschemaconfig" . }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "dialect/sql/query/step/ctxschemaconfig" -}}
|
||||
{{- $e := $.Scope.Edge }}
|
||||
step.To.Schema = schemaConfig.{{ $e.Type.Name }}
|
||||
{{- $schema := $e.Type.Name }}
|
||||
{{- if $e.OwnFK }}
|
||||
{{- $schema = $.Name }}
|
||||
{{- else if $e.M2M }}
|
||||
{{- if $e.Through }}
|
||||
{{- $schema = $e.Through.Name }}
|
||||
{{- else if $e.Inverse }}
|
||||
{{- $schema = print $e.Type.Name (pascal $e.Inverse) }}
|
||||
{{- else }}
|
||||
{{- $schema = print $.Name $e.StructField }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
step.Edge.Schema = schemaConfig.{{ $schema }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "config/init/fields/multischema" }}
|
||||
{{- if $.FeatureEnabled "sql/multischema" }}
|
||||
cfg.schemaConfig = DefaultSchemaConfig
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "dialect/sql/config/options/multischema" }}
|
||||
{{- if $.FeatureEnabled "sql/multischema" }}
|
||||
{{- $all := $.TableSchemas }}
|
||||
var (
|
||||
// DefaultSchemaConfig represents the default schema names for all tables as defined in ent/schema.
|
||||
DefaultSchemaConfig = SchemaConfig {
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.Name }}: tableSchemas[{{ indexOf $all $n.TableSchema }}],
|
||||
{{- range $e := $n.Edges }}
|
||||
{{- if and $e.M2M (not $e.Inverse) (not $e.Through) }}
|
||||
{{ $n.Name }}{{ $e.StructField }}: tableSchemas[{{ indexOf $all $e.TableSchema }}],
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
tableSchemas = [...]string{ {{- range $i, $s := $all }}{{ if $i }},{{ end }}"{{ $s }}"{{ end }} }
|
||||
)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
493
entc/gen/template/dialect/sql/feature/upsert.tmpl
Normal file
493
entc/gen/template/dialect/sql/feature/upsert.tmpl
Normal file
@@ -0,0 +1,493 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{/* Templates used by the "sql/upsert" feature-flag to allows configuring the
|
||||
`ON CONFLICT` / `ON DUPLICATE KEY` clause for `INSERT` statements */}}
|
||||
|
||||
{{/* Template for adding the "conflict" field to the create builder. */}}
|
||||
{{ define "dialect/sql/create/fields/additional/upsert" -}}
|
||||
{{- if $.FeatureEnabled "sql/upsert" }}
|
||||
conflict []sql.ConflictOption
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Template for adding the "conflict" field to the create-bulk builder. */}}
|
||||
{{ define "dialect/sql/create_bulk/fields/additional/upsert" -}}
|
||||
{{- if $.FeatureEnabled "sql/upsert" }}
|
||||
conflict []sql.ConflictOption
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Template for passing the "OnConflict" options to the sqlgraph.CreateSpec. */}}
|
||||
{{- define "dialect/sql/create/spec/upsert" }}
|
||||
{{- if $.FeatureEnabled "sql/upsert" }}
|
||||
_spec.OnConflict = {{ $.Scope.Receiver }}.conflict
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Template for passing the "OnConflict" options to the sqlgraph.BatchCreateSpec. */}}
|
||||
{{- define "dialect/sql/create_bulk/spec/upsert" }}
|
||||
{{- if $.FeatureEnabled "sql/upsert" }}
|
||||
spec.OnConflict = {{ $.Scope.Receiver }}.conflict
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Template for adding the "OnConflict" methods to the create builder. */}}
|
||||
{{ define "dialect/sql/create/additional/upsert" }}
|
||||
{{ if $.FeatureEnabled "sql/upsert" }}
|
||||
{{ template "helper/upsertone" $ }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "helper/upsertone" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $upsertOne := print $.Name "UpsertOne" }}
|
||||
{{ $upsertSet := print $.Name "Upsert" }}
|
||||
|
||||
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
|
||||
// of the `INSERT` statement. For example:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
{{- with $.Fields }}
|
||||
{{- $f := index $.Fields 0 }}
|
||||
// Set{{ index $f.StructField }}(v).
|
||||
{{- end }}
|
||||
// OnConflict(
|
||||
// // Update the row with the new values
|
||||
// // the was proposed for insertion.
|
||||
// sql.ResolveWithNewValues(),
|
||||
// ).
|
||||
{{- with $.Fields }}
|
||||
{{- $f := index $.Fields 0 }}
|
||||
// // Override some of the fields with custom
|
||||
// // update values.
|
||||
// Update(func(u *ent.{{ $.Name }}Upsert) {
|
||||
// Set{{ index $f.StructField }}(v+v).
|
||||
// }).
|
||||
{{- end }}
|
||||
// Exec(ctx)
|
||||
//
|
||||
func ({{ $receiver }} *{{ $builder }}) OnConflict(opts ...sql.ConflictOption) *{{ $upsertOne }} {
|
||||
{{ $receiver }}.conflict = opts
|
||||
return &{{ $upsertOne }}{
|
||||
create: {{ $receiver }},
|
||||
}
|
||||
}
|
||||
|
||||
// OnConflictColumns calls `OnConflict` and configures the columns
|
||||
// as conflict target. Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(sql.ConflictColumns(columns...)).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func ({{ $receiver }} *{{ $builder }}) OnConflictColumns(columns ...string) *{{ $upsertOne }} {
|
||||
{{ $receiver }}.conflict = append({{ $receiver }}.conflict, sql.ConflictColumns(columns...))
|
||||
return &{{ $upsertOne }}{
|
||||
create: {{ $receiver }},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
// {{ $upsertOne }} is the builder for "upsert"-ing
|
||||
// one {{ $.Name }} node.
|
||||
{{ $upsertOne }} struct {
|
||||
create *{{ $builder }}
|
||||
}
|
||||
|
||||
// {{ $upsertSet }} is the "OnConflict" setter.
|
||||
{{ $upsertSet }} struct {
|
||||
*sql.UpdateSet
|
||||
}
|
||||
)
|
||||
|
||||
{{ range $f := $.UpsertFields }}
|
||||
{{- if $f.IsBlob }}
|
||||
{{ $func := print "Update" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field to the value that was provided on create.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}() *{{ $upsertSet }} {
|
||||
{{- if not $f.IsBlobNoColumn }}
|
||||
u.SetExcluded({{ $.Package }}.{{ $f.Constant }})
|
||||
{{- end }}
|
||||
u.SetExcluded("{{ $f.BlobKeyColumn }}")
|
||||
return u
|
||||
}
|
||||
|
||||
{{ if $f.Optional }}
|
||||
{{ $func := print "Clear" $f.StructField }}
|
||||
// {{ $func }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}() *{{ $upsertSet }} {
|
||||
{{- if not $f.IsBlobNoColumn }}
|
||||
u.SetNull({{ $.Package }}.{{ $f.Constant }})
|
||||
{{- end }}
|
||||
u.SetNull("{{ $f.BlobKeyColumn }}")
|
||||
return u
|
||||
}
|
||||
{{ end }}
|
||||
{{- continue }}{{ end }}
|
||||
{{ $func := print "Set" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}(v {{ $f.Type }}) *{{ $upsertSet }} {
|
||||
u.Set({{ $.Package }}.{{ $f.Constant }}, v)
|
||||
return u
|
||||
}
|
||||
|
||||
{{ $func = print "Update" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field to the value that was provided on create.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}() *{{ $upsertSet }} {
|
||||
u.SetExcluded({{ $.Package }}.{{ $f.Constant }})
|
||||
return u
|
||||
}
|
||||
|
||||
{{ if $f.SupportsMutationAdd }}
|
||||
{{ $func := print "Add" $f.StructField }}
|
||||
// {{ $func }} adds v to the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}(v {{ $f.Type }}) *{{ $upsertSet }} {
|
||||
u.Add({{ $.Package }}.{{ $f.Constant }}, v)
|
||||
return u
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $f.Optional }}
|
||||
{{ $func := print "Clear" $f.StructField }}
|
||||
// {{ $func }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsertSet }}) {{ $func }}() *{{ $upsertSet }} {
|
||||
u.SetNull({{ $.Package }}.{{ $f.Constant }})
|
||||
return u
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $udfID := false }}{{ if $.HasOneFieldID }}{{ $udfID = $.ID.UserDefined }}{{ end }}
|
||||
|
||||
// UpdateNewValues updates the mutable fields using the new values that were set on create{{ if $udfID }} except the ID field{{ end }}.
|
||||
// Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(
|
||||
// sql.ResolveWithNewValues(),
|
||||
{{- if $udfID }}
|
||||
// sql.ResolveWith(func(u *sql.UpdateSet) {
|
||||
// u.SetIgnore({{ $.Package }}.{{ $.ID.Constant }})
|
||||
// }),
|
||||
{{- end }}
|
||||
// ).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func (u *{{ $upsertOne }}) UpdateNewValues() *{{ $upsertOne }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
|
||||
{{- if or $udfID $.ImmutableFields }}
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
|
||||
{{- if $udfID }}
|
||||
if _, exists := u.create.mutation.ID(); exists {
|
||||
s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $f := $.ImmutableFields }}
|
||||
if _, exists := u.create.mutation.{{ $f.MutationGet }}(); exists {
|
||||
s.SetIgnore({{ $.Package }}.{{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
}))
|
||||
{{- end }}
|
||||
return u
|
||||
}
|
||||
|
||||
// Ignore sets each column to itself in case of conflict.
|
||||
// Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(sql.ResolveWithIgnore()).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func (u *{{ $upsertOne }}) Ignore() *{{ $upsertOne }} {
|
||||
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 *{{ $upsertOne }}) DoNothing() *{{ $upsertOne }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.DoNothing())
|
||||
return u
|
||||
}
|
||||
|
||||
// Update allows overriding fields `UPDATE` values. See the {{ $builder }}.OnConflict
|
||||
// documentation for more info.
|
||||
func (u *{{ $upsertOne }}) Update(set func(*{{ $upsertSet }})) *{{ $upsertOne }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
|
||||
set(&{{ $upsertSet }}{UpdateSet: update})
|
||||
}))
|
||||
return u
|
||||
}
|
||||
|
||||
{{ with extend $ "Upsert" $upsertOne "UpsertSet" $upsertSet }}
|
||||
{{ template "helper/upsert/fields" . }}
|
||||
{{ end }}
|
||||
|
||||
// Exec executes the query.
|
||||
func (u *{{ $upsertOne }}) Exec(ctx context.Context) error {
|
||||
if len(u.create.conflict) == 0 {
|
||||
return errors.New("{{ $pkg }}: missing options for {{ $builder }}.OnConflict")
|
||||
}
|
||||
return u.create.Exec(ctx)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (u *{{ $upsertOne }}) ExecX(ctx context.Context) {
|
||||
if err := u.create.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{ if $.HasOneFieldID }}
|
||||
// Exec executes the UPSERT query and returns the inserted/updated ID.
|
||||
func (u *{{ $upsertOne }}) ID(ctx context.Context) (id {{ $.ID.Type }}, err error) {
|
||||
{{- if and $udfID (not $.ID.Type.Numeric) }}
|
||||
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("{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL driver. Use {{ $upsertOne }}.Exec instead")
|
||||
}
|
||||
{{- end }}
|
||||
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 *{{ $upsertOne }}) IDX(ctx context.Context) {{ $.ID.Type }} {
|
||||
id, err := u.ID(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ define "dialect/sql/create_bulk/additional/upsert" }}
|
||||
{{ if $.FeatureEnabled "sql/upsert" }}
|
||||
{{ template "helper/upsertbulk" $ }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Template for adding the "OnConflict" methods to the create-bulk builder. */}}
|
||||
{{ define "helper/upsertbulk" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $upsertBulk := print $.Name "UpsertBulk" }}
|
||||
{{ $upsertSet := print $.Name "Upsert" }}
|
||||
{{ $udfID := false }}{{ if $.HasOneFieldID }}{{ $udfID = $.ID.UserDefined }}{{ end }}
|
||||
|
||||
// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
|
||||
// of the `INSERT` statement. For example:
|
||||
//
|
||||
// client.{{ $.Name }}.CreateBulk(builders...).
|
||||
// OnConflict(
|
||||
// // Update the row with the new values
|
||||
// // the was proposed for insertion.
|
||||
// sql.ResolveWithNewValues(),
|
||||
// ).
|
||||
{{- with $.Fields }}
|
||||
{{- $f := index $.Fields 0 }}
|
||||
// // Override some of the fields with custom
|
||||
// // update values.
|
||||
// Update(func(u *ent.{{ $.Name }}Upsert) {
|
||||
// Set{{ index $f.StructField }}(v+v).
|
||||
// }).
|
||||
{{- end }}
|
||||
// Exec(ctx)
|
||||
//
|
||||
func ({{ $receiver }} *{{ $builder }}) OnConflict(opts ...sql.ConflictOption) *{{ $upsertBulk }} {
|
||||
{{ $receiver }}.conflict = opts
|
||||
return &{{ $upsertBulk }}{
|
||||
create: {{ $receiver }},
|
||||
}
|
||||
}
|
||||
|
||||
// OnConflictColumns calls `OnConflict` and configures the columns
|
||||
// as conflict target. Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(sql.ConflictColumns(columns...)).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func ({{ $receiver }} *{{ $builder }}) OnConflictColumns(columns ...string) *{{ $upsertBulk }} {
|
||||
{{ $receiver }}.conflict = append({{ $receiver }}.conflict, sql.ConflictColumns(columns...))
|
||||
return &{{ $upsertBulk }}{
|
||||
create: {{ $receiver }},
|
||||
}
|
||||
}
|
||||
|
||||
// {{ $upsertBulk }} is the builder for "upsert"-ing
|
||||
// a bulk of {{ $.Name }} nodes.
|
||||
type {{ $upsertBulk }} struct {
|
||||
create *{{ $builder }}
|
||||
}
|
||||
|
||||
|
||||
// UpdateNewValues updates the mutable fields using the new values that
|
||||
// were set on create. Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(
|
||||
// sql.ResolveWithNewValues(),
|
||||
{{- if $udfID }}
|
||||
// sql.ResolveWith(func(u *sql.UpdateSet) {
|
||||
// u.SetIgnore({{ $.Package }}.{{ $.ID.Constant }})
|
||||
// }),
|
||||
{{- end }}
|
||||
// ).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func (u *{{ $upsertBulk }}) UpdateNewValues() *{{ $upsertBulk }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
|
||||
{{- if or $udfID $.ImmutableFields }}
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
|
||||
for _, b := range u.create.builders {
|
||||
{{- if $udfID }}
|
||||
if _, exists := b.mutation.ID(); exists {
|
||||
s.SetIgnore({{ $.Package }}.{{ $.ID.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $f := $.ImmutableFields }}
|
||||
if _, exists := b.mutation.{{ $f.MutationGet }}(); exists {
|
||||
s.SetIgnore({{ $.Package }}.{{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
}))
|
||||
{{- end }}
|
||||
return u
|
||||
}
|
||||
|
||||
// Ignore sets each column to itself in case of conflict.
|
||||
// Using this option is equivalent to using:
|
||||
//
|
||||
// client.{{ $.Name }}.Create().
|
||||
// OnConflict(sql.ResolveWithIgnore()).
|
||||
// Exec(ctx)
|
||||
//
|
||||
func (u *{{ $upsertBulk }}) Ignore() *{{ $upsertBulk }} {
|
||||
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 *{{ $upsertBulk }}) DoNothing() *{{ $upsertBulk }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.DoNothing())
|
||||
return u
|
||||
}
|
||||
|
||||
// Update allows overriding fields `UPDATE` values. See the {{ $builder }}.OnConflict
|
||||
// documentation for more info.
|
||||
func (u *{{ $upsertBulk }}) Update(set func(*{{ $upsertSet }})) *{{ $upsertBulk }} {
|
||||
u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
|
||||
set(&{{ $upsertSet }}{UpdateSet: update})
|
||||
}))
|
||||
return u
|
||||
}
|
||||
|
||||
{{ with extend $ "Upsert" $upsertBulk "UpsertSet" $upsertSet }}
|
||||
{{ template "helper/upsert/fields" . }}
|
||||
{{ end }}
|
||||
|
||||
// Exec executes the query.
|
||||
func (u *{{ $upsertBulk }}) Exec(ctx context.Context) error {
|
||||
if u.create.err != nil {
|
||||
return u.create.err
|
||||
}
|
||||
for i, b := range u.create.builders {
|
||||
if len(b.conflict) != 0 {
|
||||
return fmt.Errorf("{{ $pkg }}: OnConflict was set for builder %d. Set it on the {{ $builder }} instead", i)
|
||||
}
|
||||
}
|
||||
if len(u.create.conflict) == 0 {
|
||||
return errors.New("{{ $pkg }}: missing options for {{ $builder }}.OnConflict")
|
||||
}
|
||||
return u.create.Exec(ctx)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (u *{{ $upsertBulk }}) ExecX(ctx context.Context) {
|
||||
if err := u.create.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "helper/upsert/fields" }}
|
||||
{{ $upsert := $.Scope.Upsert }}
|
||||
{{ $upsertSet := $.Scope.UpsertSet }}
|
||||
|
||||
{{ range $f := $.UpsertFields }}
|
||||
{{- if $f.IsBlob }}
|
||||
{{ $func := print "Update" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field to the value that was provided on create.
|
||||
func (u *{{ $upsert }}) {{ $func }}() *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}()
|
||||
})
|
||||
}
|
||||
|
||||
{{ if $f.Optional }}
|
||||
{{ $func := print "Clear" $f.StructField }}
|
||||
// {{ $func }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsert }}) {{ $func }}() *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}()
|
||||
})
|
||||
}
|
||||
{{ end }}
|
||||
{{- continue }}{{ end }}
|
||||
{{ $func := print "Set" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsert }}) {{ $func }}(v {{ $f.Type }}) *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}(v)
|
||||
})
|
||||
}
|
||||
|
||||
{{ if $f.SupportsMutationAdd }}
|
||||
{{ $func := print "Add" $f.StructField }}
|
||||
// {{ $func }} adds v to the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsert }}) {{ $func }}(v {{ $f.Type }}) *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}(v)
|
||||
})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ $func = print "Update" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field to the value that was provided on create.
|
||||
func (u *{{ $upsert }}) {{ $func }}() *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}()
|
||||
})
|
||||
}
|
||||
|
||||
{{ if $f.Optional }}
|
||||
{{ $func := print "Clear" $f.StructField }}
|
||||
// {{ $func }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (u *{{ $upsert }}) {{ $func }}() *{{ $upsert }} {
|
||||
return u.Update(func(s *{{ $upsertSet }}) {
|
||||
s.{{ $func }}()
|
||||
})
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
14
entc/gen/template/dialect/sql/globals.tmpl
Normal file
14
entc/gen/template/dialect/sql/globals.tmpl
Normal file
@@ -0,0 +1,14 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{/* custom globals and helpers for sql dialects */}}
|
||||
{{ define "dialect/sql/globals" }}
|
||||
|
||||
// queryHook describes an internal hook for the different sqlAll methods.
|
||||
type queryHook func(context.Context, *sqlgraph.QuerySpec)
|
||||
{{ end }}
|
||||
40
entc/gen/template/dialect/sql/group.tmpl
Normal file
40
entc/gen/template/dialect/sql/group.tmpl
Normal file
@@ -0,0 +1,40 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{ define "dialect/sql/group" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlScan(ctx context.Context, root *{{ $.QueryName }}, v any) error {
|
||||
selector := root.sqlQuery(ctx).Select()
|
||||
aggregation := make([]string, 0, len({{ $receiver}}.fns))
|
||||
for _, fn := range {{ $receiver }}.fns {
|
||||
aggregation = append(aggregation, fn(selector))
|
||||
}
|
||||
{{- /* If no columns were selected, the default selection is the fields used for "group-by", and the aggregation functions.*/}}
|
||||
if len(selector.SelectedColumns()) == 0 {
|
||||
columns := make([]string, 0, len(*{{ $receiver }}.flds) + len({{ $receiver}}.fns))
|
||||
for _, f := range *{{ $receiver }}.flds {
|
||||
columns = append(columns, selector.C(f))
|
||||
}
|
||||
columns = append(columns, aggregation...)
|
||||
selector.Select(columns...)
|
||||
}
|
||||
selector.GroupBy(selector.Columns(*{{ $receiver }}.flds...)...)
|
||||
if err := selector.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
rows := &sql.Rows{}
|
||||
query, args := selector.Query()
|
||||
if err := {{ $receiver }}.build.driver.Query(ctx, query, args, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
return sql.ScanSlice(rows, v)
|
||||
}
|
||||
{{ end }}
|
||||
194
entc/gen/template/dialect/sql/meta.tmpl
Normal file
194
entc/gen/template/dialect/sql/meta.tmpl
Normal file
@@ -0,0 +1,194 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Type */}}
|
||||
|
||||
{{/* Constants needed for sql dialects. */}}
|
||||
{{ define "dialect/sql/meta/constants" }}
|
||||
{{- range $t := $.RelatedTypes }}
|
||||
{{- $withEID := $.HasCompositeID }}
|
||||
{{- if and (not $withEID) $t.HasOneFieldID }}
|
||||
{{- if ne $t.ID.StorageKey $.ID.StorageKey }}{{ $withEID = true }}{{ end }}
|
||||
{{- end }}
|
||||
{{- if $withEID }}
|
||||
// {{ $t.Name }}FieldID holds the string denoting the ID field of the {{ $t.Name }}.
|
||||
{{ $t.Name }}FieldID = "{{ $t.ID.StorageKey }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
// Table holds the table name of the {{ lower $.Name }} in the database.
|
||||
Table = "{{ $.Table }}"
|
||||
{{- range $e := $.Edges }}
|
||||
// {{ $e.TableConstant }} is the table that holds the {{ $e.Name }} relation/edge.
|
||||
{{- if $e.M2M }} The primary key declared below.{{ end }}
|
||||
{{ $e.TableConstant }} = "{{ $e.Rel.Table }}"
|
||||
{{- if ne $.Table $e.Type.Table }}
|
||||
// {{ $e.InverseTableConstant }} is the table name for the {{ $e.Type.Name }} entity.
|
||||
// It exists in this package in order to avoid circular dependency with the "{{ $e.Type.Package }}" package.
|
||||
{{ $e.InverseTableConstant }} = "{{ $e.Type.Table }}"
|
||||
{{- end }}
|
||||
{{- if not $e.M2M }}
|
||||
// {{ $e.ColumnConstant }} is the table column denoting the {{ $e.Name }} relation/edge.
|
||||
{{ $e.ColumnConstant }} = "{{ $e.Rel.Column }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/meta/constants/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Variables needed for sql dialects. */}}
|
||||
{{ define "dialect/sql/meta/variables" }}
|
||||
// Columns holds all SQL columns for {{ lower $.Name }} fields.
|
||||
var Columns = []string{
|
||||
{{- if $.HasOneFieldID }}
|
||||
{{ $.ID.Constant }},
|
||||
{{- end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if and (not $f.IsDeprecated) (not $f.IsBlobNoColumn) (not $f.IsBlobLazy) }}
|
||||
{{ $f.Constant }},
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $k := $.BlobKeys }}
|
||||
"{{ $k.Field.Name }}",
|
||||
{{- end }}
|
||||
}
|
||||
{{/* If any of the edges owns a foreign-key */}}
|
||||
{{ with $.UnexportedForeignKeys }}
|
||||
// ForeignKeys holds the SQL foreign-keys that are owned by the "{{ $.Table }}"
|
||||
// table and are not defined as standalone fields in the schema.
|
||||
var ForeignKeys = []string{
|
||||
{{- range $fk := . }}
|
||||
"{{ $fk.Edge.Rel.Column }}",
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ with $.BlobKeys }}
|
||||
// BlobKeys holds the SQL columns for blob storage keys.
|
||||
var BlobKeys = []string{
|
||||
{{- range $bk := . }}
|
||||
"{{ $bk.Field.Name }}",
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ with $.NumM2M }}
|
||||
var (
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if $e.M2M }}
|
||||
// {{ $e.PKConstant }} and {{ $e.ColumnConstant }}2 are the table columns denoting the
|
||||
// primary key for the {{ $e.Name }} relation (M2M).
|
||||
{{ $e.PKConstant }} = []string{"{{ index $e.Rel.Columns 0 }}", "{{ index $e.Rel.Columns 1 }}"}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
)
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Functions needed for sql dialects. */}}
|
||||
{{ define "dialect/sql/meta/functions" }}
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
func ValidColumn(column string) bool {
|
||||
for i := range Columns {
|
||||
if column == Columns[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
{{- with $.UnexportedForeignKeys }}
|
||||
for i := range ForeignKeys {
|
||||
if column == ForeignKeys[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- with $.BlobKeys }}
|
||||
for i := range BlobKeys {
|
||||
if column == BlobKeys[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- with $.DeprecatedFields }}
|
||||
for _, f := range [...]string{ {{- range . }}{{ .Constant }},{{ end }} } {
|
||||
if column == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
return false
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* Type-safe order-by builders. */}}
|
||||
{{ define "dialect/sql/meta/order" }}
|
||||
{{ if $.HasOneFieldID }}
|
||||
// {{ $.ID.OrderName }} orders the results by the {{ $.ID.Name }} field.
|
||||
func {{ $.ID.OrderName }}(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField({{ $.ID.Constant }}, opts...).ToFunc()
|
||||
}
|
||||
{{ end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.Type.Comparable }}
|
||||
// {{ $f.OrderName }} orders the results by the {{ $f.Name }} field.
|
||||
func {{ $f.OrderName }}(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField({{ $f.Constant }}, opts...).ToFunc()
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if $e.Unique }}
|
||||
// {{ $e.OrderFieldName }} orders the results by {{ $e.Name }} field.
|
||||
func {{ $e.OrderFieldName }}(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, new{{ pascal $e.Name }}Step(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
// {{ $e.OrderCountName }} orders the results by {{ $e.Name }} count.
|
||||
func {{ $e.OrderCountName }}(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, new{{ pascal $e.Name }}Step(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// {{ $e.OrderTermsName }} orders the results by {{ $e.Name }} terms.
|
||||
func {{ $e.OrderTermsName }}(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, new{{ pascal $e.Name }}Step(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
func new{{ pascal $e.Name }}Step() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
{{- if $.HasCompositeID }}
|
||||
{{- /* Query that goes from the edge schema. */}}
|
||||
sqlgraph.From(Table, {{ $e.ColumnConstant }}),
|
||||
sqlgraph.To({{ $e.InverseTableConstant }}, {{ print $e.Type.Name "FieldID" }}),
|
||||
{{- else }}
|
||||
sqlgraph.From(Table, {{ $.ID.Constant }}),
|
||||
{{- if $e.Type.HasOneFieldID }}
|
||||
{{- $refid := $.ID.Constant }}{{ if ne $e.Type.ID.StorageKey $.ID.StorageKey }}{{ $refid = print $e.Type.Name "FieldID" }}{{ end }}
|
||||
sqlgraph.To({{ if ne $.Table $e.Type.Table }}{{ $e.InverseTableConstant }}{{ else }}Table{{ end }}, {{ $refid }}),
|
||||
{{- else }}
|
||||
{{- /* Query that goes to the edge schema. */}}
|
||||
sqlgraph.To({{ if ne $.Table $e.Type.Table }}{{ $e.InverseTableConstant }}{{ else }}Table{{ end }}, {{ $e.ColumnConstant }}),
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
sqlgraph.Edge(sqlgraph.{{ $e.Rel.Type }}, {{ $e.IsInverse }}, {{ $e.TableConstant }},
|
||||
{{- if $e.M2M -}}
|
||||
{{ $e.PKConstant }}...
|
||||
{{- else -}}
|
||||
{{ $e.ColumnConstant }}
|
||||
{{- end -}}
|
||||
),
|
||||
)
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
15
entc/gen/template/dialect/sql/open.tmpl
Normal file
15
entc/gen/template/dialect/sql/open.tmpl
Normal file
@@ -0,0 +1,15 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{ define "dialect/sql/client/open" }}
|
||||
drv, err := sql.Open(driverName, dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewClient(append(options, Driver(drv))...), nil
|
||||
{{ end }}
|
||||
99
entc/gen/template/dialect/sql/predicate.tmpl
Normal file
99
entc/gen/template/dialect/sql/predicate.tmpl
Normal file
@@ -0,0 +1,99 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{ define "dialect/sql/predicate/id" -}}
|
||||
{{- $arg := "id" }}
|
||||
{{- with $.Scope.Arg }}
|
||||
{{- $arg = . }}
|
||||
{{- end -}}
|
||||
sql.FieldEQ({{ $.ID.Constant }}, {{ $arg }})
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/id/ops" -}}
|
||||
{{- $op := $.Scope.Op -}}
|
||||
{{- $storage := $.Scope.Storage -}}
|
||||
{{- $arg := "id" -}}
|
||||
{{- if $op.Variadic }}{{ $arg = "ids" }}{{ end -}}
|
||||
{{- with $.Scope.Arg -}}
|
||||
{{- $arg = . -}}
|
||||
{{- end -}}
|
||||
sql.Field{{ call $storage.OpCode $op }}({{ $.ID.Constant }}{{ if not $op.Niladic }}, {{ $arg }}{{ if $op.Variadic }}...{{ end }}{{ end }})
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/field" -}}
|
||||
{{- $f := $.Scope.Field -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
sql.FieldEQ({{ $f.Constant }}, {{ $arg }})
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/field/ops" -}}
|
||||
{{- $f := $.Scope.Field -}}
|
||||
{{- $op := $.Scope.Op -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
{{- $storage := $.Scope.Storage -}}
|
||||
sql.Field{{ call $storage.OpCode $op }}({{ $f.Constant }}{{ if not $op.Niladic }}, {{ $arg }}{{ if $op.Variadic }}...{{ end }}{{ end }})
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/edge/has" -}}
|
||||
{{- $e := $.Scope.Edge -}}
|
||||
func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
{{- if $.HasCompositeID }}
|
||||
{{- /* Query that goes from the edge schema. */}}
|
||||
sqlgraph.From(Table, {{ $e.ColumnConstant }}),
|
||||
{{- else }}
|
||||
sqlgraph.From(Table, {{ $.ID.Constant }}),
|
||||
{{- end }}
|
||||
sqlgraph.Edge(sqlgraph.{{ $e.Rel.Type }}, {{ $e.IsInverse }}, {{ $e.TableConstant }},
|
||||
{{- if $e.M2M -}}
|
||||
{{ $e.PKConstant }}...
|
||||
{{- else -}}
|
||||
{{ $e.ColumnConstant }}
|
||||
{{- end -}}
|
||||
),
|
||||
)
|
||||
{{- /* Allow mutating the sqlgraph.Step by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/predicate/edge/has/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* sqlgraph.To is meaningless on Has<E> (without predicates), because unlike Has<E>With no predicates are applied on the "other" table. */}}
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/edge/haswith" -}}
|
||||
{{- $e := $.Scope.Edge -}}
|
||||
func(s *sql.Selector) {
|
||||
step := new{{ pascal $e.Name }}Step()
|
||||
{{- /* Allow mutating the sqlgraph.Step by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/predicate/edge/haswith/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/and" -}}
|
||||
sql.AndPredicates(predicates...)
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/or" -}}
|
||||
sql.OrPredicates(predicates...)
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/predicate/not" -}}
|
||||
sql.NotPredicates(p)
|
||||
{{- end }}
|
||||
601
entc/gen/template/dialect/sql/query.tmpl
Normal file
601
entc/gen/template/dialect/sql/query.tmpl
Normal file
@@ -0,0 +1,601 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{/* Additional fields for the builder. */}}
|
||||
{{ define "dialect/sql/query/fields" }}
|
||||
{{- with $.UnexportedForeignKeys }}
|
||||
withFKs bool
|
||||
{{- end }}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/query" }}
|
||||
{{ $pkg := $.Scope.Package }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlAll(ctx context.Context, hooks ...queryHook) ([]*{{ $.Name }}, error) {
|
||||
var (
|
||||
nodes = []*{{ $.Name }}{}
|
||||
{{- with $.UnexportedForeignKeys }}
|
||||
withFKs = {{ $receiver }}.withFKs
|
||||
{{- end }}
|
||||
_spec = {{ $receiver }}.querySpec()
|
||||
{{- with $.Edges }}
|
||||
loadedTypes = [{{ len . }}]bool{
|
||||
{{- range $e := . }}
|
||||
{{ $receiver }}.{{ $e.EagerLoadField }} != nil,
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
)
|
||||
{{- with $.UnexportedForeignKeys }}
|
||||
{{- $edgesWithoutField := list }}
|
||||
{{- range $.FKEdges }}{{ if not .Field }}{{ $edgesWithoutField = append $edgesWithoutField . }}{{ end }}{{ end }}
|
||||
{{- if $edgesWithoutField }}
|
||||
if {{ range $i, $e := $edgesWithoutField }}{{ if $i }} || {{ end }}{{ $receiver }}.{{ $e.EagerLoadField }} != nil{{ end }} {
|
||||
withFKs = true
|
||||
}
|
||||
{{- end }}
|
||||
if withFKs {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, {{ $.Package }}.ForeignKeys...)
|
||||
}
|
||||
{{- end }}
|
||||
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
return (*{{ $.Name }}).scanValues(nil, columns)
|
||||
}
|
||||
_spec.Assign = func(columns []string, values []any) error {
|
||||
node := &{{ $.Name }}{config: {{ $receiver }}.config}
|
||||
nodes = append(nodes, node)
|
||||
{{- with $.Edges }}
|
||||
node.Edges.loadedTypes = loadedTypes
|
||||
{{- end }}
|
||||
return node.assignValues(columns, values)
|
||||
}
|
||||
{{- /* Allow mutating the sqlgraph.QuerySpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
for i := range hooks {
|
||||
hooks[i](ctx, _spec)
|
||||
}
|
||||
if err := sqlgraph.QueryNodes(ctx, {{ $receiver }}.driver, _spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
{{- range $e := $.Edges }}
|
||||
if query := {{ $receiver }}.{{ $e.EagerLoadField }}; query != nil {
|
||||
if err := {{ $receiver }}.load{{ $e.StructField }}(ctx, query, nodes, {{ if $e.Unique }}nil{{ else }}
|
||||
func(n *{{ $.Name }}){ n.Edges.{{ $e.StructField }} = []*{{ $e.Type.Name }}{} }{{ end }},
|
||||
{{- $lhs := printf "n.Edges.%s" $e.StructField }}
|
||||
{{- $rhs := print "e" }}{{- if not $e.Unique }}{{ $rhs = printf "append(%s, e)" $lhs }}{{ end }}
|
||||
{{- if and ($.FeatureEnabled "bidiedges") $e.Ref $e.Ref.Unique }}
|
||||
func(n *{{ $.Name }}, e *{{ $e.Type.Name }}){
|
||||
{{ printf "%s = %s" $lhs $rhs }}
|
||||
{{- $idx := $e.Ref.Index }}
|
||||
{{- /* Set only in case this type was not loaded explicitly (without custom options). */}}
|
||||
if !e.Edges.loadedTypes[{{ $idx }}] {
|
||||
e.Edges.{{ $e.Ref.StructField }} = n
|
||||
}
|
||||
}); err != nil {
|
||||
{{- else }}
|
||||
{{- /* Keep it one-liner if there is not inverse-condition. */}}
|
||||
func(n *{{ $.Name }}, e *{{ $e.Type.Name }}){ {{ printf "%s = %s" $lhs $rhs }} }); err != nil {
|
||||
{{- end }}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- /* Allow extensions to inject code using templates to process nodes before they are returned. */}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/all/nodes/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.HasLoadOnScanFields }}
|
||||
if err := {{ $receiver }}.loadBlobFields(ctx, nodes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
{{- if $.HasLoadOnScanFields }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) loadBlobFields(ctx context.Context, nodes []*{{ $.Name }}) error {
|
||||
_blobs := ent.NewBlobStore({{ $receiver }}.blobOpeners.{{ $.Name }})
|
||||
for _, n := range nodes {
|
||||
{{- range $f := $.LoadOnScanFields }}
|
||||
if n.{{ $f.BlobKeyColumn }} != nil && *n.{{ $f.BlobKeyColumn }} != "" {
|
||||
{{- if $f.IsBlobNoColumn }}
|
||||
{{- /* Non-DualWrite: blob is the only source of data. */}}
|
||||
data, err := _blobs.Read(ctx, {{ $.Package }}.{{ $f.Constant }}, *n.{{ $f.BlobKeyColumn }})
|
||||
if err != nil {
|
||||
return errors.Join(fmt.Errorf("loading {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
}
|
||||
if data == nil {
|
||||
return errors.Join(fmt.Errorf("loading {{ $f.Name }}: object %q not found in blob storage", *n.{{ $f.BlobKeyColumn }}), _blobs.Close())
|
||||
}
|
||||
{{- if $f.HasValueScanner }}
|
||||
sv := {{ $f.ScanValueFunc }}()
|
||||
if err := sv.Scan(data); err != nil {
|
||||
return errors.Join(fmt.Errorf("scanning {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
}
|
||||
v, err := {{ $f.FromValueFunc }}(sv)
|
||||
if err != nil {
|
||||
return errors.Join(fmt.Errorf("scanning {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
}
|
||||
n.{{ $f.StructField }} = v
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
n.{{ $f.StructField }} = {{ $f.Type }}(data)
|
||||
{{- else }}
|
||||
n.{{ $f.StructField }} = data
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- /* DualWrite: prefer blob value, but preserve the column value when blob is missing. */}}
|
||||
switch data, err := _blobs.Read(ctx, {{ $.Package }}.{{ $f.Constant }}, *n.{{ $f.BlobKeyColumn }}); {
|
||||
case err != nil:
|
||||
return errors.Join(fmt.Errorf("loading {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
case data != nil:
|
||||
{{- if $f.HasValueScanner }}
|
||||
sv := {{ $f.ScanValueFunc }}()
|
||||
if err := sv.Scan(data); err != nil {
|
||||
return errors.Join(fmt.Errorf("scanning {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
}
|
||||
v, err := {{ $f.FromValueFunc }}(sv)
|
||||
if err != nil {
|
||||
return errors.Join(fmt.Errorf("scanning {{ $f.Name }}: %w", err), _blobs.Close())
|
||||
}
|
||||
n.{{ $f.StructField }} = v
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
n.{{ $f.StructField }} = {{ $f.Type }}(data)
|
||||
{{- else }}
|
||||
n.{{ $f.StructField }} = data
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
return _blobs.Close()
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{/* Generate a method to eager-load each edge. */}}
|
||||
{{- range $e := $.Edges }}
|
||||
func ({{ $receiver }} *{{ $builder }}) load{{ $e.StructField }}(ctx context.Context, query *{{ $e.Type.QueryName }}, nodes []*{{ $.Name }}, init func(*{{ $.Name }}), assign func(*{{ $.Name }}, *{{ $e.Type.Name }})) error {
|
||||
{{- if $e.M2M }}
|
||||
edgeIDs := make([]driver.Value, len(nodes))
|
||||
byID := make(map[{{ $.ID.Type }}]*{{ $.Name }})
|
||||
nids := make(map[{{ $e.Type.ID.Type }}]map[*{{ $.Name }}]struct{})
|
||||
for i, node := range nodes {
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
vv, err := {{ $.ID.ValueFunc }}(node.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
edgeIDs[i] = vv
|
||||
{{- else }}
|
||||
edgeIDs[i] = node.ID
|
||||
{{- end }}
|
||||
byID[node.ID] = node
|
||||
if init != nil {
|
||||
init(node)
|
||||
}
|
||||
}
|
||||
query.Where(func(s *sql.Selector) {
|
||||
joinT := sql.Table({{ $.Package }}.{{ $e.TableConstant }})
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/eagerloading/join/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- with extend $ "Edge" $e }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $edgeid := print $e.Type.Package "." $e.Type.ID.Constant }}
|
||||
{{- $fk1idx := 1 }}{{- $fk2idx := 0 }}{{ if $e.IsInverse }}{{ $fk1idx = 0 }}{{ $fk2idx = 1 }}{{ end }}
|
||||
s.Join(joinT).On(s.C({{ $edgeid }}), joinT.C({{ $.Package }}.{{ $e.PKConstant }}[{{ $fk1idx }}]))
|
||||
s.Where(sql.InValues(joinT.C({{ $.Package }}.{{ $e.PKConstant }}[{{ $fk2idx }}]), edgeIDs...))
|
||||
columns := s.SelectedColumns()
|
||||
s.Select(joinT.C({{ $.Package }}.{{ $e.PKConstant }}[{{ $fk2idx }}]))
|
||||
s.AppendSelect(columns...)
|
||||
s.SetDistinct(false)
|
||||
})
|
||||
if err := query.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) {
|
||||
assign := spec.Assign
|
||||
values := spec.ScanValues
|
||||
{{- $out := "sql.NullInt64" }}{{ if $.ID.UserDefined }}{{ $out = $.ID.ScanType }}{{ end }}
|
||||
{{- $in := "sql.NullInt64" }}{{ if $e.Type.ID.UserDefined }}{{ $in = $e.Type.ID.ScanType }}{{ end }}
|
||||
spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
values, err := values(columns[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
return append([]any{ {{ $.ID.ScanValueFunc }}() }, values...), nil
|
||||
{{- else }}
|
||||
return append([]any{new({{ $out }})}, values...), nil
|
||||
{{- end }}
|
||||
}
|
||||
spec.Assign = func(columns []string, values []any) error {
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
var outValue {{ $.ID.Type }}
|
||||
{{- with extend $ "Arg" "values[0]" "Field" $.ID "ScanType" $out "Ident" "outValue" }}
|
||||
{{ template "dialect/sql/query/eagerloading/m2massign" . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
outValue := {{- with extend $ "Arg" "values[0]" "Field" $.ID "ScanType" $out }}{{ template "dialect/sql/query/eagerloading/m2massignexpr" . }}{{- end }}
|
||||
{{- end }}
|
||||
{{- if $e.Type.ID.HasValueScanner }}
|
||||
var inValue {{ $e.Type.ID.Type }}
|
||||
{{- with extend $ "Arg" "values[1]" "Field" $e.Type.ID "ScanType" $in "Ident" "inValue" }}
|
||||
{{ template "dialect/sql/query/eagerloading/m2massign" . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
inValue := {{- with extend $ "Arg" "values[1]" "Field" $e.Type.ID "ScanType" $in }}{{ template "dialect/sql/query/eagerloading/m2massignexpr" . }}{{- end }}
|
||||
{{- end }}
|
||||
if nids[inValue] == nil {
|
||||
nids[inValue] = map[*{{ $.Name }}]struct{}{byID[outValue]: {}}
|
||||
return assign(columns[1:], values[1:])
|
||||
}
|
||||
nids[inValue][byID[outValue]] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
})
|
||||
neighbors, err := withInterceptors[[]*{{ $e.Type.Name }}](ctx, query, qr, query.inters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected "{{ $e.Name }}" node returned %v`, n.ID)
|
||||
}
|
||||
for kn := range nodes {
|
||||
assign(kn, n)
|
||||
}
|
||||
}
|
||||
{{- else if $e.OwnFK }}
|
||||
ids := make([]{{ $e.Type.ID.Type }}, 0, len(nodes))
|
||||
nodeids := make(map[{{ $e.Type.ID.Type }}][]*{{ $.Name }})
|
||||
for i := range nodes {
|
||||
{{- $fk := $e.ForeignKey }}
|
||||
{{- if $fk.Field.Nillable }}
|
||||
if nodes[i].{{ $fk.StructField }} == nil {
|
||||
continue
|
||||
}
|
||||
{{- end }}
|
||||
fk := {{ if $fk.Field.Nillable }}*{{ end }}nodes[i].{{ $fk.StructField }}
|
||||
if _, ok := nodeids[fk]; !ok {
|
||||
ids = append(ids, fk)
|
||||
}
|
||||
nodeids[fk] = append(nodeids[fk], nodes[i])
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
query.Where({{ $e.Type.Package }}.IDIn(ids...))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nodeids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected foreign-key "{{ $fk.Field.Name }}" returned %v`, n.ID)
|
||||
}
|
||||
for i := range nodes {
|
||||
assign(nodes[i], n)
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
fks := make([]driver.Value, 0, len(nodes))
|
||||
nodeids := make(map[{{ $.ID.Type }}]*{{ $.Name }})
|
||||
for i := range nodes {
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
vv, err := {{ $.ID.ValueFunc }}(nodes[i].ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fks = append(fks, vv)
|
||||
{{- else }}
|
||||
fks = append(fks, nodes[i].ID)
|
||||
{{- end }}
|
||||
nodeids[nodes[i].ID] = nodes[i]
|
||||
{{- if $e.O2M }}
|
||||
if init != nil {
|
||||
init(nodes[i])
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
{{- with $e.Type.UnexportedForeignKeys }}
|
||||
query.withFKs = true
|
||||
{{- end }}
|
||||
{{- /* In case of custom field selection, ensure edge-fields used for binding below were selected. */}}
|
||||
{{- if and $e.Ref $e.Ref.Field }}
|
||||
if len(query.ctx.Fields) > 0 {
|
||||
query.ctx.AppendFieldOnce({{ $e.Type.Package }}.{{ $e.Ref.Field.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
query.Where(predicate.{{ $e.Type.Name }}(func(s *sql.Selector) {
|
||||
s.Where(sql.InValues(s.C({{ $.Package }}.{{ $e.ColumnConstant }}), fks...))
|
||||
}))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
{{- $fk := $e.ForeignKey }}
|
||||
fk := n.{{ $fk.StructField }}
|
||||
{{- if $fk.Field.Nillable }}
|
||||
if fk == nil {
|
||||
return fmt.Errorf(`foreign-key "{{ $fk.Field.Name }}" is nil for node %v`, n.ID)
|
||||
}
|
||||
{{- end }}
|
||||
node, ok := nodeids[{{ if $fk.Field.Nillable }}*{{ end }}fk]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected referenced foreign-key "{{ $fk.Field.Name }}" returned %v for node %v`, {{ if $fk.Field.Nillable }}*{{ end }}fk, n{{ if $e.Type.HasOneFieldID }}.ID{{ end }})
|
||||
}
|
||||
assign(node, n)
|
||||
}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlCount(ctx context.Context) (int, error) {
|
||||
_spec := {{ $receiver }}.querySpec()
|
||||
{{- /* Allow mutating the sqlgraph.QuerySpec by ent extensions or user templates. */}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.HasCompositeID }}
|
||||
{{- /* In case of an edge schema with composite primary-key, there is no need to SELECT DISTINCT. */}}
|
||||
_spec.Unique = false
|
||||
_spec.Node.Columns = nil
|
||||
{{- else }}
|
||||
_spec.Node.Columns = {{ $receiver }}.ctx.Fields
|
||||
if len({{ $receiver }}.ctx.Fields) > 0 {
|
||||
{{- /* In case of field selection, configure query to unique only if was explicitly set to true. */}}
|
||||
_spec.Unique = {{ $receiver }}.ctx.Unique != nil && *{{ $receiver }}.ctx.Unique
|
||||
}
|
||||
{{- end }}
|
||||
return sqlgraph.CountNodes(ctx, {{ $receiver }}.driver, _spec)
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) querySpec() *sqlgraph.QuerySpec {
|
||||
_spec := sqlgraph.NewQuerySpec({{ $.Package }}.Table, {{ $.Package }}.Columns, {{ if $.HasOneFieldID }}sqlgraph.NewFieldSpec({{ $.Package }}.{{ $.ID.Constant }}, field.{{ $.ID.Type.ConstName }}){{ else }}nil{{ end }})
|
||||
{{- /* Setup any intermediate queries if exist (traversal path). */}}
|
||||
_spec.From = {{ $receiver }}.sql
|
||||
if unique := {{ $receiver }}.ctx.Unique; unique != nil {
|
||||
_spec.Unique = *unique
|
||||
} else if {{ $receiver }}.path != nil {
|
||||
_spec.Unique = true
|
||||
}
|
||||
if fields := {{ $receiver }}.ctx.Fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
{{- if $.HasOneFieldID }}
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, {{ $.Package }}.{{ $.ID.Constant }})
|
||||
for i := range fields {
|
||||
if fields[i] != {{ $.Package }}.{{ $.ID.Constant }} {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||
}
|
||||
}
|
||||
{{- else }}
|
||||
for i := range fields {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||
}
|
||||
{{- end }}
|
||||
{{- /* In case of custom field selection, ensure edge-fields are loaded in case their edges were loaded. */}}
|
||||
{{- range $.FKEdges }}
|
||||
{{- $f := .Field }}
|
||||
{{- if not $f }}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
if {{ $receiver }}.{{ .EagerLoadField }} != nil {
|
||||
_spec.Node.AddColumnOnce({{ $.Package }}.{{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
if ps := {{ $receiver }}.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if limit := {{ $receiver }}.ctx.Limit; limit != nil {
|
||||
_spec.Limit = *limit
|
||||
}
|
||||
if offset := {{ $receiver }}.ctx.Offset; offset != nil {
|
||||
_spec.Offset = *offset
|
||||
}
|
||||
if ps := {{ $receiver }}.order; len(ps) > 0 {
|
||||
_spec.Order = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
return _spec
|
||||
}
|
||||
|
||||
{{ template "dialect/sql/query/selector" $ }}
|
||||
|
||||
{{- /* Allow adding methods to the query-builder by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/sql/query/selector" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlQuery(ctx context.Context) *sql.Selector {
|
||||
{{- $builderV := "builder" }}{{ if eq $.Package $builderV }}{{ $builderV = "builderC" }}{{ end }}
|
||||
{{ $builderV }} := sql.Dialect({{ $receiver }}.driver.Dialect())
|
||||
t1 := {{ $builderV }}.Table({{ $.Package }}.Table)
|
||||
columns := {{ $receiver }}.ctx.Fields
|
||||
if len(columns) == 0 {
|
||||
columns = {{ $.Package }}.Columns
|
||||
}
|
||||
selector := {{ $builderV }}.Select(t1.Columns(columns...)...).From(t1)
|
||||
if {{ $receiver }}.sql != nil {
|
||||
selector = {{ $receiver }}.sql
|
||||
selector.Select(selector.Columns(columns...)...)
|
||||
}
|
||||
if {{ $receiver }}.ctx.Unique != nil && *{{ $receiver }}.ctx.Unique {
|
||||
selector.Distinct()
|
||||
}
|
||||
{{- /* Allow mutating the sql.Selector by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/selector/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
for _, p := range {{ $receiver }}.predicates {
|
||||
p(selector)
|
||||
}
|
||||
for _, p := range {{ $receiver }}.order {
|
||||
p(selector)
|
||||
}
|
||||
if offset := {{ $receiver }}.ctx.Offset; offset != nil {
|
||||
// limit is mandatory for offset clause. We start
|
||||
// with default value, and override it below if needed.
|
||||
selector.Offset(*offset).Limit(math.MaxInt32)
|
||||
}
|
||||
if limit := {{ $receiver }}.ctx.Limit; limit != nil {
|
||||
selector.Limit(*limit)
|
||||
}
|
||||
return selector
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* query/path defines the query generation for path of a given edge. */}}
|
||||
{{ define "dialect/sql/query/path" }}
|
||||
{{- $n := $ }} {{/* the node we start the query from. */}}
|
||||
{{- $e := $.Scope.Edge }} {{/* the edge we need to generate the path to. */}}
|
||||
{{- $ident := $.Scope.Ident -}}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
selector := {{ $receiver }}.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From({{ $n.Package }}.Table, {{ $n.Package }}.{{ if $n.HasCompositeID }}{{ $e.ColumnConstant }}{{ else }}{{ $n.ID.Constant }}{{ end }}, selector),
|
||||
sqlgraph.To({{ $e.Type.Package }}.Table, {{ $e.Type.Package }}.{{ if $e.Type.HasCompositeID }}{{ $e.Ref.ColumnConstant }}{{ else }}{{ $e.Type.ID.Constant }}{{ end }}),
|
||||
sqlgraph.Edge(sqlgraph.{{ $e.Rel.Type }}, {{ $e.IsInverse }}, {{ $n.Package }}.{{ $e.TableConstant }},
|
||||
{{- if $e.M2M -}}
|
||||
{{ $n.Package }}.{{ $e.PKConstant }}...
|
||||
{{- else -}}
|
||||
{{ $n.Package }}.{{ $e.ColumnConstant }}
|
||||
{{- end -}}
|
||||
),
|
||||
)
|
||||
{{- /* Allow mutating the sqlgraph.Step by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/path/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ $ident }} = sqlgraph.SetNeighbors({{ $receiver }}.driver.Dialect(), step)
|
||||
{{ end }}
|
||||
|
||||
{{/* query/from defines the query generation for an edge query from a given node. */}}
|
||||
{{ define "dialect/sql/query/from" }}
|
||||
{{- $n := $ }} {{/* the node we start the query from. */}}
|
||||
{{- $e := $.Scope.Edge }} {{/* the edge we need to genegrate the path to. */}}
|
||||
{{- $ident := $.Scope.Ident -}}
|
||||
{{- $receiver := $.Scope.Receiver -}}
|
||||
{{- if $n.ID.HasValueScanner -}}
|
||||
id := any({{ $receiver }}.ID)
|
||||
vv, err := {{ $n.ID.ValueFunc }}({{ $receiver }}.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id = vv
|
||||
{{- else -}}
|
||||
id := {{ $receiver }}.ID
|
||||
{{- end }}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From({{ $n.Package }}.Table, {{ $n.Package }}.{{ $n.ID.Constant }}, id),
|
||||
sqlgraph.To({{ $e.Type.Package }}.Table, {{ $e.Type.Package }}.{{ if $e.Type.HasCompositeID }}{{ $e.Ref.ColumnConstant }}{{ else }}{{ $e.Type.ID.Constant }}{{ end }}),
|
||||
sqlgraph.Edge(sqlgraph.{{ $e.Rel.Type }}, {{ $e.IsInverse }}, {{ $n.Package }}.{{ $e.TableConstant }},
|
||||
{{- if $e.M2M -}}
|
||||
{{ $n.Package }}.{{ $e.PKConstant }}...
|
||||
{{- else -}}
|
||||
{{ $n.Package }}.{{ $e.ColumnConstant }}
|
||||
{{- end -}}
|
||||
),
|
||||
)
|
||||
{{- /* Allow mutating the sqlgraph.Step by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/query/from/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ $ident }} = sqlgraph.Neighbors({{ $receiver }}.driver.Dialect(), step)
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/sql/query/eagerloading/m2massign" -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
{{- $field := $.Scope.Field -}}
|
||||
{{- $scantype := $.Scope.ScanType -}}
|
||||
{{- $ident := $.Scope.Ident -}}
|
||||
{{- if $field.HasValueScanner }}
|
||||
if value, err := {{ $field.FromValueFunc }}({{ $arg }}); err != nil {
|
||||
return err
|
||||
} else {
|
||||
{{ $ident }} = value
|
||||
}
|
||||
{{- else if hasPrefix $scantype "sql" -}}
|
||||
{{ $ident }} = {{ printf "%s.(*%s)" $arg $scantype | $field.ScanTypeField }}
|
||||
{{- else -}}
|
||||
{{ $ident }} = {{ if not $field.Nillable }}*{{ end }}{{ printf "%s.(*%s)" $arg $scantype }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/query/eagerloading/m2massignexpr" -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
{{- $field := $.Scope.Field -}}
|
||||
{{- $scantype := $.Scope.ScanType -}}
|
||||
{{- if hasPrefix $scantype "sql" -}}
|
||||
{{ printf "%s.(*%s)" $arg $scantype | $field.ScanTypeField -}}
|
||||
{{- else -}}
|
||||
{{ if not $field.Nillable }}*{{ end }}{{ printf "%s.(*%s)" $arg $scantype }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/query/preparecheck" }}
|
||||
{{- $pkg := $.Scope.Package }}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
for _, f := range {{ $receiver }}.ctx.Fields {
|
||||
if !{{ $.Package }}.ValidColumn(f) {
|
||||
return &ValidationError{Name: f, err: fmt.Errorf("{{ $pkg }}: invalid field %q for query", f)}
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
42
entc/gen/template/dialect/sql/select.tmpl
Normal file
42
entc/gen/template/dialect/sql/select.tmpl
Normal file
@@ -0,0 +1,42 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{ define "dialect/sql/select" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlScan(ctx context.Context, root *{{ $.QueryName }}, v any) error {
|
||||
selector := root.sqlQuery(ctx)
|
||||
aggregation := make([]string, 0, len({{ $receiver}}.fns))
|
||||
for _, fn := range {{ $receiver }}.fns {
|
||||
aggregation = append(aggregation, fn(selector))
|
||||
}
|
||||
switch n := len(*{{ $receiver }}.selector.flds); {
|
||||
{{- /* If no columns were selected, the default selection is the aggregation.*/}}
|
||||
case n == 0 && len(aggregation) > 0:
|
||||
selector.Select(aggregation...)
|
||||
case n != 0 && len(aggregation) > 0:
|
||||
selector.AppendSelect(aggregation...)
|
||||
}
|
||||
rows := &sql.Rows{}
|
||||
query, args := selector.Query()
|
||||
if err := {{ $receiver }}.driver.Query(ctx, query, args, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
return sql.ScanSlice(rows, v)
|
||||
}
|
||||
|
||||
{{/* Allow adding methods to the select-builder by ent extensions or user templates.*/}}
|
||||
{{ with $tmpls := matchTemplate "dialect/sql/select/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
31
entc/gen/template/dialect/sql/tx.tmpl
Normal file
31
entc/gen/template/dialect/sql/tx.tmpl
Normal file
@@ -0,0 +1,31 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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.Graph */}}
|
||||
|
||||
{{ define "dialect/sql/txoptions" }}
|
||||
// BeginTx returns a transactional client with specified options.
|
||||
func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
|
||||
if _, ok := c.driver.(*txDriver); ok {
|
||||
return nil, errors.New("ent: cannot start a transaction within a transaction")
|
||||
}
|
||||
tx, err := c.driver.(interface {
|
||||
BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error)
|
||||
}).BeginTx(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ent: starting a transaction: %w", err)
|
||||
}
|
||||
cfg := c.config
|
||||
cfg.driver = &txDriver{tx: tx, drv: c.driver}
|
||||
return &Tx{
|
||||
ctx: ctx,
|
||||
config: cfg,
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.Name }}: New{{ $n.ClientName }}(cfg),
|
||||
{{- end }}
|
||||
}, nil
|
||||
}
|
||||
{{ end }}
|
||||
395
entc/gen/template/dialect/sql/update.tmpl
Normal file
395
entc/gen/template/dialect/sql/update.tmpl
Normal file
@@ -0,0 +1,395 @@
|
||||
{{/*
|
||||
Copyright 2019-present Facebook Inc. All rights reserved.
|
||||
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 */}}
|
||||
|
||||
{{/* Additional fields for the builder. */}}
|
||||
{{ define "dialect/sql/update/fields" }}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/update/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/sql/update" }}
|
||||
{{ $pkg := $.Scope.Package }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
{{ $one := hasSuffix $builder "One" }}
|
||||
{{- $zero := 0 }}{{ if $one }}{{ $zero = "nil" }}{{ end }}
|
||||
|
||||
{{- /* Allow adding methods to the update-builder by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/update/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) sqlSave(ctx context.Context) (_node {{ if $one }}*{{ $.Name }}{{ else }}int{{ end }}, err error) {
|
||||
{{- if $.HasUpdateCheckers }}
|
||||
if err := {{ $receiver }}.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
{{- end }}
|
||||
_spec := sqlgraph.NewUpdateSpec({{ $.Package }}.Table, {{ $.Package }}.Columns,
|
||||
{{- if $.HasOneFieldID -}}
|
||||
sqlgraph.NewFieldSpec({{ $.Package }}.{{ $.ID.Constant }}, field.{{ $.ID.Type.ConstName }})
|
||||
{{- else -}}
|
||||
{{- range $id := $.EdgeSchema.ID -}}
|
||||
sqlgraph.NewFieldSpec({{ $.Package }}.{{ $id.Constant }}, field.{{ $id.Type.ConstName }}),
|
||||
{{- end -}}
|
||||
{{- end }})
|
||||
{{- if $one }}
|
||||
{{- if $.HasOneFieldID }}
|
||||
id, ok := {{ $mutation }}.{{ $.ID.MutationGet }}()
|
||||
if !ok {
|
||||
return {{ $zero }}, &ValidationError{Name: "{{ $.ID.Name }}", err: errors.New(`{{ $pkg }}: missing "{{ $.Name }}.{{ $.ID.Name }}" for update`)}
|
||||
}
|
||||
{{- if $.ID.HasValueScanner }}
|
||||
vv, err := {{ $.ID.ValueFunc }}(id)
|
||||
if err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
_spec.Node.ID.Value = vv
|
||||
{{- else }}
|
||||
_spec.Node.ID.Value = id
|
||||
{{- end }}
|
||||
if fields := {{ $receiver }}.fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, {{ $.Package }}.{{ $.ID.Constant }})
|
||||
for _, f := range fields {
|
||||
if !{{ $.Package }}.ValidColumn(f) {
|
||||
return nil, &ValidationError{Name: f, err: fmt.Errorf("{{ $pkg }}: invalid field %q for query", f)}
|
||||
}
|
||||
if f != {{ $.Package }}.{{ $.ID.Constant }} {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
{{- else }}{{/* Composite ID. */}}
|
||||
{{- range $i, $id := $.EdgeSchema.ID }}
|
||||
if id, ok := {{ $mutation }}.{{ $id.MutationGet }}(); !ok {
|
||||
return {{ $zero }}, &ValidationError{Name: "{{ $id.Name }}", err: errors.New(`{{ $pkg }}: missing "{{ $.Name }}.{{ $id.Name }}" for update`)}
|
||||
} else {
|
||||
_spec.Node.CompositeID[{{ $i }}].Value = id
|
||||
}
|
||||
{{- end }}
|
||||
if fields := {{ $receiver }}.fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, len(fields))
|
||||
for i, f := range fields {
|
||||
if !{{ $.Package }}.ValidColumn(f) {
|
||||
return nil, &ValidationError{Name: f, err: fmt.Errorf("{{ $pkg }}: invalid field %q for query", f)}
|
||||
}
|
||||
_spec.Node.Columns[i] = f
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if ps := {{ $mutation }}.Predicates(); len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{- if $f.IsBlobNoColumn }}{{ continue }}{{ end }}
|
||||
{{- if or (not $f.Immutable) $f.UpdateDefault }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
vv, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
_spec.SetField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, vv)
|
||||
{{- else }}
|
||||
_spec.SetField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, value)
|
||||
{{- end }}
|
||||
}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationAdded }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
vv, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
_spec.AddField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, vv)
|
||||
{{- else }}
|
||||
_spec.AddField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }}, value)
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $f.SupportsMutationAppend }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationAppended }}(); ok {
|
||||
_spec.AddModifier(func(u *sql.UpdateBuilder) {
|
||||
sqljson.Append(u, {{ $.Package }}.{{ $f.Constant }}, value)
|
||||
})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $f.Optional }}
|
||||
if {{ $mutation }}.{{ $f.StructField }}Cleared() {
|
||||
_spec.ClearField({{ $.Package }}.{{ $f.Constant }}, field.{{ $f.Type.ConstName }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- if $e.Immutable }}
|
||||
{{- /* Skip to the next one as immutable edges cannot be updated. */}}
|
||||
{{- continue }}
|
||||
{{- end }}
|
||||
if {{ $mutation }}.{{ $e.MutationCleared }}() {
|
||||
{{- with extend $ "Edge" $e }}
|
||||
{{ template "dialect/sql/defedge" . }}
|
||||
{{- end }}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
{{- if not $e.Unique }}
|
||||
if nodes := {{ $mutation }}.Removed{{ $e.StructField }}IDs(); len(nodes) > 0 && !{{ $mutation }}.{{ $e.MutationCleared }}() {
|
||||
{{- with extend $ "Edge" $e "Nodes" true "Zero" $zero }}
|
||||
{{ template "dialect/sql/defedge" . }}
|
||||
{{- end }}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
{{- end }}
|
||||
if nodes := {{ $mutation }}.{{ $e.StructField }}IDs(); len(nodes) > 0 {
|
||||
{{- with extend $ "Edge" $e "Nodes" true "Zero" $zero }}
|
||||
{{ template "dialect/sql/defedge" . }}
|
||||
{{- end }}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
{{- end }}
|
||||
{{- /* Allow mutating the sqlgraph.UpdateSpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/update/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and $one $.HasBlobFields }}
|
||||
_blobs := ent.NewBlobs({{ $mutation }}.blobOpeners.{{ $.Name }})
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.HasValueScanner }}
|
||||
_blobDV, err := {{ $f.ValueFunc }}(value)
|
||||
if err != nil {
|
||||
return {{ $zero }}, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: %w", err)
|
||||
}
|
||||
var _blobData []byte
|
||||
switch v := _blobDV.(type) {
|
||||
case []byte:
|
||||
_blobData = v
|
||||
case string:
|
||||
_blobData = []byte(v)
|
||||
default:
|
||||
return {{ $zero }}, fmt.Errorf("{{ $pkg }}: encoding {{ $f.Name }}: expected []byte or string, got %T", _blobDV)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, []byte(value),
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- else }}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, value,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
{{- end }}
|
||||
}
|
||||
{{- if $f.Optional }}
|
||||
if {{ $mutation }}.{{ $f.StructField }}Cleared() {
|
||||
_blobs.SetCleared({{ $.Package }}.{{ $f.Constant }}, func() {
|
||||
_spec.ClearField("{{ $f.BlobKeyColumn }}", field.TypeString)
|
||||
})
|
||||
}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
if r, ok := {{ $mutation }}.{{ $f.StructField }}(); ok {
|
||||
_blobData, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return {{ $zero }}, fmt.Errorf("{{ $pkg }}: reading {{ $f.Name }}: %w", err)
|
||||
}
|
||||
_blobs.Set({{ $.Package }}.{{ $f.Constant }}, _blobData,
|
||||
{{ if $f.HasBlobKey }}{{ $.Package }}.{{ $f.BlobKeyName }}{{ else }}defaultBlobKey{{ end }},
|
||||
func(k string) {
|
||||
_spec.SetField("{{ $f.BlobKeyColumn }}", field.TypeString, k)
|
||||
},
|
||||
)
|
||||
}
|
||||
{{- if $f.Optional }}
|
||||
if {{ $mutation }}.{{ $f.MutationCleared }}() {
|
||||
_blobs.SetCleared({{ $.Package }}.{{ $f.Constant }}, func() {
|
||||
_spec.ClearField("{{ $f.BlobKeyColumn }}", field.TypeString)
|
||||
})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
_blobResult, err := _blobs.Update(ctx, &sqlgraph.BlobSpec{
|
||||
Driver: {{ $receiver }}.driver,
|
||||
Table: {{ $.Package }}.Table,
|
||||
Columns: map[string]string{
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: "{{ $f.BlobKeyColumn }}",
|
||||
{{- end }}
|
||||
},
|
||||
{{- if $.HasBlobRefCheckedFields }}
|
||||
CheckRefs: map[string]bool{
|
||||
{{- range $f := $.BlobRefCheckedFields }}
|
||||
{{ $.Package }}.{{ $f.Constant }}: true,
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
Predicate: func(s *sql.Selector) {
|
||||
s.Where(sql.EQ({{ $.Package }}.{{ $.ID.Constant }}, _spec.Node.ID.Value))
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $one }}
|
||||
_node = &{{ $.Name }}{config: {{ $receiver }}.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
{{- end }}
|
||||
{{- if $one }}
|
||||
if err = sqlgraph.UpdateNode(ctx, {{ $receiver }}.driver, _spec); err != nil {
|
||||
{{- else }}
|
||||
if _node, err = sqlgraph.UpdateNodes(ctx, {{ $receiver }}.driver, _spec); err != nil {
|
||||
{{- end }}
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{ {{ $.Package }}.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
{{- if and $one $.HasBlobFields }}
|
||||
return {{ $zero }}, errors.Join(err, _blobResult.Rollback(ctx))
|
||||
{{- else }}
|
||||
return {{ $zero }}, err
|
||||
{{- end }}
|
||||
}
|
||||
{{ $mutation }}.done = true
|
||||
{{- if and $one $.HasBlobFields }}
|
||||
// Resolve which replaced blobs are unreferenced now that the row moved to its new
|
||||
// keys, while the transaction is still open, and remove them once it commits.
|
||||
_blobCleanup, err := _blobResult.Cleanup(ctx)
|
||||
if err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
if txd, ok := {{ $receiver }}.driver.(*txDriver); ok {
|
||||
txd.mu.Lock()
|
||||
txd.onCommit = append(txd.onCommit, func(next Committer) Committer {
|
||||
return CommitFunc(func(ctx context.Context, tx *Tx) error {
|
||||
if err := next.Commit(ctx, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
return _blobCleanup(ctx)
|
||||
})
|
||||
})
|
||||
txd.onRollback = append(txd.onRollback, func(next Rollbacker) Rollbacker {
|
||||
return RollbackFunc(func(ctx context.Context, tx *Tx) error {
|
||||
err := next.Rollback(ctx, tx)
|
||||
return errors.Join(err, _blobResult.Rollback(ctx))
|
||||
})
|
||||
})
|
||||
txd.mu.Unlock()
|
||||
} else if err := _blobCleanup(ctx); err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
{{- end }}
|
||||
{{- if and $one $.HasLoadOnScanFields }}
|
||||
{{- /* Blob values are consistent after write — use mutation value for mutated fields
|
||||
instead of re-reading from storage. For DualWrite fields, assignValues already
|
||||
populated the value from the SQL column. For non-DualWrite fields not mutated,
|
||||
read from blob storage. */}}
|
||||
_blobReader := ent.NewBlobStore({{ $mutation }}.blobOpeners.{{ $.Name }})
|
||||
{{- range $f := $.LoadOnScanFields }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
_node.{{ $f.StructField }} = value
|
||||
}
|
||||
{{- if $f.IsBlobNoColumn }} else if _node.{{ $f.BlobKeyColumn }} != nil && *_node.{{ $f.BlobKeyColumn }} != "" {
|
||||
data, err := _blobReader.Read(ctx, {{ $.Package }}.{{ $f.Constant }}, *_node.{{ $f.BlobKeyColumn }})
|
||||
if err != nil {
|
||||
return nil, errors.Join(fmt.Errorf("loading {{ $f.Name }} after update: %w", err), _blobReader.Close())
|
||||
}
|
||||
{{- if $f.HasValueScanner }}
|
||||
sv := {{ $f.ScanValueFunc }}()
|
||||
if err := sv.Scan(data); err != nil {
|
||||
return nil, errors.Join(fmt.Errorf("scanning {{ $f.Name }} after update: %w", err), _blobReader.Close())
|
||||
}
|
||||
v, err := {{ $f.FromValueFunc }}(sv)
|
||||
if err != nil {
|
||||
return nil, errors.Join(fmt.Errorf("scanning {{ $f.Name }} after update: %w", err), _blobReader.Close())
|
||||
}
|
||||
_node.{{ $f.StructField }} = v
|
||||
{{- else if $f.IsBlobGoString }}
|
||||
_node.{{ $f.StructField }} = {{ $f.Type }}(data)
|
||||
{{- else }}
|
||||
_node.{{ $f.StructField }} = data
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if err := _blobReader.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
return _node, nil
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/sql/defedge" }}
|
||||
{{- $e := $.Scope.Edge -}}
|
||||
{{- $receiver := $.Scope.Receiver -}}
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.{{ $e.Rel.Type }},
|
||||
Inverse: {{ $e.IsInverse }},
|
||||
Table: {{ $.Package }}.{{ $e.TableConstant }},
|
||||
Columns: {{ if $e.M2M }}{{ $.Package }}.{{ $e.PKConstant }}{{ else }}[]string{ {{ $.Package }}.{{ $e.ColumnConstant }} }{{ end }},
|
||||
Bidi: {{ $e.Bidi }},
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec({{ $e.Type.Package }}.{{ $e.Type.ID.Constant }}, field.{{ $e.Type.ID.Type.ConstName }}),
|
||||
},
|
||||
}
|
||||
{{- /* Allow mutating the sqlgraph.EdgeSpec by ent extensions or user templates.*/}}
|
||||
{{- with $tmpls := matchTemplate "dialect/sql/defedge/spec/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with $.Scope.Nodes }}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
{{- end }}
|
||||
{{- with $e.Through }}
|
||||
{{- if .HasDefault }}
|
||||
createE := &{{ .CreateName }}{config: {{ $receiver }}.config, mutation: new{{ .MutationName }}({{ $receiver }}.config, OpCreate)}
|
||||
{{- /* Skip error handling here as this check was already handled. */}}
|
||||
{{ if or .NumHooks .NumPolicy }}_ = {{ end }}createE.defaults()
|
||||
_, specE := createE.createSpec()
|
||||
edge.Target.Fields = specE.Fields
|
||||
{{- if and .HasOneFieldID .ID.Default }}
|
||||
if specE.ID.Value != nil {
|
||||
edge.Target.Fields = append(edge.Target.Fields, specE.ID)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
Reference in New Issue
Block a user