snapshot of https://github.com/ent/ent.git (master, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
429
entc/gen/template/base.tmpl
Normal file
429
entc/gen/template/base.tmpl
Normal file
@@ -0,0 +1,429 @@
|
||||
{{/*
|
||||
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 "base" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ template "import" $ }}
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $n.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
// ent aliases to avoid import conflicts in user's code.
|
||||
type (
|
||||
Op = ent.Op
|
||||
Hook = ent.Hook
|
||||
Value = ent.Value
|
||||
Query = ent.Query
|
||||
QueryContext = ent.QueryContext
|
||||
Querier = ent.Querier
|
||||
QuerierFunc = ent.QuerierFunc
|
||||
Interceptor = ent.Interceptor
|
||||
InterceptFunc = ent.InterceptFunc
|
||||
Traverser = ent.Traverser
|
||||
TraverseFunc = ent.TraverseFunc
|
||||
Policy = ent.Policy
|
||||
Mutator = ent.Mutator
|
||||
Mutation = ent.Mutation
|
||||
MutateFunc = ent.MutateFunc
|
||||
)
|
||||
|
||||
type clientCtxKey struct{}
|
||||
|
||||
// FromContext returns a Client stored inside a context, or nil if there isn't one.
|
||||
func FromContext(ctx context.Context) *Client {
|
||||
c, _ := ctx.Value(clientCtxKey{}).(*Client)
|
||||
return c
|
||||
}
|
||||
|
||||
// NewContext returns a new context with the given Client attached.
|
||||
func NewContext(parent context.Context, c *Client) context.Context {
|
||||
return context.WithValue(parent, clientCtxKey{}, c)
|
||||
}
|
||||
|
||||
type txCtxKey struct {}
|
||||
|
||||
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
|
||||
func TxFromContext(ctx context.Context) *Tx {
|
||||
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
|
||||
return tx
|
||||
}
|
||||
|
||||
// NewTxContext returns a new context with the given Tx attached.
|
||||
func NewTxContext(parent context.Context, tx *Tx) context.Context {
|
||||
return context.WithValue(parent, txCtxKey{}, tx)
|
||||
}
|
||||
|
||||
{{ $tmpl := printf "dialect/%s/order/signature" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
|
||||
{{ range $f, $order := order }}
|
||||
{{ $f = pascal $f }}
|
||||
// {{ $f }} applies the given fields in {{ upper $f }} order.
|
||||
func {{ $f }}(fields ...string) func({{ $.Storage.Builder }}) {
|
||||
{{- with extend (index $.Nodes 0) "Func" $f "Order" $order -}}
|
||||
{{ $tmpl := printf "dialect/%s/order/func" $.Storage }}
|
||||
return {{ xtemplate $tmpl . }}
|
||||
{{ end -}}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/group/signature" $.Storage }}
|
||||
// AggregateFunc applies an aggregation step on the group-by traversal/selector.
|
||||
{{ xtemplate $tmpl . }}
|
||||
|
||||
|
||||
// As is a pseudo aggregation function for renaming another other functions with custom names. For example:
|
||||
//
|
||||
// GroupBy(field1, field2).
|
||||
// Aggregate({{ $pkg }}.As({{ $pkg }}.Sum(field1), "sum_field1"), ({{ $pkg }}.As({{ $pkg }}.Sum(field2), "sum_field2")).
|
||||
// Scan(ctx, &v)
|
||||
//
|
||||
func As(fn AggregateFunc, end string) AggregateFunc {
|
||||
{{- $tmpl = printf "dialect/%s/group/as" $.Storage }}
|
||||
return {{ xtemplate $tmpl . }}
|
||||
}
|
||||
|
||||
{{ range $name, $withField := aggregate }}
|
||||
{{ $fn := pascal $name }}
|
||||
{{ $tmpl := printf "dialect/%s/group/const" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ with extend (index $.Nodes 0) "Name" $name "Func" $fn -}}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
// {{ $fn }} applies the {{ quote $name }} aggregation function on {{ if $withField }}the given field of {{ end }}each group.
|
||||
func {{ $fn }}({{ if $withField }}field string{{ end }}) AggregateFunc {
|
||||
{{- with extend (index $.Nodes 0) "Func" $fn "WithField" $withField -}}
|
||||
{{ $tmpl := printf "dialect/%s/group/func" $.Storage }}
|
||||
return {{ xtemplate $tmpl . }}
|
||||
{{ end -}}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// ValidationError returns when validating a field or edge fails.
|
||||
type ValidationError struct {
|
||||
Name string // Field or edge name.
|
||||
err error
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *ValidationError) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
// Unwrap implements the errors.Wrapper interface.
|
||||
func (e *ValidationError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// IsValidationError returns a boolean indicating whether the error is a validation error.
|
||||
func IsValidationError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var e *ValidationError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
// NotFoundError returns when trying to fetch a specific entity and it was not found in the database.
|
||||
type NotFoundError struct {
|
||||
label string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *NotFoundError) Error() string {
|
||||
return "{{ $pkg }}: " + e.label + " not found"
|
||||
}
|
||||
|
||||
// IsNotFound returns a boolean indicating whether the error is a not found error.
|
||||
func IsNotFound(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var e *NotFoundError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
// MaskNotFound masks not found error.
|
||||
func MaskNotFound(err error) error {
|
||||
if IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database.
|
||||
type NotSingularError struct {
|
||||
label string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *NotSingularError) Error() string {
|
||||
return "{{ $pkg }}: " + e.label + " not singular"
|
||||
}
|
||||
|
||||
// IsNotSingular returns a boolean indicating whether the error is a not singular error.
|
||||
func IsNotSingular(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var e *NotSingularError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
// NotLoadedError returns when trying to get a node that was not loaded by the query.
|
||||
type NotLoadedError struct {
|
||||
edge string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *NotLoadedError) Error() string {
|
||||
return "{{ $pkg }}: " + e.edge + " edge was not loaded"
|
||||
}
|
||||
|
||||
// IsNotLoaded returns a boolean indicating whether the error is a not loaded error.
|
||||
func IsNotLoaded(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var e *NotLoadedError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
// ConstraintError returns when trying to create/update one or more entities and
|
||||
// one or more of their constraints failed. For example, violation of edge or
|
||||
// field uniqueness.
|
||||
type ConstraintError struct {
|
||||
msg string
|
||||
wrap error
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e ConstraintError) Error() string {
|
||||
return "{{ $pkg }}: constraint failed: " + e.msg
|
||||
}
|
||||
|
||||
// Unwrap implements the errors.Wrapper interface.
|
||||
func (e *ConstraintError) Unwrap() error {
|
||||
return e.wrap
|
||||
}
|
||||
|
||||
// IsConstraintError returns a boolean indicating whether the error is a constraint failure.
|
||||
func IsConstraintError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var e *ConstraintError
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
|
||||
// selector embedded by the different Select/GroupBy builders.
|
||||
type selector struct {
|
||||
label string
|
||||
flds *[]string
|
||||
fns []AggregateFunc
|
||||
scan func (context.Context, any) error
|
||||
}
|
||||
|
||||
// ScanX is like Scan, but panics if an error occurs.
|
||||
func (s *selector) ScanX(ctx context.Context, v any) {
|
||||
if err := s.scan(ctx, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{ range $t := primitives }}
|
||||
{{ $plural := pascal $t | plural }}
|
||||
// {{ $plural }} returns list of {{ plural $t }} from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) {{ $plural }}(ctx context.Context) ([]{{ $t }}, error) {
|
||||
if len(*s.flds) > 1 {
|
||||
return nil, errors.New("{{ $pkg }}: {{ $plural }} is not achievable when selecting more than 1 field")
|
||||
}
|
||||
var v []{{ $t }}
|
||||
if err := s.scan(ctx, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// {{ $plural }}X is like {{ $plural }}, but panics if an error occurs.
|
||||
func (s *selector) {{ $plural }}X(ctx context.Context) []{{ $t }} {
|
||||
v, err := s.{{ $plural }}(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
{{ $singular := pascal $t -}}
|
||||
// {{ $singular }} returns a single {{ $t }} from a selector. It is only allowed when selecting one field.
|
||||
func (s *selector) {{ $singular }}(ctx context.Context) (_ {{ $t }}, err error) {
|
||||
var v []{{ $t }}
|
||||
if v, err = s.{{ $plural }}(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(v) {
|
||||
case 1:
|
||||
return v[0], nil
|
||||
case 0:
|
||||
err = &NotFoundError{s.label}
|
||||
default:
|
||||
err = fmt.Errorf("{{ $pkg }}: {{ $plural }} returned %d results when one was expected", len(v))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// {{ $singular }}X is like {{ $singular }}, but panics if an error occurs.
|
||||
func (s *selector) {{ $singular }}X(ctx context.Context) {{ $t }} {
|
||||
v, err := s.{{ $singular }}(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
// withHooks invokes the builder operation with the given hooks, if any.
|
||||
func withHooks[V Value, M any, PM interface {
|
||||
*M
|
||||
Mutation
|
||||
}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) {
|
||||
if len(hooks) == 0 {
|
||||
return exec(ctx)
|
||||
}
|
||||
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
|
||||
{{- /* redundant any() conversion is a workaround for golang/go#58633 */}}
|
||||
mutationT, ok := any(m).(PM)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected mutation type %T", m)
|
||||
}
|
||||
// Set the mutation to the builder.
|
||||
*mutation = *mutationT
|
||||
return exec(ctx)
|
||||
})
|
||||
for i := len(hooks) - 1; i >= 0; i-- {
|
||||
if hooks[i] == nil {
|
||||
return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
|
||||
}
|
||||
mut = hooks[i](mut)
|
||||
}
|
||||
v, err := mut.Mutate(ctx, mutation)
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
nv, ok := v.(V)
|
||||
if !ok {
|
||||
return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation)
|
||||
}
|
||||
return nv, nil
|
||||
}
|
||||
|
||||
// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist.
|
||||
func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context {
|
||||
if ent.QueryFromContext(ctx) == nil {
|
||||
qc.Op = op
|
||||
ctx = ent.NewQueryContext(ctx, qc)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func querierAll[V Value, Q interface {
|
||||
{{ $.Storage }}All(context.Context, ...queryHook) (V, error)
|
||||
}]() Querier {
|
||||
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
return query.{{ $.Storage }}All(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func querierCount[Q interface {
|
||||
{{ $.Storage }}Count(context.Context) (int, error)
|
||||
}]() Querier {
|
||||
return QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
return query.{{ $.Storage }}Count(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) {
|
||||
for i := len(inters) - 1; i >= 0; i-- {
|
||||
qr = inters[i].Intercept(qr)
|
||||
}
|
||||
rv, err := qr.Query(ctx, q)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
vt, ok := rv.(V)
|
||||
if !ok {
|
||||
return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v)
|
||||
}
|
||||
return vt, nil
|
||||
}
|
||||
|
||||
|
||||
func scanWithInterceptors[Q1 ent.Query, Q2 interface {
|
||||
{{ $.Storage }}Scan(context.Context, Q1, any) error
|
||||
}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error {
|
||||
rv := reflect.ValueOf(v)
|
||||
var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
|
||||
query, ok := q.(Q1)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected query type %T", q)
|
||||
}
|
||||
if err := selectOrGroup.{{ $.Storage }}Scan(ctx, query, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() {
|
||||
return rv.Elem().Interface(), nil
|
||||
}
|
||||
return v, nil
|
||||
})
|
||||
for i := len(inters) - 1; i >= 0; i-- {
|
||||
qr = inters[i].Intercept(qr)
|
||||
}
|
||||
vv, err := qr.Query(ctx, rootQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch rv2 := reflect.ValueOf(vv); {
|
||||
case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer:
|
||||
case rv.Type() == rv2.Type():
|
||||
rv.Elem().Set(rv2.Elem())
|
||||
case rv.Elem().Type() == rv2.Type():
|
||||
rv.Elem().Set(rv2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
{{/* expand error types and global helpers. */}}
|
||||
{{ $tmpl = printf "dialect/%s/errors" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/globals" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
173
entc/gen/template/builder/create.tmpl
Normal file
173
entc/gen/template/builder/create.tmpl
Normal file
@@ -0,0 +1,173 @@
|
||||
{{/*
|
||||
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 "create" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ $runtimeRequired := or $.NumHooks $.NumPolicy }}
|
||||
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ with extend $ "Imports" $.SiblingImports }}
|
||||
{{ template "import" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ $builder := $.CreateName }}
|
||||
{{ $receiver := $.CreateReceiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
// {{ $builder }} is the builder for creating a {{ $.Name }} entity.
|
||||
type {{ $builder }} struct {
|
||||
config
|
||||
mutation *{{ $.MutationName }}
|
||||
hooks []Hook
|
||||
{{- /* Additional fields to add to the builder. */}}
|
||||
{{- $tmpl := printf "dialect/%s/create/fields" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder }}
|
||||
{{ template "setter" . }}
|
||||
{{ end }}
|
||||
|
||||
// Save creates the {{ $.Name }} in the database.
|
||||
func ({{ $receiver }} *{{ $builder }}) Save(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
{{- if $.HasDefault }}
|
||||
{{- if $runtimeRequired }}
|
||||
if err := {{ $receiver }}.defaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- else }}
|
||||
{{ $receiver }}.defaults()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return withHooks(ctx, {{ $receiver }}.{{ $.Storage }}Save, {{ $mutation }}, {{ $receiver }}.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
{{- $fields := $.Fields }}{{ if $.HasOneFieldID }}{{ if $.ID.UserDefined }}{{ $fields = append $fields $.ID }}{{ end }}{{ end }}
|
||||
{{ if $.HasDefault }}
|
||||
// defaults sets the default values of the builder before save.
|
||||
func ({{ $receiver }} *{{ $builder }}) defaults() {{ if $runtimeRequired }}error{{ end }}{
|
||||
{{- range $f := $fields }}
|
||||
{{- if $f.Default }}
|
||||
if _, ok := {{ $mutation }}.{{ $f.MutationGet }}(); !ok {
|
||||
{{- if and $runtimeRequired $f.DefaultFunc }}
|
||||
if {{ $.Package }}.{{ $f.DefaultName }} == nil {
|
||||
return fmt.Errorf("{{ $pkg }}: uninitialized {{ $.Package }}.{{ $f.DefaultName }} (forgotten import {{ $pkg }}/runtime?)")
|
||||
}
|
||||
{{- end }}
|
||||
v := {{ $.Package }}.{{ $f.DefaultName }}{{ if $f.DefaultFunc }}(){{ end }}
|
||||
{{ $mutation }}.{{ $f.MutationSet }}(v)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $runtimeRequired }}
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) check() error {
|
||||
{{- range $f := $fields }}
|
||||
{{- if and $f.IsBlobLazy (not (hasTemplate (printf "dialect/%s/model/fields" $.Storage))) }}{{ continue }}{{ end }}
|
||||
{{- $skip := false }}{{ if $.HasOneFieldID }}{{ if eq $f.Name $.ID.Name }}{{ $skip = true }}{{ end }}{{ end }}
|
||||
{{- if and (not $f.Optional) (not $skip) }}
|
||||
{{- $dialects := $f.RequiredFor }}
|
||||
{{- $n := len $dialects }}
|
||||
{{- if $n }}
|
||||
{{- $partially := ne $n (len $.Config.Storage.Dialects) }}
|
||||
{{- if $partially }}
|
||||
switch {{ $receiver }}.driver.Dialect() {
|
||||
case {{ join $dialects ", " }}:
|
||||
{{- end }}
|
||||
if _, ok := {{ $mutation }}.{{ $f.MutationGet }}(); !ok {
|
||||
return &ValidationError{Name: "{{ $f.Name }}", err: errors.New(`{{ $pkg }}: missing required field "{{ $.Name }}.{{ $f.Name }}"`)}
|
||||
}
|
||||
{{- if $partially }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $isValidator := and ($f.HasGoType) ($f.Type.Validator) }}
|
||||
{{- with or $f.Validators $f.IsEnum $isValidator }}
|
||||
if v, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
if err := {{ if or $f.Validators $f.IsEnum }}{{ $.Package }}.{{ $f.Validator }}({{ $f.BasicType "v" }}){{ else }}v.Validate(){{ end }}; err != nil {
|
||||
return &ValidationError{Name: "{{ $f.Name }}", err: fmt.Errorf(`{{ $pkg }}: validator failed for field "{{ $.Name }}.{{ $f.Name }}": %w`, err)}
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- if not $e.Optional }}
|
||||
if len({{ $mutation }}.{{ $e.StructField }}IDs()) == 0 {
|
||||
return &ValidationError{Name: "{{ $e.Name }}", err: errors.New(`{{ $pkg }}: missing required edge "{{ $.Name }}.{{ $e.Name }}"`)}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder }}
|
||||
{{ $tmpl := printf "dialect/%s/create" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{- /* Support adding create methods by global templates. */}}
|
||||
{{- with $tmpls := matchTemplate "create/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ $bulk := $.CreateBulkName }}
|
||||
{{ $receiver = $.CreateBulReceiver }}
|
||||
|
||||
// {{ $bulk }} is the builder for creating many {{ $.Name }} entities in bulk.
|
||||
type {{ $bulk }} struct {
|
||||
config
|
||||
err error
|
||||
builders []*{{ $builder }}
|
||||
{{- /* Additional fields to add to the builder. */}}
|
||||
{{- $tmpl = printf "dialect/%s/create_bulk/fields" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{/* If the storage driver supports bulk creation */}}
|
||||
{{ $tmpl = printf "dialect/%s/create_bulk" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $bulk }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
97
entc/gen/template/builder/delete.tmpl
Normal file
97
entc/gen/template/builder/delete.tmpl
Normal file
@@ -0,0 +1,97 @@
|
||||
{{/*
|
||||
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 "delete" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ template "import" $ }}
|
||||
|
||||
import (
|
||||
{{ $.PackageAlias }} "{{ $.Config.Package }}/{{ $.PackageDir }}"
|
||||
)
|
||||
|
||||
{{ $builder := $.DeleteName }}
|
||||
{{ $receiver := $.DeleteReceiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
// {{ $builder }} is the builder for deleting a {{ $.Name }} entity.
|
||||
type {{ $builder }} struct {
|
||||
config
|
||||
hooks []Hook
|
||||
mutation *{{ $.MutationName }}
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the {{ $builder }} builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) Where(ps ...predicate.{{ $.Name }}) *{{ $builder }} {
|
||||
{{ $mutation }}.Where(ps...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func ({{ $receiver }} *{{ $builder }}) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, {{ $receiver }}.{{ $.Storage }}Exec, {{ $mutation }}, {{ $receiver }}.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) ExecX(ctx context.Context) int {
|
||||
n, err := {{ $receiver }}.Exec(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder }}
|
||||
{{ $tmpl := printf "dialect/%s/delete" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{- /* Support adding delete methods by global templates. */}}
|
||||
{{- with $tmpls := matchTemplate "delete/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ $onebuilder := $.DeleteOneName }}
|
||||
{{ $oneReceiver := $.DeleteOneReceiver }}
|
||||
|
||||
// {{ $onebuilder }} is the builder for deleting a single {{ $.Name }} entity.
|
||||
type {{ $onebuilder }} struct {
|
||||
{{ $receiver }} *{{ $builder }}
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the {{ $builder }} builder.
|
||||
func ({{ $oneReceiver }} *{{ $onebuilder }}) Where(ps ...predicate.{{ $.Name }}) *{{ $onebuilder }} {
|
||||
{{ $oneReceiver }}.{{ $mutation }}.Where(ps...)
|
||||
return {{ $oneReceiver }}
|
||||
}
|
||||
|
||||
// Exec executes the deletion query.
|
||||
func ({{ $oneReceiver }} *{{ $onebuilder }}) Exec(ctx context.Context) error {
|
||||
n, err := {{ $oneReceiver }}.{{ $receiver }}.Exec(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case n == 0:
|
||||
return &NotFoundError{ {{ $.Package }}.Label}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func ({{ $oneReceiver }} *{{ $onebuilder }}) ExecX(ctx context.Context) {
|
||||
if err := {{ $oneReceiver }}.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
14
entc/gen/template/builder/entql.tmpl
Normal file
14
entc/gen/template/builder/entql.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 */}}
|
||||
|
||||
{{ define "entql" }}
|
||||
{{ $tmpl := printf "dialect/%s/entql" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
213
entc/gen/template/builder/mutation_graph.tmpl
Normal file
213
entc/gen/template/builder/mutation_graph.tmpl
Normal file
@@ -0,0 +1,213 @@
|
||||
{{/*
|
||||
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 "mutation_graph" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ template "header" $ }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- template "import/types" $n }}
|
||||
{{- end }}
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
const (
|
||||
// Operation types.
|
||||
OpCreate = ent.OpCreate
|
||||
OpDelete = ent.OpDelete
|
||||
OpDeleteOne = ent.OpDeleteOne
|
||||
OpUpdate = ent.OpUpdate
|
||||
OpUpdateOne = ent.OpUpdateOne
|
||||
|
||||
// Node types.
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.TypeName }} = "{{ $n.Name }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
{{ range $n := $.MutableNodes }}
|
||||
|
||||
{{ $mutation := $n.MutationName }}
|
||||
{{ $baseMutation := print $n.Package ".Mutation" }}
|
||||
// {{ $mutation }} represents an operation that mutates the {{ $n.Name }} nodes in the graph.
|
||||
type {{ $mutation }} struct {
|
||||
{{ $baseMutation }}
|
||||
config
|
||||
{{- with $n.HasOneFieldID }}
|
||||
{{ $n.ID.BuilderField }} *{{ $n.ID.Type }}
|
||||
{{- end }}
|
||||
done bool
|
||||
oldValue func(context.Context) (*{{ $n.Name }}, error)
|
||||
}
|
||||
|
||||
var _ ent.Mutation = (*{{ $mutation }})(nil)
|
||||
|
||||
{{ $mutationOption := print (camel $n.Name) "Option" }}
|
||||
// {{ $mutationOption }} allows management of the mutation configuration using functional options.
|
||||
type {{ $mutationOption }} func(*{{ $mutation }})
|
||||
|
||||
// new{{ $mutation }} creates new mutation for the {{ $n.Name }} entity.
|
||||
func new{{ $mutation }}(c config, op Op, opts ...{{ $mutationOption }}) *{{ $mutation }} {
|
||||
m := &{{ $mutation }}{
|
||||
Mutation: *{{ $n.Package }}.NewMutation(op),
|
||||
config: c,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
{{ with $n.HasOneFieldID }}
|
||||
{{ if $n.ID.UserDefined }}
|
||||
// SetID sets the value of the id field. Note that this
|
||||
// operation is only accepted on creation of {{ $n.Name }} entities.
|
||||
func (m *{{ $mutation }}) SetID(id {{ $n.ID.Type }}) {
|
||||
m.{{ $n.ID.BuilderField }} = &id
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// ID returns the ID value in the mutation. Note that the ID is only available
|
||||
// if it was provided to the builder or after it was returned from the database.
|
||||
func (m *{{ $mutation }}) ID() (id {{ $n.ID.Type }}, exists bool) {
|
||||
if m.{{ $n.ID.BuilderField }} == nil {
|
||||
return
|
||||
}
|
||||
return *m.{{ $n.ID.BuilderField }}, true
|
||||
}
|
||||
|
||||
{{ $opt := print "with" $n.Name "ID" }}
|
||||
// {{ $opt }} sets the ID field of the mutation.
|
||||
func {{ $opt }}(id {{ $n.ID.Type }}) {{ $mutationOption }} {
|
||||
return func(m *{{ $mutation }}) {
|
||||
var (
|
||||
err error
|
||||
once sync.Once
|
||||
value *{{ $n.Name }}
|
||||
)
|
||||
m.oldValue = func(ctx context.Context) (*{{ $n.Name }}, error) {
|
||||
once.Do(func() {
|
||||
if m.done {
|
||||
err = errors.New("querying old values post mutation is not allowed")
|
||||
} else {
|
||||
value, err = m.Client().{{ $n.Name }}.Get(ctx, id)
|
||||
}
|
||||
})
|
||||
return value, err
|
||||
}
|
||||
m.{{ $n.ID.BuilderField }} = &id
|
||||
}
|
||||
}
|
||||
|
||||
{{ $opt = print "with" $n.Name }}
|
||||
// {{ $opt }} sets the old {{ $n.Name }} of the mutation.
|
||||
func {{ $opt }}(node *{{ $n.Name }}) {{ $mutationOption }} {
|
||||
return func(m *{{ $mutation }}) {
|
||||
m.oldValue = func(context.Context) (*{{ $n.Name }}, error) {
|
||||
return node, nil
|
||||
}
|
||||
m.{{ $n.ID.BuilderField }} = &node.ID
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Client returns a new `ent.Client` from the mutation. If the mutation was
|
||||
// executed in a transaction (ent.Tx), a transactional client is returned.
|
||||
func (m {{ $mutation }}) Client() *Client {
|
||||
client := &Client{config: m.config}
|
||||
client.init()
|
||||
return client
|
||||
}
|
||||
|
||||
// Tx returns an `ent.Tx` for mutations that were executed in transactions;
|
||||
// it returns an error otherwise.
|
||||
func (m {{ $mutation }}) Tx() (*Tx, error) {
|
||||
if _, ok := m.driver.(*txDriver); !ok {
|
||||
return nil, errors.New("{{ $pkg }}: mutation is not running in a transaction")
|
||||
}
|
||||
tx := &Tx{config: m.config}
|
||||
tx.init()
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
{{ with $n.HasOneFieldID }}
|
||||
// IDs queries the database and returns the entity ids that match the mutation's predicate.
|
||||
// That means, if the mutation is applied within a transaction with an isolation level such
|
||||
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
|
||||
// or updated by the mutation.
|
||||
func (m *{{ $mutation }}) IDs(ctx context.Context) ([]{{ $n.ID.Type }}, error) {
|
||||
switch {
|
||||
case m.Op().Is(OpUpdateOne | OpDeleteOne):
|
||||
id, exists := m.ID()
|
||||
if exists {
|
||||
return []{{ $n.ID.Type }}{id}, nil
|
||||
}
|
||||
fallthrough
|
||||
case m.Op().Is(OpUpdate | OpDelete):
|
||||
return m.Client().{{ $n.Name }}.Query().Where(m.Predicates()...).IDs(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.Op())
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range $f := $n.Fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ if $n.HasOneFieldID }}
|
||||
{{ $const := print $n.Package "." $f.Constant }}
|
||||
// {{ $f.MutationGetOld }} returns the old "{{ $f.Name }}" field's value of the {{ $n.Name }} entity.
|
||||
// If the {{ $n.Name }} object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *{{ $mutation }}) {{ $f.MutationGetOld }}(ctx context.Context) (v {{ if $f.NillableValue }}*{{ end }}{{ $f.Type }}, err error) {
|
||||
if !m.Op().Is(OpUpdateOne) {
|
||||
return v, errors.New("{{ $f.MutationGetOld }} is only allowed on UpdateOne operations")
|
||||
}
|
||||
if _, exists := m.ID(); !exists || m.oldValue == nil {
|
||||
return v, errors.New("{{ $f.MutationGetOld }} requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for {{ $f.MutationGetOld }}: %w", err)
|
||||
}
|
||||
return oldValue.{{ $f.StructField }}, nil
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
// OldField returns the old value of the field from the database. An error is
|
||||
// returned if the mutation operation is not UpdateOne, or the query to the
|
||||
// database failed.
|
||||
func (m *{{ $mutation }}) OldField(ctx context.Context, name string) (ent.Value, error) {
|
||||
{{- if $n.HasCompositeID }}
|
||||
return nil, errors.New("edge schema {{ $n.Name }} does not support getting old values")
|
||||
{{- else }}
|
||||
{{- with $n.Fields }}
|
||||
switch name {
|
||||
{{- range $f := . }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- $const := print $n.Package "." $f.Constant }}
|
||||
case {{ $const }}:
|
||||
return m.{{ $f.MutationGetOld }}(ctx)
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return nil, fmt.Errorf("unknown {{ $n.Name }} field %s", name)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
500
entc/gen/template/builder/query.tmpl
Normal file
500
entc/gen/template/builder/query.tmpl
Normal file
@@ -0,0 +1,500 @@
|
||||
{{/*
|
||||
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 "query" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ with extend $ "Imports" $.SiblingImports }}
|
||||
{{ template "import" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ $builder := $.QueryName }}
|
||||
{{ $receiver := $.QueryReceiver }}
|
||||
|
||||
// {{ $builder }} is the builder for querying {{ $.Name }} entities.
|
||||
type {{ $builder }} struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []{{ $.Package }}.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.{{ $.Name }}
|
||||
{{- /* Eager loading fields. */}}
|
||||
{{- range $e := $.Edges }}
|
||||
{{ $e.EagerLoadField }} *{{ $e.Type.QueryName }}
|
||||
{{- end }}
|
||||
{{- /* Additional fields to add to the builder. */}}
|
||||
{{- $tmpl := printf "dialect/%s/query/fields" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
// intermediate query (i.e. traversal path).
|
||||
{{ $.Storage }} {{ $.Storage.Builder }}
|
||||
path func(context.Context) ({{ $.Storage.Builder }}, error)
|
||||
}
|
||||
|
||||
// Where adds a new predicate for the {{ $builder }} builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) Where(ps ...predicate.{{ $.Name }}) *{{ $builder }} {
|
||||
{{ $receiver}}.predicates = append({{ $receiver }}.predicates, ps...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Limit the number of records to be returned by this query.
|
||||
func ({{ $receiver }} *{{ $builder }}) Limit(limit int) *{{ $builder }} {
|
||||
{{ $receiver }}.ctx.Limit = &limit
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Offset to start from.
|
||||
func ({{ $receiver }} *{{ $builder }}) Offset(offset int) *{{ $builder }} {
|
||||
{{ $receiver }}.ctx.Offset = &offset
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Unique configures the query builder to filter duplicate records on query.
|
||||
// By default, unique is set to true, and can be disabled using this method.
|
||||
func ({{ $receiver }} *{{ $builder }}) Unique(unique bool) *{{ $builder }} {
|
||||
{{ $receiver }}.ctx.Unique = &unique
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func ({{ $receiver }} *{{ $builder }}) Order(o ...{{ $.Package }}.OrderOption) *{{ $builder }} {
|
||||
{{ $receiver }}.order = append({{ $receiver }}.order, o...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
{{/* this code has similarity with edge queries in client.tmpl */}}
|
||||
{{ range $e := $.Edges }}
|
||||
{{ $edge_builder := print $e.Type.QueryName }}
|
||||
// Query{{ pascal $e.Name }} chains the current query on the "{{ $e.Name }}" edge.
|
||||
func ({{ $receiver }} *{{ $builder }}) Query{{ pascal $e.Name }}() *{{ $edge_builder }} {
|
||||
query := (&{{ $e.Type.ClientName }}{config: {{ $receiver }}.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU {{ $.Storage.Builder }}, err error) {
|
||||
if err := {{ $receiver }}.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- with extend $ "Receiver" $receiver "Edge" $e "Ident" "fromU" -}}
|
||||
{{ $tmpl := printf "dialect/%s/query/path" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// First returns the first {{ $.Name }} entity from the query.
|
||||
// Returns a *NotFoundError when no {{ $.Name }} was found.
|
||||
func ({{ $receiver }} *{{ $builder }}) First(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
nodes, err := {{ $receiver }}.Limit(1).All(setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryFirst))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return nil, &NotFoundError{ {{ $.Package }}.Label}
|
||||
}
|
||||
return nodes[0], nil
|
||||
}
|
||||
|
||||
// FirstX is like First, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) FirstX(ctx context.Context) *{{ $.Name }} {
|
||||
node, err := {{ $receiver }}.First(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
{{ if $.HasOneFieldID }}
|
||||
// FirstID returns the first {{ $.Name }} ID from the query.
|
||||
// Returns a *NotFoundError when no {{ $.Name }} ID was found.
|
||||
func ({{ $receiver }} *{{ $builder }}) FirstID(ctx context.Context) (id {{ $.ID.Type }}, err error) {
|
||||
var ids []{{ $.ID.Type }}
|
||||
if ids, err = {{ $receiver }}.Limit(1).IDs(setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryFirstID)); err != nil {
|
||||
return
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
err = &NotFoundError{ {{ $.Package }}.Label}
|
||||
return
|
||||
}
|
||||
return ids[0], nil
|
||||
}
|
||||
|
||||
// FirstIDX is like FirstID, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) FirstIDX(ctx context.Context) {{ $.ID.Type }} {
|
||||
id, err := {{ $receiver }}.FirstID(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Only returns a single {{ $.Name }} entity found by the query, ensuring it only returns one.
|
||||
// Returns a *NotSingularError when more than one {{ $.Name }} entity is found.
|
||||
// Returns a *NotFoundError when no {{ $.Name }} entities are found.
|
||||
func ({{ $receiver }} *{{ $builder }}) Only(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
nodes, err := {{ $receiver }}.Limit(2).All(setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryOnly))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch len(nodes) {
|
||||
case 1:
|
||||
return nodes[0], nil
|
||||
case 0:
|
||||
return nil, &NotFoundError{ {{ $.Package }}.Label}
|
||||
default:
|
||||
return nil, &NotSingularError{ {{ $.Package }}.Label}
|
||||
}
|
||||
}
|
||||
|
||||
// OnlyX is like Only, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) OnlyX(ctx context.Context) *{{ $.Name }} {
|
||||
node, err := {{ $receiver }}.Only(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
{{ if $.HasOneFieldID }}
|
||||
// OnlyID is like Only, but returns the only {{ $.Name }} ID in the query.
|
||||
// Returns a *NotSingularError when more than one {{ $.Name }} ID is found.
|
||||
// Returns a *NotFoundError when no entities are found.
|
||||
func ({{ $receiver }} *{{ $builder }}) OnlyID(ctx context.Context) (id {{ $.ID.Type }}, err error) {
|
||||
var ids []{{ $.ID.Type }}
|
||||
if ids, err = {{ $receiver }}.Limit(2).IDs(setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryOnlyID)); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(ids) {
|
||||
case 1:
|
||||
id = ids[0]
|
||||
case 0:
|
||||
err = &NotFoundError{ {{ $.Package }}.Label}
|
||||
default:
|
||||
err = &NotSingularError{ {{ $.Package }}.Label}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OnlyIDX is like OnlyID, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) OnlyIDX(ctx context.Context) {{ $.ID.Type }} {
|
||||
id, err := {{ $receiver }}.OnlyID(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// All executes the query and returns a list of {{ plural $.Name }}.
|
||||
func ({{ $receiver }} *{{ $builder }}) All(ctx context.Context) ([]*{{ $.Name }}, error) {
|
||||
ctx = setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryAll)
|
||||
if err := {{ $receiver }}.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qr := querierAll[[]*{{ $.Name }}, *{{ $builder }}]()
|
||||
return withInterceptors[[]*{{ $.Name }}](ctx, {{ $receiver }}, qr, {{ $receiver }}.inters)
|
||||
}
|
||||
|
||||
// AllX is like All, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) AllX(ctx context.Context) []*{{ $.Name }} {
|
||||
nodes, err := {{ $receiver }}.All(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
{{ if $.HasOneFieldID }}
|
||||
// IDs executes the query and returns a list of {{ $.Name }} IDs.
|
||||
func ({{ $receiver }} *{{ $builder }}) IDs(ctx context.Context) (ids []{{ $.ID.Type }}, err error) {
|
||||
{{- /* Since a graph traversal such as JOINs can return duplicate IDs, set the Unique modifier unless specified otherwise. */}}
|
||||
if {{ $receiver }}.ctx.Unique == nil && {{ $receiver }}.path != nil {
|
||||
{{ $receiver }}.Unique(true)
|
||||
}
|
||||
ctx = setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryIDs)
|
||||
{{- if and $.ID.HasValueScanner (eq $.Storage.Name "sql") }}
|
||||
var nodes []*{{ $.Name }}
|
||||
if nodes, err = {{ $receiver }}.Select({{ $.Package }}.FieldID).All(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids = make([]{{ $.ID.Type }}, len(nodes))
|
||||
for i := range nodes {
|
||||
ids[i] = nodes[i].ID
|
||||
}
|
||||
{{- else }}
|
||||
if err = {{ $receiver }}.Select({{ $.Package }}.FieldID).Scan(ctx, &ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- end }}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// IDsX is like IDs, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) IDsX(ctx context.Context) []{{ $.ID.Type }} {
|
||||
ids, err := {{ $receiver }}.IDs(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Count returns the count of the given query.
|
||||
func ({{ $receiver }} *{{ $builder }}) Count(ctx context.Context) (int, error) {
|
||||
ctx = setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryCount)
|
||||
if err := {{ $receiver }}.prepareQuery(ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return withInterceptors[int](ctx, {{ $receiver }}, querierCount[*{{ $builder }}](), {{ $receiver }}.inters)
|
||||
}
|
||||
|
||||
// CountX is like Count, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) CountX(ctx context.Context) int {
|
||||
count, err := {{ $receiver }}.Count(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Exist returns true if the query has elements in the graph.
|
||||
func ({{ $receiver }} *{{ $builder }}) Exist(ctx context.Context) (bool, error) {
|
||||
ctx = setContextOp(ctx, {{ $receiver }}.ctx, ent.OpQueryExist)
|
||||
switch _, err := {{ $receiver }}.First{{ if $.HasOneFieldID }}ID{{ end }}(ctx);{
|
||||
case IsNotFound(err):
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, fmt.Errorf("{{ $pkg }}: check existence: %w", err)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExistX is like Exist, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) ExistX(ctx context.Context) bool {
|
||||
exist, err := {{ $receiver }}.Exist(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return exist
|
||||
}
|
||||
|
||||
// Clone returns a duplicate of the {{ $builder }} builder, including all associated steps. It can be
|
||||
// used to prepare common query builders and use them differently after the clone is made.
|
||||
func ({{ $receiver }} *{{ $builder }}) Clone() *{{ $builder }} {
|
||||
if {{ $receiver }} == nil {
|
||||
return nil
|
||||
}
|
||||
return &{{ $builder }}{
|
||||
config: {{ $receiver }}.config,
|
||||
ctx: {{ $receiver }}.ctx.Clone(),
|
||||
order: append([]{{ $.Package }}.OrderOption{}, {{ $receiver }}.order...),
|
||||
inters: append([]Interceptor{}, {{ $receiver }}.inters...),
|
||||
predicates: append([]predicate.{{ $.Name }}{}, {{ $receiver }}.predicates...),
|
||||
{{- range $e := $.Edges }}
|
||||
{{ $e.EagerLoadField }}: {{ $receiver }}.{{ $e.EagerLoadField }}.Clone(),
|
||||
{{- end }}
|
||||
// clone intermediate query.
|
||||
{{ $.Storage }}: {{ $receiver }}.{{ $.Storage }}.Clone(),
|
||||
path: {{ $receiver }}.path,
|
||||
{{- if $.FeatureEnabled "sql/modifier" }}
|
||||
modifiers: append([]func(*sql.Selector){}, {{ $receiver }}.modifiers...),
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
{{- range $e := $.Edges }}
|
||||
{{ $ebuilder := $e.Type.QueryName }}
|
||||
{{ $func := print "With" $e.StructField }}
|
||||
// {{ $func }} tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "{{ $e.Name }}" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(opts ...func(*{{ $ebuilder }})) *{{ $builder }} {
|
||||
query := (&{{ $e.Type.ClientName }}{config: {{ $receiver }}.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
{{ $receiver }}.{{ $e.EagerLoadField }} = query
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ $groupBuilder := pascal $.Name | printf "%sGroupBy" }}
|
||||
|
||||
// GroupBy is used to group vertices by one or more fields/columns.
|
||||
// It is often used with aggregate functions, like: {{ join (keys aggregate) ", " }}.
|
||||
{{- with len $.Fields }}
|
||||
{{- $f := index $.Fields 0 }}
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var v []struct {
|
||||
// {{ $f.StructField }} {{ $f.Type }} `{{ $f.StructTag }}`
|
||||
// Count int `json:"count,omitempty"`
|
||||
// }
|
||||
//
|
||||
// client.{{ pascal $.Name }}.Query().
|
||||
// GroupBy({{ $.Package }}.{{ $f.Constant }}).
|
||||
// Aggregate({{ $pkg }}.Count()).
|
||||
// Scan(ctx, &v)
|
||||
//
|
||||
{{- end }}
|
||||
func ({{ $receiver }} *{{ $builder }}) GroupBy(field string, fields ...string) *{{ $groupBuilder }} {
|
||||
{{ $receiver }}.ctx.Fields = append([]string{field}, fields...)
|
||||
grbuild := &{{ $groupBuilder }}{build: {{ $receiver }}}
|
||||
grbuild.flds = &{{ $receiver }}.ctx.Fields
|
||||
grbuild.label = {{ $.Package }}.Label
|
||||
grbuild.scan = grbuild.Scan
|
||||
return grbuild
|
||||
}
|
||||
|
||||
{{ $selectBuilder := pascal $.Name | printf "%sSelect" }}
|
||||
|
||||
// Select allows the selection one or more fields/columns for the given query,
|
||||
// instead of selecting all fields in the entity.
|
||||
{{- with len $.Fields }}
|
||||
{{- $f := index $.Fields 0 }}
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var v []struct {
|
||||
// {{ $f.StructField }} {{ $f.Type }} `{{ $f.StructTag }}`
|
||||
// }
|
||||
//
|
||||
// client.{{ pascal $.Name }}.Query().
|
||||
// Select({{ $.Package }}.{{ $f.Constant }}).
|
||||
// Scan(ctx, &v)
|
||||
//
|
||||
{{- end }}
|
||||
func ({{ $receiver }} *{{ $builder }}) Select(fields ...string) *{{ $selectBuilder }} {
|
||||
{{ $receiver }}.ctx.Fields = append({{ $receiver }}.ctx.Fields, fields...)
|
||||
sbuild := &{{ $selectBuilder }}{ {{ $builder }}: {{ $receiver }} }
|
||||
sbuild.label = {{ $.Package }}.Label
|
||||
sbuild.flds, sbuild.scan = &{{ $receiver }}.ctx.Fields, sbuild.Scan
|
||||
return sbuild
|
||||
}
|
||||
|
||||
// Aggregate returns a {{ $selectBuilder }} configured with the given aggregations.
|
||||
func ({{ $receiver }} *{{ $builder }}) Aggregate(fns ...AggregateFunc) *{{ $selectBuilder }} {
|
||||
return {{ $receiver }}.Select().Aggregate(fns...)
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) prepareQuery(ctx context.Context) error {
|
||||
for _, inter := range {{ $receiver }}.inters {
|
||||
if inter == nil {
|
||||
return fmt.Errorf("{{ $pkg }}: uninitialized interceptor (forgotten import {{ $pkg }}/runtime?)")
|
||||
}
|
||||
if trv, ok := inter.(Traverser); ok {
|
||||
if err := trv.Traverse(ctx, {{ $receiver }}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
{{- /* Optional prepare checks per dialect. */}}
|
||||
{{- $tmpl = printf "dialect/%s/query/preparecheck" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- with extend $ "Receiver" $receiver "Package" $pkg }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if {{ $receiver }}.path != nil {
|
||||
prev, err := {{ $receiver }}.path(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{ $receiver }}.{{ $.Storage }} = prev
|
||||
}
|
||||
{{- if $.NumPolicy }}
|
||||
if {{ $.Package }}.Policy == nil {
|
||||
return errors.New("{{ $pkg }}: uninitialized {{ $.Package }}.Policy (forgotten import {{ $pkg }}/runtime?)")
|
||||
}
|
||||
if err := {{ $.Package }}.Policy.EvalQuery(ctx, {{ $receiver }}); err != nil {
|
||||
return err
|
||||
}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder "Package" $pkg }}
|
||||
{{ $tmpl := printf "dialect/%s/query" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{- /* Support adding query methods by global templates. In order to generate dialect-specific methods,
|
||||
prefix this template with "dialect/{{ .Storage }}". For example: "dialect/sql/query/additional/*". */}}
|
||||
{{- with $tmpls := matchTemplate "query/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/* groupby builder */}}
|
||||
|
||||
{{ $groupReceiver := $.GroupReceiver }}
|
||||
|
||||
// {{ $groupBuilder }} is the group-by builder for {{ $.Name }} entities.
|
||||
type {{ $groupBuilder }} struct {
|
||||
selector
|
||||
build *{{ $builder }}
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the group-by query.
|
||||
func ({{ $groupReceiver }} *{{ $groupBuilder }}) Aggregate(fns ...AggregateFunc) *{{ $groupBuilder }} {
|
||||
{{ $groupReceiver }}.fns = append({{ $groupReceiver }}.fns, fns...)
|
||||
return {{ $groupReceiver }}
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func ({{ $groupReceiver }} *{{ $groupBuilder }}) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, {{ $groupReceiver }}.build.ctx, ent.OpQueryGroupBy)
|
||||
if err := {{ $groupReceiver }}.build.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*{{ $builder }}, *{{ $groupBuilder }}](ctx, {{ $groupReceiver }}.build, {{ $groupReceiver }}, {{ $groupReceiver }}.build.inters, v)
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $groupReceiver "Builder" $groupBuilder }}
|
||||
{{ $tmpl := printf "dialect/%s/group" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{/* select builder */}}
|
||||
|
||||
{{ $selectReceiver := $.SelectReceiver }}
|
||||
|
||||
// {{ $selectBuilder }} is the builder for selecting fields of {{ pascal $.Name }} entities.
|
||||
type {{ $selectBuilder }} struct {
|
||||
*{{ $builder }}
|
||||
selector
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the selector query.
|
||||
func ({{ $selectReceiver }} *{{ $selectBuilder }}) Aggregate(fns ...AggregateFunc) *{{ $selectBuilder }} {
|
||||
{{ $selectReceiver }}.fns = append({{ $selectReceiver }}.fns, fns...)
|
||||
return {{ $selectReceiver }}
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func ({{ $selectReceiver }} *{{ $selectBuilder }}) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, {{ $selectReceiver }}.ctx, ent.OpQuerySelect)
|
||||
if err := {{ $selectReceiver }}.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*{{ $builder }}, *{{ $selectBuilder }}](ctx, {{ $selectReceiver }}.{{ $.QueryName }}, {{ $selectReceiver }}, {{ $selectReceiver }}.inters, v)
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $selectReceiver "Builder" $selectBuilder }}
|
||||
{{ $tmpl := printf "dialect/%s/select" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
150
entc/gen/template/builder/setter.tmpl
Normal file
150
entc/gen/template/builder/setter.tmpl
Normal file
@@ -0,0 +1,150 @@
|
||||
{{/*
|
||||
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 "setter" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $fields := $.Fields }}
|
||||
{{ $updater := false }}
|
||||
{{ $creator := true }}
|
||||
{{- if or (hasSuffix $builder "Update") (hasSuffix $builder "UpdateOne") }}
|
||||
{{ $updater = true }}
|
||||
{{ $creator = false }}
|
||||
{{ $fields = $.MutableFields }}
|
||||
{{- else if $.HasCompositeID }}
|
||||
{{- else if $.ID.UserDefined }}
|
||||
{{ $fields = append $fields $.ID }}
|
||||
{{- end }}
|
||||
|
||||
{{ range $f := $fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- /* Skip all blob fields on multi-row Update (blob key updates require UpdateOne). */}}
|
||||
{{- if and $f.IsBlob $updater (not (hasSuffix $builder "UpdateOne")) }}{{ continue }}{{ end }}
|
||||
{{ $func := print "Set" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(v {{ $f.Type }}) *{{ $builder }} {
|
||||
{{- /* setting numeric type override previous calls to Add. */}}
|
||||
{{- if and $updater $f.SupportsMutationAdd }}
|
||||
{{ $receiver }}.mutation.{{ $f.MutationReset }}()
|
||||
{{- end }}
|
||||
{{ $receiver }}.mutation.{{ $f.MutationSet }}(v)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
{{/* Avoid generating nillable setters for nillable types. */}}
|
||||
{{ $nillableC := and $creator (or $f.Optional $f.Default) }}
|
||||
{{ $nillableU := and $updater (not $f.UpdateDefault) }}
|
||||
{{ $nillableFunc := print "SetNillable" $f.StructField }}
|
||||
{{ $skipNillable := false }}{{ range $fields }}{{ if and (ne .Name $f.Name) (eq .MutationSet $nillableFunc) }}{{ $skipNillable = true }}{{ break }}{{ end }}{{ end }}
|
||||
{{ if and (not $f.Type.Nillable) (not $skipNillable) (or $nillableC $nillableU) }}
|
||||
// {{ $nillableFunc }} sets the "{{ $f.Name }}" field if the given value is not nil.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $nillableFunc }}(v *{{ $f.Type }}) *{{ $builder }} {
|
||||
if v != nil {
|
||||
{{ $receiver }}.{{ $func }}(*v)
|
||||
}
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if and $updater $f.SupportsMutationAdd }}
|
||||
// {{ $f.MutationAdd }} adds value to the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $f.MutationAdd }}(v {{ $f.SignedType }}) *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.{{ $f.MutationAdd }}(v)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if and $updater $f.SupportsMutationAppend }}
|
||||
// {{ $f.MutationAppend }} appends value to the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $f.MutationAppend }}(v {{ $f.Type }}) *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.{{ $f.MutationAppend }}(v)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if and $f.Optional $updater }}
|
||||
{{ $func := print "Clear" $f.StructField }}
|
||||
// {{ $func }} clears the value of the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}() *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.{{ $func }}()
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{- /* Lazy blob setters only on Create and UpdateOne (blob writes require a single-row context). */}}
|
||||
{{- $updateOne := hasSuffix $builder "UpdateOne" }}
|
||||
{{- if or $creator $updateOne }}
|
||||
{{- if hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $func := print "Set" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(v io.Reader) *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.Set{{ $f.StructField }}(v)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
{{- if and $f.Optional $updateOne }}
|
||||
{{ $clearFunc := print "Clear" $f.StructField }}
|
||||
// {{ $clearFunc }} clears the value of the "{{ $f.Name }}" field.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $clearFunc }}() *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.{{ $clearFunc }}()
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ range $e := $.EdgesWithID }}
|
||||
{{ if and $updater $e.Immutable }}
|
||||
{{/* Skip to the next one as immutable edges cannot be updated. */}}
|
||||
{{continue}}
|
||||
{{ end }}
|
||||
{{ $op := "add" }}{{ $idsFunc := $e.MutationAdd }}{{ if $e.Unique }}{{ $op = "set" }}{{ $idsFunc = $e.MutationSet }}{{ end }}
|
||||
{{/* Check if this setter was already defined by the field-setters (e.g. edge-field with the same name). */}}
|
||||
{{ $withSetter := not $e.HasFieldSetter }}
|
||||
{{ if $withSetter }}
|
||||
// {{ $idsFunc }} {{ $op }}s the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity by ID{{ if not $e.Unique }}s{{ end }}.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $idsFunc }}({{ if $e.Unique }}id{{ else }}ids ...{{ end }} {{ $e.Type.ID.Type }}) *{{ $builder }} {
|
||||
{{ $receiver }}.mutation.{{ $idsFunc }}({{ if $e.Unique }}id{{ else }}ids ...{{ end }})
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ if and $e.Unique $e.Optional $withSetter }}
|
||||
{{ $nillableIDsFunc := print "SetNillable" $e.StructField "ID" }}
|
||||
// {{ $nillableIDsFunc }} sets the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity by ID if the given value is not nil.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $nillableIDsFunc }}(id *{{ $e.Type.ID.Type }}) *{{ $builder }} {
|
||||
if id != nil {
|
||||
{{ $receiver}} = {{ $receiver }}.{{ $idsFunc }}(*id)
|
||||
}
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ $func := print (pascal $op) $e.StructField }}
|
||||
// {{ $func }} {{ $op }}s the "{{ $e.Name }}" edge{{if not $e.Unique}}s{{ end }} to the {{ $e.Type.Name }} entity.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(v {{ if not $e.Unique }}...{{ end }}*{{ $e.Type.Name}}) *{{ $builder }} {
|
||||
{{ if $e.Unique -}}
|
||||
return {{ $receiver }}.{{ $idsFunc }}(v.ID)
|
||||
{{- else -}}
|
||||
ids := make([]{{ $e.Type.ID.Type }}, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return {{ $receiver }}.{{ $idsFunc }}(ids...)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Mutation returns the {{ $.MutationName }} object of the builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) Mutation() *{{ $.MutationName }} {
|
||||
return {{ $receiver }}.mutation
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
277
entc/gen/template/builder/update.tmpl
Normal file
277
entc/gen/template/builder/update.tmpl
Normal file
@@ -0,0 +1,277 @@
|
||||
{{/*
|
||||
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 "update" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ with extend $ "Imports" $.SiblingImports }}
|
||||
{{ template "import" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ $builder := $.UpdateName }}
|
||||
{{ $receiver := $.UpdateReceiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
{{ $runtimeRequired := or $.NumHooks $.NumPolicy }}
|
||||
|
||||
// {{ $builder }} is the builder for updating {{ $.Name }} entities.
|
||||
type {{ $builder }} struct {
|
||||
config
|
||||
{{- template "update/fields" $ -}}
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the {{ $builder }} builder.
|
||||
func ({{ $receiver}} *{{ $builder }}) Where(ps ...predicate.{{ $.Name }}) *{{ $builder }} {
|
||||
{{ $mutation }}.Where(ps...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder }}
|
||||
{{ template "setter" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder }}
|
||||
{{ template "update/edges" . }}
|
||||
{{ end }}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func ({{ $receiver }} *{{ $builder }}) Save(ctx context.Context) (int, error) {
|
||||
{{- if $.HasUpdateDefault }}
|
||||
{{- if $runtimeRequired }}
|
||||
if err := {{ $receiver }}.defaults(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
{{- else }}
|
||||
{{ $receiver }}.defaults()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return withHooks(ctx, {{ $receiver }}.{{ $.Storage }}Save, {{ $mutation }}, {{ $receiver }}.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $builder }}) SaveX(ctx context.Context) int {
|
||||
affected, err := {{ $receiver }}.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return affected
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder "Package" $pkg }}
|
||||
{{ template "update/checks" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $builder "Package" $pkg }}
|
||||
{{ $tmpl := printf "dialect/%s/update" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{ $onebuilder := $.UpdateOneName }}
|
||||
{{ $receiver = $.UpdateOneReceiver }}
|
||||
{{ $mutation = print $receiver ".mutation" }}
|
||||
|
||||
// {{ $onebuilder }} is the builder for updating a single {{ $.Name }} entity.
|
||||
type {{ $onebuilder }} struct {
|
||||
config
|
||||
fields []string
|
||||
{{- template "update/fields" $ }}
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $onebuilder }}
|
||||
{{ template "setter" . }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $onebuilder }}
|
||||
{{ template "update/edges" . }}
|
||||
{{ end }}
|
||||
|
||||
// Where appends a list predicates to the {{ $builder }} builder.
|
||||
func ({{ $receiver }} *{{ $onebuilder }}) Where(ps ...predicate.{{ $.Name }}) *{{ $onebuilder }} {
|
||||
{{ $mutation }}.Where(ps...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||
// The default is selecting all fields defined in the entity schema.
|
||||
func ({{ $receiver }} *{{ $onebuilder }}) Select(field string, fields ...string) *{{ $onebuilder }} {
|
||||
{{ $receiver }}.fields = append([]string{field}, fields...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
// Save executes the query and returns the updated {{ $.Name }} entity.
|
||||
func ({{ $receiver }} *{{ $onebuilder }} ) Save(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
{{- if $.HasUpdateDefault }}
|
||||
{{- if $runtimeRequired }}
|
||||
if err := {{ $receiver }}.defaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- else }}
|
||||
{{ $receiver }}.defaults()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return withHooks(ctx, {{ $receiver }}.{{ $.Storage }}Save, {{ $mutation }}, {{ $receiver }}.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $onebuilder }}) SaveX(ctx context.Context) *{{ $.Name }} {
|
||||
node, err := {{ $receiver }}.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Exec executes the query on the entity.
|
||||
func ({{ $receiver }} *{{ $onebuilder }}) Exec(ctx context.Context) error {
|
||||
_, err := {{ $receiver }}.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func ({{ $receiver }} *{{ $onebuilder }}) ExecX(ctx context.Context) {
|
||||
if err := {{ $receiver }}.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $onebuilder "Package" $pkg }}
|
||||
{{ template "update/checks" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ with extend $ "Receiver" $receiver "Builder" $onebuilder "Package" $pkg }}
|
||||
{{ $tmpl := printf "dialect/%s/update" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
|
||||
{{- /* Support adding update methods by global templates. */}}
|
||||
{{- with $tmpls := matchTemplate "update/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{/* shared struct fields between the two updaters */}}
|
||||
{{ define "update/fields"}}
|
||||
hooks []Hook
|
||||
mutation *{{ $.MutationName }}
|
||||
{{- /* Additional fields to add to the builder. */}}
|
||||
{{- $tmpl := printf "dialect/%s/update/fields" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* shared edges removal between the two updaters */}}
|
||||
{{ define "update/edges" }}
|
||||
{{ $builder := pascal .Scope.Builder }}
|
||||
{{ $receiver := .Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
{{ range $e := $.EdgesWithID }}
|
||||
{{ if $e.Immutable }}
|
||||
{{/* Skip to the next one as immutable edges cannot be updated. */}}
|
||||
{{continue}}
|
||||
{{ end }}
|
||||
{{ $func := $e.MutationClear }}
|
||||
// {{ $func }} clears {{ if $e.Unique }}the "{{ $e.Name }}" edge{{ else }}all "{{ $e.Name }}" edges{{ end }} to the {{ $e.Type.Name }} entity.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}() *{{ $builder }} {
|
||||
{{ $mutation }}.{{ $func }}()
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ if not $e.Unique }}
|
||||
{{ $idsFunc := print "Remove" (singular $e.Name | pascal) "IDs" }}
|
||||
// {{ $idsFunc }} removes the "{{ $e.Name }}" edge to {{ $e.Type.Name }} entities by IDs.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $idsFunc }}(ids ...{{ $e.Type.ID.Type }}) *{{ $builder }} {
|
||||
{{ $mutation }}.{{ $idsFunc }}(ids...)
|
||||
return {{ $receiver }}
|
||||
}
|
||||
{{ $func := print "Remove" $e.StructField }}
|
||||
// {{ $func }} removes "{{ $e.Name }}" edges to {{ $e.Type.Name }} entities.
|
||||
func ({{ $receiver }} *{{ $builder }}) {{ $func }}(v ...*{{ $e.Type.Name }}) *{{ $builder }} {
|
||||
ids := make([]{{ $e.Type.ID.Type }}, len(v))
|
||||
for i := range v {
|
||||
ids[i] = v[i].ID
|
||||
}
|
||||
return {{ $receiver }}.{{ $idsFunc }}(ids...)
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* shared template for the 2 update builders */}}
|
||||
{{ define "update/checks" }}
|
||||
{{ $pkg := .Scope.Package }}
|
||||
{{ $receiver := .Scope.Receiver }}
|
||||
{{ $builder := pascal .Scope.Builder }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
{{ $runtimeRequired := or $.NumHooks $.NumPolicy }}
|
||||
|
||||
{{ if $.HasUpdateDefault }}
|
||||
// defaults sets the default values of the builder before save.
|
||||
func ({{ $receiver }} *{{ $builder }}) defaults() {{ if $runtimeRequired }}error{{ end }}{
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.UpdateDefault }}
|
||||
if _, ok := {{ $mutation }}.{{ $f.MutationGet }}(); !ok {{ if $f.Optional }} && !{{ $mutation }}.{{ $f.StructField }}Cleared() {{ end }} {
|
||||
{{- if $runtimeRequired }}
|
||||
if {{ $.Package }}.{{ $f.UpdateDefaultName }} == nil {
|
||||
return fmt.Errorf("{{ $pkg }}: uninitialized {{ $.Package }}.{{ $f.UpdateDefaultName }} (forgotten import {{ $pkg }}/runtime?)")
|
||||
}
|
||||
{{- end }}
|
||||
v := {{ $.Package }}.{{ $f.UpdateDefaultName }}()
|
||||
{{ $mutation }}.{{ $f.MutationSet }}(v)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $runtimeRequired }}
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $.HasUpdateCheckers }}
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func ({{ $receiver }} *{{ $builder }}) check() error {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- $isValidator := and ($f.HasGoType) ($f.Type.Validator) }}
|
||||
{{- with and (not $f.Immutable) (or $f.Validators $f.IsEnum $isValidator) }}
|
||||
if v, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
if err := {{ if or $f.Validators $f.IsEnum }}{{ $.Package }}.{{ $f.Validator }}({{ $f.BasicType "v" }}){{ else }}v.Validate(){{ end }}; err != nil {
|
||||
return &ValidationError{Name: "{{ $f.Name }}", err: fmt.Errorf(`{{ $pkg }}: validator failed for field "{{ $.Name }}.{{ $f.Name }}": %w`, err)}
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- if and $e.Unique (not $e.Optional) }}
|
||||
if {{ $mutation }}.{{ $e.StructField }}Cleared() && len({{ $mutation }}.{{ $e.StructField }}IDs()) > 0 {
|
||||
return errors.New(`{{ $pkg }}: clearing a required unique edge "{{ $.Name }}.{{ $e.Name }}"`)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
561
entc/gen/template/client.tmpl
Normal file
561
entc/gen/template/client.tmpl
Normal file
@@ -0,0 +1,561 @@
|
||||
{{/*
|
||||
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 "client/init" }}
|
||||
// Client is the client that holds all ent builders.
|
||||
type Client struct {
|
||||
config
|
||||
{{- if $.SupportMigrate }}
|
||||
// Schema is the client for creating, migrating and dropping schema.
|
||||
Schema *migrate.Schema
|
||||
{{- end }}
|
||||
{{- range $n := $.Nodes }}
|
||||
// {{ $n.Name }} is the client for interacting with the {{ $n.Name }} builders.
|
||||
{{ $n.Name }} *{{ $n.ClientName }}
|
||||
{{- end }}
|
||||
{{- template "client/fields/additional" $ }}
|
||||
{{- with $tmpls := matchTemplate "client/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// NewClient creates a new client configured with the given options.
|
||||
func NewClient(opts ...Option) *Client {
|
||||
client := &Client{config: newConfig(opts...)}
|
||||
client.init()
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *Client) init() {
|
||||
{{- if $.SupportMigrate }}
|
||||
c.Schema = migrate.NewSchema(c.driver)
|
||||
{{- end }}
|
||||
{{- range $n := $.Nodes }}
|
||||
c.{{ $n.Name }} = New{{ $n.ClientName }}(c.config)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ define "client" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{/* Additional dependencies injected to config. */}}
|
||||
{{ $deps := list }}{{ with $.Config.Annotations }}{{ $deps = $.Config.Annotations.Dependencies }}{{ end }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"{{ $.Config.Package }}/migrate"
|
||||
{{ range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $n.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- end }}
|
||||
{{- range $dep := $deps }}
|
||||
{{ $dep.Type.PkgName }} "{{ $dep.Type.PkgPath }}"
|
||||
{{- end }}
|
||||
"entgo.io/ent/dialect"
|
||||
{{- $hasBlobNodes := false }}{{ $blobSupported := hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}{{ if $blobSupported }}{{ range $n := $.Nodes }}{{ if $n.HasBlobFields }}{{ $hasBlobNodes = true }}{{ end }}{{ end }}{{ end }}
|
||||
{{ range $import := $.Storage.Imports -}}
|
||||
"{{ $import }}"
|
||||
{{ end -}}
|
||||
{{- template "import/additional" $ }}
|
||||
)
|
||||
|
||||
{{ template "client/init" $ }}
|
||||
|
||||
type (
|
||||
// config is the configuration for the client and its builder.
|
||||
config struct {
|
||||
// driver used for executing database requests.
|
||||
driver dialect.Driver
|
||||
// debug enable a debug logging.
|
||||
debug bool
|
||||
// log used for logging on debug mode.
|
||||
log func(...any)
|
||||
// hooks to execute on mutations.
|
||||
hooks *hooks
|
||||
// interceptors to execute on queries.
|
||||
inters *inters
|
||||
{{- if $hasBlobNodes }}
|
||||
// blobOpeners configures how blob buckets are opened for each entity type.
|
||||
blobOpeners BlobOpeners
|
||||
{{- end }}
|
||||
{{- /* Additional dependency fields. */}}
|
||||
{{- range $dep := $deps }}
|
||||
{{ $dep.Field }} {{ $dep.Type }}
|
||||
{{- end }}
|
||||
{{- /* Support adding config fields from both global or dialect-specific templates. */}}
|
||||
{{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }}
|
||||
{{- with $tmpls := matchTemplate (print $prefix "config/fields/*") }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
// Option function to configure the client.
|
||||
Option func(*config)
|
||||
)
|
||||
|
||||
// newConfig creates a new config for the client.
|
||||
func newConfig(opts ...Option) config {
|
||||
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
|
||||
{{- with $tmpls := matchTemplate "config/init/fields/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
cfg.options(opts...)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// options applies the options on the config object.
|
||||
func (c *config) options(opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
if c.debug {
|
||||
c.driver = dialect.Debug(c.driver, c.log)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug enables debug logging on the ent.Driver.
|
||||
func Debug() Option {
|
||||
return func(c *config) {
|
||||
c.debug = true
|
||||
}
|
||||
}
|
||||
|
||||
// Log sets the logging function for debug mode.
|
||||
func Log(fn func(...any)) Option {
|
||||
return func(c *config) {
|
||||
c.log = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Driver configures the client driver.
|
||||
func Driver(driver dialect.Driver) Option {
|
||||
return func(c *config) {
|
||||
c.driver = driver
|
||||
}
|
||||
}
|
||||
|
||||
{{- /* Additional dependency options reside on the config object. */}}
|
||||
{{- range $dep := $deps }}
|
||||
// {{ $dep.Option }} configures the {{ $dep.Field }}.
|
||||
func {{ $dep.Option }}(v {{ $dep.Type }}) Option {
|
||||
return func(c *config) {
|
||||
c.{{ $dep.Field }} = v
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{- if $hasBlobNodes }}
|
||||
// Blob is an alias for the [ent.Blob] interface defined in the entgo.io/ent package.
|
||||
type Blob = ent.Blob
|
||||
|
||||
// BlobOpeners configures how blob buckets are opened for each entity type.
|
||||
// Each field is a function that opens a blob bucket for the given field name.
|
||||
type BlobOpeners struct {
|
||||
{{- range $n := $.Nodes }}
|
||||
{{- if $n.HasBlobFields }}
|
||||
{{ $n.Name }} ent.BlobOpener
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// WithBlobOpeners configures the blob bucket openers.
|
||||
func WithBlobOpeners(openers BlobOpeners) Option {
|
||||
return func(c *config) {
|
||||
c.blobOpeners = openers
|
||||
}
|
||||
}
|
||||
|
||||
{{- $needsDefaultBlobKey := false }}
|
||||
{{- range $n := $.Nodes }}
|
||||
{{- range $f := $n.BlobFields }}
|
||||
{{- if not $f.HasBlobKey }}{{ $needsDefaultBlobKey = true }}{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $needsDefaultBlobKey }}
|
||||
|
||||
func defaultBlobKey(_ context.Context, data []byte) (string, error) {
|
||||
h := sha256.Sum256(data)
|
||||
return hex.EncodeToString(h[:]), nil
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
// Open opens a database/sql.DB specified by the driver name and
|
||||
// the data source name, and returns a new client attached to it.
|
||||
// Optional parameters can be added for configuring the client.
|
||||
func Open(driverName, dataSourceName string, options ...Option) (*Client, error) {
|
||||
switch driverName {
|
||||
case {{ join $.Storage.Dialects ", " }}:
|
||||
{{- $tmpl := printf "dialect/%s/client/open" $.Storage -}}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported driver: %q", driverName)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
|
||||
var ErrTxStarted = errors.New("{{ $pkg }}: cannot start a transaction within a transaction")
|
||||
|
||||
// Tx returns a new transactional client. The provided context
|
||||
// is used until the transaction is committed or rolled back.
|
||||
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
||||
if _, ok := c.driver.(*txDriver); ok {
|
||||
return nil, ErrTxStarted
|
||||
}
|
||||
tx, err := newTx(ctx, c.driver)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: starting a transaction: %w", err)
|
||||
}
|
||||
cfg := c.config
|
||||
cfg.driver = tx
|
||||
return &Tx{
|
||||
ctx: ctx,
|
||||
config: cfg,
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.Name }}: New{{ $n.ClientName }}(cfg),
|
||||
{{- end }}
|
||||
}, nil
|
||||
}
|
||||
|
||||
{{- /* If the storage driver supports TxOptions (like SQL) */}}
|
||||
{{- $tmpl = printf "dialect/%s/txoptions" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
|
||||
// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
|
||||
//
|
||||
// client.Debug().
|
||||
// {{ (index $.Nodes 0).Name }}.
|
||||
// Query().
|
||||
// Count(ctx)
|
||||
//
|
||||
func (c *Client) Debug() *Client {
|
||||
if c.debug {
|
||||
return c
|
||||
}
|
||||
cfg := c.config
|
||||
cfg.driver = dialect.Debug(c.driver, c.log)
|
||||
client := &Client{config: cfg}
|
||||
client.init()
|
||||
return client
|
||||
}
|
||||
|
||||
// Close closes the database connection and prevents new queries from starting.
|
||||
func (c *Client) Close() error {
|
||||
return c.driver.Close()
|
||||
}
|
||||
|
||||
// Use adds the mutation hooks to all the entity clients.
|
||||
// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
|
||||
func (c *Client) Use(hooks ...Hook) {
|
||||
{{- if lt (len $.Nodes) 6 }}
|
||||
{{- range $n := $.MutableNodes }}
|
||||
c.{{ $n.Name }}.Use(hooks...)
|
||||
{{- end }}
|
||||
{{- /* Use a for-loop for setting hooks in case there are too many nodes. */}}
|
||||
{{- else }}
|
||||
{{- $hooks := slist }}
|
||||
{{- range $n := $.MutableNodes }}{{ $hooks = append $hooks (printf "c.%s," $n.Name) }}{{ end }}
|
||||
for _, n := range []interface{ Use(...Hook) }{
|
||||
{{ joinWords $hooks 80 }}
|
||||
}{
|
||||
n.Use(hooks...)
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// Intercept adds the query interceptors to all the entity clients.
|
||||
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
|
||||
func (c *Client) Intercept(interceptors ...Interceptor) {
|
||||
{{- if lt (len $.Nodes) 6 }}
|
||||
{{- range $n := $.Nodes }}
|
||||
c.{{ $n.Name }}.Intercept(interceptors...)
|
||||
{{- end }}
|
||||
{{- /* Use a for-loop for setting interceptors in case there are too many nodes. */}}
|
||||
{{- else }}
|
||||
{{- $inters := slist }}
|
||||
{{- range $n := $.Nodes }}{{ $inters = append $inters (printf "c.%s," $n.Name) }}{{ end }}
|
||||
for _, n := range []interface{ Intercept(...Interceptor) }{
|
||||
{{ joinWords $inters 80 }}
|
||||
}{
|
||||
n.Intercept(interceptors...)
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{- with $tmpls := matchTemplate "client/additional/*" "client/additional/*/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
// Mutate implements the ent.Mutator interface.
|
||||
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
|
||||
switch m := m.(type) {
|
||||
{{- range $n := $.MutableNodes }}
|
||||
case *{{ $n.MutationName }}:
|
||||
return c.{{ $n.Name }}.mutate(ctx, m)
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, fmt.Errorf("{{ $pkg }}: unknown mutation type %T", m)
|
||||
}
|
||||
}
|
||||
|
||||
{{ range $n := $.Nodes }}
|
||||
{{ $client := $n.ClientName }}
|
||||
// {{ $client }} is a client for the {{ $n.Name }} schema.
|
||||
type {{ $client }} struct {
|
||||
config
|
||||
}
|
||||
|
||||
{{ $rec := $n.Receiver }}{{ if eq $rec "c" }}{{ $rec = printf "%.2s" $n.Name | lower }}{{ end }}
|
||||
|
||||
// New{{ $client }} returns a client for the {{ $n.Name }} from the given config.
|
||||
func New{{ $client }}(c config) *{{ $client }} {
|
||||
return &{{ $client }}{config: c}
|
||||
}
|
||||
|
||||
{{- if not $n.IsView }}
|
||||
// Use adds a list of mutation hooks to the hooks stack.
|
||||
// A call to `Use(f, g, h)` equals to `{{ $n.Package }}.Hooks(f(g(h())))`.
|
||||
func (c *{{ $client }}) Use(hooks ...Hook) {
|
||||
c.hooks.{{ $n.Name }} = append(c.hooks.{{ $n.Name }}, hooks...)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||
// A call to `Intercept(f, g, h)` equals to `{{ $n.Package }}.Intercept(f(g(h())))`.
|
||||
func (c *{{ $client }}) Intercept(interceptors ...Interceptor) {
|
||||
c.inters.{{ $n.Name }} = append(c.inters.{{ $n.Name }}, interceptors...)
|
||||
}
|
||||
|
||||
{{- if not $n.IsView }}
|
||||
// Create returns a builder for creating a {{ $n.Name }} entity.
|
||||
func (c *{{ $client }}) Create() *{{ $n.CreateName }} {
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpCreate)
|
||||
return &{{ $n.CreateName }}{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// CreateBulk returns a builder for creating a bulk of {{ $n.Name }} entities.
|
||||
func (c *{{ $client }}) CreateBulk(builders ...*{{ $n.CreateName }}) *{{ $n.CreateBulkName }} {
|
||||
return &{{ $n.CreateBulkName }}{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *{{ $client }}) MapCreateBulk(slice any, setFunc func(*{{ $n.CreateName }}, int)) *{{ $n.CreateBulkName }} {
|
||||
{{- /* This check should be replaced with type-parameter method, if it will be supported by Go. */}}
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &{{ $n.CreateBulkName }}{err: fmt.Errorf("calling to {{ $client }}.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*{{ $n.CreateName }}, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &{{ $n.CreateBulkName }}{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for {{ $n.Name }}.
|
||||
func (c *{{ $client }}) Update() *{{ $n.UpdateName }} {
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpUpdate)
|
||||
return &{{ $n.UpdateName }}{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// UpdateOne returns an update builder for the given entity.
|
||||
func (c *{{ $client }}) UpdateOne({{ $rec }} *{{ $n.Name }}) *{{ $n.UpdateOneName }} {
|
||||
{{- if $n.HasOneFieldID }}
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne, {{ print "with" $n.Name }}({{ $rec }}))
|
||||
{{- else }}
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne)
|
||||
{{- range $id := $n.EdgeSchema.ID }}
|
||||
mutation.{{ $id.MutationSet }}({{ $rec }}.{{ $id.StructField }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return &{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
{{ with $n.HasOneFieldID }}
|
||||
// UpdateOneID returns an update builder for the given id.
|
||||
func (c *{{ $client }}) UpdateOneID(id {{ $n.ID.Type }}) *{{ $n.UpdateOneName }} {
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne, {{ print "with" $n.Name "ID" }}(id))
|
||||
return &{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Delete returns a delete builder for {{ $n.Name }}.
|
||||
func (c *{{ $client }}) Delete() *{{ $n.DeleteName }} {
|
||||
mutation := new{{ $n.MutationName }}(c.config, OpDelete)
|
||||
return &{{ $n.DeleteName }}{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
{{ with $n.HasOneFieldID }}
|
||||
// DeleteOne returns a builder for deleting the given entity.
|
||||
func (c *{{ $client }}) DeleteOne({{ $rec }} *{{ $n.Name }}) *{{ $n.DeleteOneName }} {
|
||||
return c.DeleteOneID({{ $rec }}.ID)
|
||||
}
|
||||
|
||||
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||
func (c *{{ $client }}) DeleteOneID(id {{ $n.ID.Type }}) *{{ $n.DeleteOneName }} {
|
||||
{{- $builder := "builder" }}{{ if eq $n.Package $builder }}{{ $builder = "builderC" }}{{ end }}
|
||||
{{ $builder }} := c.Delete().Where({{ $n.Package }}.ID(id))
|
||||
{{ $builder }}.mutation.{{ $n.ID.BuilderField }} = &id
|
||||
{{ $builder }}.mutation.SetOp(OpDeleteOne)
|
||||
return &{{ $n.DeleteOneName }}{ {{ $builder }} }
|
||||
}
|
||||
{{ end }}
|
||||
{{- end }} {{/* End of if not IsView. */}}
|
||||
|
||||
// Query returns a query builder for {{ $n.Name }}.
|
||||
func (c *{{ $client }}) Query() *{{ $n.QueryName }} {
|
||||
return &{{ $n.QueryName }}{
|
||||
config: c.config,
|
||||
ctx: &QueryContext{Type: {{ $n.TypeName }} },
|
||||
inters: c.Interceptors(),
|
||||
{{- with $tmpls := matchTemplate (printf "dialect/%s/query/fields/init/*" $.Storage) }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $n }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
}
|
||||
|
||||
{{ with $n.HasOneFieldID }}
|
||||
// Get returns a {{ $n.Name }} entity by its id.
|
||||
func (c *{{ $client }}) Get(ctx context.Context, id {{ $n.ID.Type }}) (*{{ $n.Name }}, error) {
|
||||
return c.Query().Where({{ $n.Package }}.ID(id)).Only(ctx)
|
||||
}
|
||||
|
||||
// GetX is like Get, but panics if an error occurs.
|
||||
func (c *{{ $client }}) GetX(ctx context.Context, id {{ $n.ID.Type }}) *{{ $n.Name }} {
|
||||
obj, err := c.Get(ctx, id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ range $e := $n.Edges }}
|
||||
{{ $builder := $e.Type.QueryName }}
|
||||
{{ $arg := $rec }}{{ if eq $arg "id" }}{{ $arg = "node" }}{{ end }}
|
||||
{{ $func := print "Query" (pascal $e.Name) }}
|
||||
// Query{{ pascal $e.Name }} queries the {{ $e.Name }} edge of a {{ $n.Name }}.
|
||||
func (c *{{ $client }}) {{ $func }}({{ $arg }} *{{ $n.Name }}) *{{ $builder }} {
|
||||
{{- if $n.HasOneFieldID }}
|
||||
query := (&{{ $e.Type.ClientName }}{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV {{ $.Storage.Builder }}, _ error) {
|
||||
{{- with extend $n "Receiver" $arg "Edge" $e "Ident" "fromV" }}
|
||||
{{ $tmpl := printf "dialect/%s/query/from" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
{{- end -}}
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
{{- else }}
|
||||
{{- /* For edge schema, we use the predicate-based approach. */}}
|
||||
return c.Query().
|
||||
Where({{ range $id := $n.EdgeSchema.ID }}{{ $n.Package }}.{{ $id.StructField }}({{ $arg }}.{{ $id.StructField }}),{{ end }}).
|
||||
{{ $func }}()
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{- if not $n.IsView }}
|
||||
// Hooks returns the client hooks.
|
||||
func (c *{{ $client }}) Hooks() []Hook {
|
||||
{{- if or $n.NumHooks $n.NumPolicy }}
|
||||
hooks := c.hooks.{{ $n.Name }}
|
||||
return append(hooks[:len(hooks):len(hooks)], {{ $n.Package }}.Hooks[:]...)
|
||||
{{- else }}
|
||||
return c.hooks.{{ $n.Name }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// Interceptors returns the client interceptors.
|
||||
func (c *{{ $client }}) Interceptors() []Interceptor {
|
||||
{{- if $n.NumInterceptors }}
|
||||
inters := c.inters.{{ $n.Name }}
|
||||
return append(inters[:len(inters):len(inters)], {{ $n.Package }}.Interceptors[:]...)
|
||||
{{- else }}
|
||||
return c.inters.{{ $n.Name }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ if not $n.IsView }}
|
||||
func (c *{{ $client }}) mutate(ctx context.Context, m *{{ $n.MutationName }}) (Value, error) {
|
||||
switch m.Op() {
|
||||
case OpCreate:
|
||||
return (&{{ $n.CreateName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdate:
|
||||
return (&{{ $n.UpdateName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdateOne:
|
||||
return (&{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpDelete, OpDeleteOne:
|
||||
return (&{{ $n.DeleteName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("{{ $pkg }}: unknown {{ $n.Name }} mutation op: %q", m.Op())
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
|
||||
// hooks and interceptors per client, for fast access.
|
||||
type (
|
||||
{{- $leni := len $.Nodes | add -1 }}
|
||||
{{- $hooks := slist }}
|
||||
{{- $inters := slist }}
|
||||
{{- range $i, $n := $.Nodes }}
|
||||
{{- if eq $i $leni }}
|
||||
{{ if not $n.IsView }}{{- $hooks = append $hooks (printf "%s []ent.Hook" $n.Name) }}{{ end }}
|
||||
{{- $inters = append $inters (printf "%s []ent.Interceptor" $n.Name) }}
|
||||
{{- else }}
|
||||
{{ if not $n.IsView }}{{- $hooks = append $hooks (print $n.Name ",") }}{{ end }}
|
||||
{{- $inters = append $inters (print $n.Name ",") }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
hooks struct {
|
||||
{{ joinWords $hooks 80 }}
|
||||
}
|
||||
inters struct {
|
||||
{{ joinWords $inters 80 }}
|
||||
}
|
||||
)
|
||||
|
||||
{{- /* Support adding config options from both global or dialect-specific templates. */}}
|
||||
{{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }}
|
||||
{{- with $tmpls := matchTemplate (print $prefix "config/options/*") }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $tmpls := matchTemplate "config/additional/*" "config/additional/*/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* A template that can be overridden in order to add additional fields to the client.*/}}
|
||||
{{ define "client/fields/additional" }}{{ end }}
|
||||
57
entc/gen/template/dialect/gremlin/by.tmpl
Normal file
57
entc/gen/template/dialect/gremlin/by.tmpl
Normal file
@@ -0,0 +1,57 @@
|
||||
{{/*
|
||||
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/gremlin/order/signature" -}}
|
||||
// OrderFunc applies an ordering on the graph traversal.
|
||||
type OrderFunc func(*dsl.Traversal)
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/order/func" -}}
|
||||
{{- $order := $.Scope.Order -}}
|
||||
func(tr *dsl.Traversal) {
|
||||
for _, f := range fields {
|
||||
tr.By(f, dsl.{{ pascal $order }})
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{/* custom signature for group-by function */}}
|
||||
{{ define "dialect/gremlin/group/signature" -}}
|
||||
// It gets two labels as parameters. The first used in the `As` step for the predicate,
|
||||
// and the second is an optional name for the next predicates (or for later usage).
|
||||
type AggregateFunc func(string, string) (string, *dsl.Traversal)
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/group/as" -}}
|
||||
func(start, _ string) (string, *dsl.Traversal) {
|
||||
return fn(start, end)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/group/func" -}}
|
||||
{{- $fn := $.Scope.Func -}}
|
||||
{{- $withField := $.Scope.WithField -}}
|
||||
func(start, end string) (string, *dsl.Traversal) {
|
||||
if end == "" {
|
||||
end = Default{{ $fn }}Label
|
||||
}
|
||||
return end, __.As(start).{{ if $withField }}Unfold().Values(field).{{ $fn }}(){{ else }}{{ $fn }}(dsl.Local){{ end }}.As(end)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{/* optional constants for group-by default values. */}}
|
||||
{{ define "dialect/gremlin/group/const" -}}
|
||||
{{- $fn := $.Scope.Func }}
|
||||
{{- $name := $.Scope.Name }}
|
||||
{{- $pkg := base $.Config.Package }}
|
||||
// Default{{ $fn }}Label is the default label name for the {{ $fn }} aggregation function.
|
||||
// It should be used as the struct-tag for decoding, or a map key for interaction with the returned response.
|
||||
// In order to {{ quote $name }} 2 or more fields and avoid conflicting, use the `{{ $pkg }}.As({{ $pkg }}.{{ $fn }}(field), "custom_name")`
|
||||
// function with custom name in order to override it.
|
||||
const Default{{ $fn }}Label = {{ quote $name }}
|
||||
{{- end }}
|
||||
95
entc/gen/template/dialect/gremlin/create.tmpl
Normal file
95
entc/gen/template/dialect/gremlin/create.tmpl
Normal file
@@ -0,0 +1,95 @@
|
||||
{{/*
|
||||
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/gremlin/create" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinSave(ctx context.Context) (*{{ $.Name }}, error) {
|
||||
if err := {{ $receiver }}.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &gremlin.Response{}
|
||||
query, bindings := {{ $receiver }}.gremlin().Query()
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err, ok := isConstantError(res); ok {
|
||||
return nil, err
|
||||
}
|
||||
rnode := &{{ $.Name }}{config: {{ $receiver }}.config}
|
||||
if err := rnode.FromResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
{{- if $.HasOneFieldID }}
|
||||
{{ $mutation }}.{{ $.ID.BuilderField }} = &rnode.{{ $.ID.StructField }}
|
||||
{{ $mutation }}.done = true
|
||||
{{- end }}
|
||||
return rnode, nil
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlin() *dsl.Traversal {
|
||||
{{- with .NumConstraint }}
|
||||
type constraint struct {
|
||||
pred *dsl.Traversal // constraint predicate.
|
||||
test *dsl.Traversal // test matches and its constant.
|
||||
}
|
||||
constraints := make([]*constraint, 0, {{ . }})
|
||||
{{- end }}
|
||||
v := g.AddV({{ $.Package }}.Label)
|
||||
{{- if $.ID.UserDefined }}
|
||||
if id, ok := {{ $mutation }}.{{ $.ID.MutationGet }}(); ok {
|
||||
v.Property(dsl.ID, id)
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.Unique }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.V().Has({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, value).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, value)),
|
||||
})
|
||||
{{- end }}
|
||||
v.Property(dsl.Single, {{ $.Package }}.{{ $f.Constant }}, value)
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- $direction := "In" }}
|
||||
{{- $name := printf "%s.%s" $.Package $e.LabelConstant }}
|
||||
for _, id := range {{ $mutation }}.{{ $e.StructField }}IDs() {
|
||||
{{- if $e.IsInverse }}
|
||||
{{- $direction = "Out" }}
|
||||
{{- $name = printf "%s.%s" $e.Type.Package $e.LabelConstant }}
|
||||
v.AddE({{ $name }}).From(g.V(id)).InV()
|
||||
{{- else }}
|
||||
v.AddE({{ $name }}).To(g.V(id)).OutV()
|
||||
{{- end }}
|
||||
{{- if $e.HasConstraint }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.E().HasLabel({{ $name }}).{{ $direction }}V().HasID(id).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueEdge({{ $.Package }}.Label, {{ $name }}, id)),
|
||||
})
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- with .NumConstraint }}
|
||||
if len(constraints) == 0 {
|
||||
return v.ValueMap(true)
|
||||
}
|
||||
tr := constraints[0].pred.Coalesce(constraints[0].test, v.ValueMap(true))
|
||||
for _, cr := range constraints[1:] {
|
||||
tr = cr.pred.Coalesce(cr.test, tr)
|
||||
}
|
||||
return tr
|
||||
{{- else }}
|
||||
return v.ValueMap(true)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
87
entc/gen/template/dialect/gremlin/decode.tmpl
Normal file
87
entc/gen/template/dialect/gremlin/decode.tmpl
Normal file
@@ -0,0 +1,87 @@
|
||||
{{/*
|
||||
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/gremlin/decode/one" }}
|
||||
{{ $receiver := $.Receiver }}
|
||||
|
||||
// FromResponse scans the gremlin response data into {{ $.Name }}.
|
||||
func ({{ $receiver }} *{{ $.Name }}) FromResponse(res *gremlin.Response) error {
|
||||
vmap, err := res.ReadValueMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{- $scan := print "scan" $receiver }}
|
||||
var {{ $scan }} struct {
|
||||
ID {{ $.ID.Type }} `json:"id,omitempty"`
|
||||
{{ range $f := $.Fields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- $f.StructField }} {{ if and $f.IsTime (not $f.HasGoType) }}int64{{ else }}{{ if $f.NillableValue }}*{{ end }}{{ $f.Type }}{{ end }} `json:"{{ $f.StorageKey }},omitempty"`
|
||||
{{ end }}
|
||||
}
|
||||
if err := vmap.Decode(&{{ $scan }}); err != nil {
|
||||
return err
|
||||
}
|
||||
{{ $receiver }}.ID = {{ $scan }}.ID
|
||||
{{- range $i, $f := $.Fields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- if and $f.IsTime (not $f.HasGoType) }}
|
||||
{{- if $f.Nillable }}
|
||||
v{{ $i }} := time.Unix(0, {{ $scan }}.{{ $f.StructField }})
|
||||
{{ $receiver }}.{{ $f.StructField }} = &v{{ $i }}
|
||||
{{- else }}
|
||||
{{ $receiver }}.{{ $f.StructField }} = time.Unix(0, {{ $scan }}.{{ $f.StructField }})
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{ $receiver }}.{{ $f.StructField }} = {{ $scan }}.{{ $f.StructField }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ define "dialect/gremlin/decode/many" }}
|
||||
{{ $receiver := $.Receiver }}
|
||||
{{ $slice := $.Scope.Slice }}
|
||||
|
||||
// FromResponse scans the gremlin response data into {{ $slice }}.
|
||||
func ({{ $receiver }} *{{ $slice }}) FromResponse(res *gremlin.Response) error {
|
||||
vmap, err := res.ReadValueMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{{- $scan := print "scan" $receiver }}
|
||||
var {{ $scan }} []struct {
|
||||
ID {{ $.ID.Type }} `json:"id,omitempty"`
|
||||
{{ range $f := $.Fields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- $f.StructField }} {{ if and $f.IsTime (not $f.HasGoType) }}int64{{ else }}{{ if $f.NillableValue }}*{{ end }}{{ $f.Type }}{{ end }} `json:"{{ $f.StorageKey }},omitempty"`
|
||||
{{ end }}
|
||||
}
|
||||
if err := vmap.Decode(&{{ $scan }}); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range {{ $scan }} {
|
||||
node := &{{ $.Name }}{ID: v.ID}
|
||||
{{- range $i, $f := $.Fields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- if and $f.IsTime (not $f.HasGoType) }}
|
||||
{{- if $f.Nillable }}
|
||||
v{{ $i }} := time.Unix(0, v.{{ $f.StructField }})
|
||||
node.{{ $f.StructField }} = &v{{ $i }}
|
||||
{{- else }}
|
||||
node.{{ $f.StructField }} = time.Unix(0, v.{{ $f.StructField }})
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
node.{{ $f.StructField }} = v.{{ $f.StructField }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
*{{ $receiver }} = append(*{{ $receiver }}, node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
{{ end }}
|
||||
32
entc/gen/template/dialect/gremlin/delete.tmpl
Normal file
32
entc/gen/template/dialect/gremlin/delete.tmpl
Normal file
@@ -0,0 +1,32 @@
|
||||
{{/*
|
||||
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/gremlin/delete" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
{{ $mutation := print $receiver ".mutation" }}
|
||||
|
||||
func ({{ $receiver}} *{{ $builder }}) gremlinExec(ctx context.Context) (int, error) {
|
||||
res := &gremlin.Response{}
|
||||
query, bindings := {{ $receiver }}.gremlin().Query()
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
{{ $mutation }}.done = true
|
||||
return res.ReadInt()
|
||||
}
|
||||
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlin() *dsl.Traversal {
|
||||
t := g.V().HasLabel({{ $.Package }}.Label)
|
||||
for _, p := range {{ $mutation }}.Predicates() {
|
||||
p(t)
|
||||
}
|
||||
return t.SideEffect(__.Drop()).Count()
|
||||
}
|
||||
{{ end }}
|
||||
54
entc/gen/template/dialect/gremlin/errors.tmpl
Normal file
54
entc/gen/template/dialect/gremlin/errors.tmpl
Normal file
@@ -0,0 +1,54 @@
|
||||
{{/*
|
||||
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 from sql dialects */}}
|
||||
{{ define "dialect/gremlin/errors" }}
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
// Code implements the dsl.Node interface.
|
||||
func (e ConstraintError) Code() (string, []any) {
|
||||
return strconv.Quote(e.prefix() + e.msg), nil
|
||||
}
|
||||
|
||||
func (e *ConstraintError) UnmarshalGraphson(b []byte) error {
|
||||
var v [1]*string
|
||||
if err := graphson.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
if v[0] == nil {
|
||||
return fmt.Errorf("{{ $pkg }}: missing string value")
|
||||
}
|
||||
if !strings.HasPrefix(*v[0], e.prefix()) {
|
||||
return fmt.Errorf("{{ $pkg }}: invalid string for error: %s", *v[0])
|
||||
}
|
||||
e.msg = strings.TrimPrefix(*v[0], e.prefix())
|
||||
return nil
|
||||
}
|
||||
|
||||
// prefix returns the prefix used for gremlin constants.
|
||||
func (ConstraintError) prefix() string { return "Error: " }
|
||||
|
||||
// NewErrUniqueField creates a constraint error for unique fields.
|
||||
func NewErrUniqueField(label, field string, v any) *ConstraintError {
|
||||
return &ConstraintError{msg: fmt.Sprintf("field %s.%s with value: %#v", label, field, v)}
|
||||
}
|
||||
|
||||
// NewErrUniqueEdge creates a constraint error for unique edges.
|
||||
func NewErrUniqueEdge(label, edge, id string) *ConstraintError {
|
||||
return &ConstraintError{msg: fmt.Sprintf("edge %s.%s with id: %#v", label, edge, id)}
|
||||
}
|
||||
|
||||
// isConstantError indicates if the given response holds a gremlin constant containing an error.
|
||||
func isConstantError(r *gremlin.Response) (*ConstraintError, bool) {
|
||||
e := &ConstraintError{}
|
||||
if err := graphson.Unmarshal(r.Result.Data, e); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return e, true
|
||||
}
|
||||
{{ end }}
|
||||
14
entc/gen/template/dialect/gremlin/globals.tmpl
Normal file
14
entc/gen/template/dialect/gremlin/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 gremlin dialects */}}
|
||||
{{ define "dialect/gremlin/globals" }}
|
||||
{{/* Align API with SQL driver. */}}
|
||||
// queryHook describes an internal hook for the different gremlinAll methods.
|
||||
type queryHook func(context.Context)
|
||||
{{ end }}
|
||||
46
entc/gen/template/dialect/gremlin/group.tmpl
Normal file
46
entc/gen/template/dialect/gremlin/group.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.typeScope */}}
|
||||
|
||||
{{ define "dialect/gremlin/group" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinScan(ctx context.Context, root *{{ $.QueryName }}, v any) error {
|
||||
var (
|
||||
trs []any
|
||||
names []any
|
||||
)
|
||||
for _, fn := range {{ $receiver }}.fns {
|
||||
name, tr := fn("p", "")
|
||||
trs = append(trs, tr)
|
||||
names = append(names, name)
|
||||
}
|
||||
for _, f := range *{{ $receiver }}.flds {
|
||||
names = append(names, f)
|
||||
trs = append(trs, __.As("p").Unfold().Values(f).As(f))
|
||||
}
|
||||
query, bindings := root.gremlinQuery(ctx).Group().
|
||||
By(__.Values(*{{ $receiver }}.flds...).Fold()).
|
||||
By(__.Fold().Match(trs...).Select(names...)).
|
||||
Select(dsl.Values).
|
||||
Next().
|
||||
Query()
|
||||
res := &gremlin.Response{}
|
||||
if err := {{ $receiver }}.build.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*{{ $receiver }}.flds)+len({{ $receiver }}.fns) == 1 {
|
||||
return res.ReadVal(v)
|
||||
}
|
||||
vm, err := res.ReadValueMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return vm.Decode(v)
|
||||
}
|
||||
{{ end }}
|
||||
19
entc/gen/template/dialect/gremlin/meta.tmpl
Normal file
19
entc/gen/template/dialect/gremlin/meta.tmpl
Normal file
@@ -0,0 +1,19 @@
|
||||
{{/*
|
||||
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 gremlin dialects. */}}
|
||||
{{ define "dialect/gremlin/meta/constants" }}
|
||||
{{ range $e := $.Edges }}{{ $label := $e.LabelConstant -}}
|
||||
{{ if $e.IsInverse }}{{- $label = $e.InverseLabelConstant -}}
|
||||
// {{ $label }} holds the string label denoting the {{ lower $e.Name }} inverse edge type in the database.
|
||||
{{ else -}}
|
||||
// {{ $label }} holds the string label denoting the {{ lower $e.Name }} edge type in the database.
|
||||
{{ end -}}
|
||||
{{ $label }} = "{{ $e.Label }}"
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
24
entc/gen/template/dialect/gremlin/open.tmpl
Normal file
24
entc/gen/template/dialect/gremlin/open.tmpl
Normal file
@@ -0,0 +1,24 @@
|
||||
{{/*
|
||||
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/gremlin/client/open" }}
|
||||
u, err := url.Parse(dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := gremlin.NewClient(gremlin.Config{
|
||||
Endpoint: gremlin.Endpoint{
|
||||
URL: u,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
drv := gremlin.NewDriver(c)
|
||||
return NewClient(append(options, Driver(drv))...), nil
|
||||
{{ end }}
|
||||
135
entc/gen/template/dialect/gremlin/predicate.tmpl
Normal file
135
entc/gen/template/dialect/gremlin/predicate.tmpl
Normal file
@@ -0,0 +1,135 @@
|
||||
{{/*
|
||||
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/gremlin/predicate/id" -}}
|
||||
func(t *dsl.Traversal) {
|
||||
t.HasID(id)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/id/ops" -}}
|
||||
{{- $op := $.Scope.Op -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
{{- $storage := $.Scope.Storage -}}
|
||||
func(t *dsl.Traversal) {
|
||||
{{- if $op.Variadic }}
|
||||
v := make([]any, len({{ $arg }}))
|
||||
for i := range v {
|
||||
v[i] = {{ $arg }}[i]
|
||||
}
|
||||
{{- end }}
|
||||
t.HasID(p.{{ call $storage.OpCode $op }}({{ if $op.Variadic }}v...{{ else }}{{ $arg }}{{ end }}))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/field" -}}
|
||||
{{- $f := $.Scope.Field -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
func(t *dsl.Traversal) {
|
||||
t.Has(Label, {{ $f.Constant }}, p.EQ({{ $arg }}))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/field/ops" -}}
|
||||
{{- $f := $.Scope.Field -}}
|
||||
{{- $op := $.Scope.Op -}}
|
||||
{{- $arg := $.Scope.Arg -}}
|
||||
{{- $storage := $.Scope.Storage -}}
|
||||
func(t *dsl.Traversal) {
|
||||
{{- if not $op.Niladic }}
|
||||
t.Has(Label, {{ $f.Constant }}, p.{{ call $storage.OpCode $op }}({{ $arg }}{{ if $op.Variadic }}...{{ end }}))
|
||||
{{- else }}
|
||||
t.HasLabel(Label).{{ call $storage.OpCode $op }}({{ $f.Constant }})
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/edge/has" -}}
|
||||
{{- $e := $.Scope.Edge -}}
|
||||
{{- $label := $e.LabelConstant -}}
|
||||
{{- $direction := "Out" -}}
|
||||
{{- if $e.IsInverse -}}
|
||||
{{- $direction = "In" -}}
|
||||
{{/* avoid circular dependencies */}}
|
||||
{{- $label = $e.InverseLabelConstant -}}
|
||||
{{- end -}}
|
||||
func(t *dsl.Traversal) {
|
||||
{{- /* if it's an edge with self-reference, take the two vertices */}}
|
||||
{{- if $e.Bidi }}
|
||||
t.Both({{ $label }})
|
||||
{{- else }}
|
||||
t.{{ $direction }}E({{ $label }}).{{ $direction }}V()
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/edge/haswith" -}}
|
||||
{{- $e := $.Scope.Edge -}}
|
||||
{{- $label := $e.LabelConstant -}}
|
||||
{{- $direction := "Out" -}}
|
||||
{{- $inverse_direction := "In" -}}
|
||||
{{- if $e.IsInverse -}}
|
||||
{{- $direction = "In" -}}
|
||||
{{- $inverse_direction = "Out" -}}
|
||||
{{/* avoid circular dependencies */}}
|
||||
{{- $label = $e.InverseLabelConstant -}}
|
||||
{{- end -}}
|
||||
func(t *dsl.Traversal) {
|
||||
{{- if $e.Bidi }}{{/* selfref means it should be true in one of the directions */}}
|
||||
in, out := __.InV(), __.OutV()
|
||||
for _, p := range preds {
|
||||
p(in)
|
||||
p(out)
|
||||
}
|
||||
t.Where(
|
||||
__.Or(
|
||||
__.OutE({{ $label }}).Where(in),
|
||||
__.InE({{ $label }}).Where(out),
|
||||
),
|
||||
)
|
||||
{{- else }}
|
||||
tr := __.{{ $inverse_direction }}V()
|
||||
for _, p := range preds {
|
||||
p(tr)
|
||||
}
|
||||
t.{{ $direction }}E({{ $label }}).Where(tr).{{ $direction }}V()
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/and" -}}
|
||||
func(tr *dsl.Traversal) {
|
||||
trs := make([]any, 0, len(predicates))
|
||||
for _, p := range predicates {
|
||||
t := __.New()
|
||||
p(t)
|
||||
trs = append(trs, t)
|
||||
}
|
||||
tr.Where(__.And(trs...))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/or" -}}
|
||||
func(tr *dsl.Traversal) {
|
||||
trs := make([]any, 0, len(predicates))
|
||||
for _, p := range predicates {
|
||||
t := __.New()
|
||||
p(t)
|
||||
trs = append(trs, t)
|
||||
}
|
||||
tr.Where(__.Or(trs...))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
{{ define "dialect/gremlin/predicate/not" -}}
|
||||
func(tr *dsl.Traversal) {
|
||||
t := __.New()
|
||||
p(t)
|
||||
tr.Where(__.Not(t))
|
||||
}
|
||||
{{- end }}
|
||||
107
entc/gen/template/dialect/gremlin/query.tmpl
Normal file
107
entc/gen/template/dialect/gremlin/query.tmpl
Normal file
@@ -0,0 +1,107 @@
|
||||
{{/*
|
||||
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/gremlin/query" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinAll(ctx context.Context, hooks ...queryHook) ([]*{{ $.Name }}, error) {
|
||||
res := &gremlin.Response{}
|
||||
traversal := {{ $receiver }}.gremlinQuery(ctx)
|
||||
if len({{ $receiver }}.ctx.Fields) > 0 {
|
||||
fields := make([]any, len({{ $receiver }}.ctx.Fields))
|
||||
for i, f := range {{ $receiver }}.ctx.Fields {
|
||||
fields[i] = f
|
||||
}
|
||||
traversal.ValueMap(fields...)
|
||||
} else {
|
||||
traversal.ValueMap(true)
|
||||
}
|
||||
query, bindings := traversal.Query()
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var {{ plural $.Receiver }} {{ plural $.Name }}
|
||||
if err := {{ plural $.Receiver }}.FromResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range {{ plural $.Receiver }} {
|
||||
{{ plural $.Receiver }}[i].config = {{ $receiver }}.config
|
||||
}
|
||||
return {{ plural $.Receiver }}, nil
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinCount(ctx context.Context) (int, error) {
|
||||
res := &gremlin.Response{}
|
||||
query, bindings := {{ $receiver }}.gremlinQuery(ctx).Count().Query()
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.ReadInt()
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinQuery(context.Context) *dsl.Traversal {
|
||||
v := g.V().HasLabel({{ $.Package }}.Label)
|
||||
if {{ $receiver }}.gremlin != nil {
|
||||
v = {{ $receiver }}.gremlin.Clone()
|
||||
}
|
||||
for _, p := range {{ $receiver }}.predicates {
|
||||
p(v)
|
||||
}
|
||||
if len({{ $receiver }}.order) > 0 {
|
||||
v.Order()
|
||||
for _, p := range {{ $receiver }}.order {
|
||||
p(v)
|
||||
}
|
||||
}
|
||||
switch limit, offset := {{ $receiver }}.ctx.Limit, {{ $receiver }}.ctx.Offset; {
|
||||
case limit != nil && offset != nil:
|
||||
v.Range(*offset, *offset + *limit)
|
||||
case offset != nil:
|
||||
v.Range(*offset, math.MaxInt32)
|
||||
case limit != nil:
|
||||
v.Limit(*limit)
|
||||
}
|
||||
if unique := {{ $receiver }}.ctx.Unique; unique == nil || *unique {
|
||||
v.Dedup()
|
||||
}
|
||||
return v
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* query/path defines the query generation for path of a given edge. */}}
|
||||
{{ define "dialect/gremlin/query/path" }}
|
||||
{{- $e := $.Scope.Edge }} {{/* the edge we need to genegrate the path to. */}}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- $ident := $.Scope.Ident }}
|
||||
gremlin := {{ $receiver }}.gremlinQuery(ctx)
|
||||
{{- if $e.Bidi }}
|
||||
{{ $ident }} = gremlin.Both({{ $.Package }}.{{ $e.LabelConstant }})
|
||||
{{- else if $e.IsInverse }}
|
||||
{{ $ident }} = gremlin.InE({{ $e.Type.Package }}.{{ $e.LabelConstant }}).OutV()
|
||||
{{- else }}
|
||||
{{ $ident }} = gremlin.OutE({{ $.Package }}.{{ $e.LabelConstant }}).InV()
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{/* query/from defines the query generation for an edge query from a given node. */}}
|
||||
{{ define "dialect/gremlin/query/from" }}
|
||||
{{- $n := $ }} {{/* the node we start the query from. */}}
|
||||
{{- $e := $.Scope.Edge }} {{/* the edge we need to genegrate the path to. */}}
|
||||
{{- $receiver := $.Scope.Receiver }}
|
||||
{{- $ident := $.Scope.Ident -}}
|
||||
|
||||
{{- if $e.Bidi }}
|
||||
{{ $ident }} = g.V({{ $receiver }}.ID).Both({{ $n.Package }}.{{ $e.LabelConstant }})
|
||||
{{- else if $e.IsInverse }}
|
||||
{{ $ident }} = g.V({{ $receiver }}.ID).InE({{ $e.Type.Package }}.{{ $e.LabelConstant }}).OutV()
|
||||
{{- else }}
|
||||
{{ $ident }} = g.V({{ $receiver }}.ID).OutE({{ $n.Package }}.{{ $e.LabelConstant }}).InV()
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
44
entc/gen/template/dialect/gremlin/select.tmpl
Normal file
44
entc/gen/template/dialect/gremlin/select.tmpl
Normal file
@@ -0,0 +1,44 @@
|
||||
{{/*
|
||||
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/gremlin/select" }}
|
||||
{{ $builder := pascal $.Scope.Builder }}
|
||||
{{ $receiver := $.Scope.Receiver }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinScan(ctx context.Context, root *{{ $.QueryName }}, v any) error {
|
||||
var (
|
||||
res = &gremlin.Response{}
|
||||
traversal = root.gremlinQuery(ctx)
|
||||
)
|
||||
if fields := {{ $receiver }}.ctx.Fields; len(fields) == 1 {
|
||||
if fields[0] != {{ $.Package }}.FieldID {
|
||||
traversal = traversal.Values(fields...)
|
||||
} else {
|
||||
traversal = traversal.ID()
|
||||
}
|
||||
} else {
|
||||
fields := make([]any, len({{ $receiver }}.ctx.Fields))
|
||||
for i, f := range {{ $receiver }}.ctx.Fields {
|
||||
fields[i] = f
|
||||
}
|
||||
traversal = traversal.ValueMap(fields...)
|
||||
}
|
||||
query, bindings := traversal.Query()
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(root.ctx.Fields) == 1 {
|
||||
return res.ReadVal(v)
|
||||
}
|
||||
vm, err := res.ReadValueMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return vm.Decode(v)
|
||||
}
|
||||
{{ end }}
|
||||
198
entc/gen/template/dialect/gremlin/update.tmpl
Normal file
198
entc/gen/template/dialect/gremlin/update.tmpl
Normal file
@@ -0,0 +1,198 @@
|
||||
{{/*
|
||||
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/gremlin/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 }}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlinSave(ctx context.Context) ({{- if $one }}*{{ $.Name }}{{ else }}int{{ end }}, error) {
|
||||
{{- if $.HasUpdateCheckers }}
|
||||
if err := {{ $receiver }}.check(); err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
{{- end }}
|
||||
res := &gremlin.Response{}
|
||||
{{- if $one }}
|
||||
id, ok := {{ $mutation }}.{{ $.ID.MutationGet }}()
|
||||
if !ok {
|
||||
return {{ $zero }}, &ValidationError{Name: "{{ $.ID.Name }}", err: errors.New(`{{ $pkg }}: missing "{{ $.Name }}.{{ $.ID.Name }}" for update`)}
|
||||
}
|
||||
query, bindings := {{ $receiver }}.gremlin(id).Query()
|
||||
{{- else }}
|
||||
query, bindings := {{ $receiver }}.gremlin().Query()
|
||||
{{- end }}
|
||||
if err := {{ $receiver }}.driver.Exec(ctx, query, bindings, res); err != nil {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
if err, ok := isConstantError(res); ok {
|
||||
return {{ $zero }}, err
|
||||
}
|
||||
{{ $mutation }}.done = true
|
||||
{{- if $one }}
|
||||
{{- $r := $.Receiver }}
|
||||
{{ $r }} := &{{ $.Name }}{config: {{ $receiver }}.config}
|
||||
if err := {{ $r }}.FromResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return {{ $r }}, nil
|
||||
{{- else }}
|
||||
return res.ReadInt()
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
func ({{ $receiver }} *{{ $builder }}) gremlin({{ if $one }}id {{ $.ID.Type }}{{ end }}) *dsl.Traversal {
|
||||
{{- with .NumConstraint }}
|
||||
type constraint struct {
|
||||
pred *dsl.Traversal // constraint predicate.
|
||||
test *dsl.Traversal // test matches and its constant.
|
||||
}
|
||||
constraints := make([]*constraint, 0, {{ . }})
|
||||
{{- end }}
|
||||
{{- /* case of update specific vertex */}}
|
||||
{{- if $one }}
|
||||
v := g.V(id)
|
||||
{{- /* general update for N vertices */}}
|
||||
{{- else }}
|
||||
v := g.V().HasLabel({{ $.Package }}.Label)
|
||||
for _, p := range {{ $mutation }}.Predicates() {
|
||||
p(v)
|
||||
}
|
||||
{{- end }}
|
||||
var (
|
||||
{{ if or .NumConstraint (len $.Edges) }}
|
||||
rv = v.Clone()
|
||||
_ = rv
|
||||
{{ end }}
|
||||
trs []*dsl.Traversal
|
||||
)
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- if or (not $f.Immutable) $f.UpdateDefault }}
|
||||
if value, ok := {{ $mutation }}.{{ $f.MutationGet }}(); ok {
|
||||
{{- if $f.Unique }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.V().Has({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, value).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, value)),
|
||||
})
|
||||
{{- end }}
|
||||
v.Property(dsl.Single, {{ $.Package }}.{{ $f.Constant }}, value)
|
||||
}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
if value, ok := {{ $mutation }}.Added{{ $f.StructField }}(); ok {
|
||||
{{- if $f.Unique }}
|
||||
addValue := rv.Clone().Union(__.Values({{ $.Package }}.{{ $f.Constant }}), __.Constant(value)).Sum().Next()
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.V().Has({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, addValue).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueField({{ $.Package }}.Label, {{ $.Package }}.{{ $f.Constant }}, fmt.Sprintf("+= %v", value))),
|
||||
})
|
||||
{{- end }}
|
||||
v.Property(dsl.Single, {{ $.Package }}.{{ $f.Constant }}, __.Union(__.Values({{ $.Package }}.{{ $f.Constant }}), __.Constant(value)).Sum())
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* clear optional fields. */}}
|
||||
{{- with $.HasOptional }}
|
||||
var properties []any
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{- if $f.IsBlob }}{{ continue }}{{ end }}
|
||||
{{- if $f.Optional }}
|
||||
if {{ $mutation }}.{{ $f.StructField }}Cleared() {
|
||||
properties = append(properties, {{ $.Package }}.{{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
if len(properties) > 0 {
|
||||
v.SideEffect(__.Properties(properties...).Drop())
|
||||
}
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- $direction := "In" }}
|
||||
{{- $name := printf "%s.%s" $.Package $e.LabelConstant }}
|
||||
{{- if $e.IsInverse }}
|
||||
{{- $direction = "Out" }}
|
||||
{{- $name = printf "%s.%s" $e.Type.Package $e.LabelConstant }}
|
||||
{{- end }}
|
||||
{{- /* remove edges */}}
|
||||
{{- if $e.Unique }}
|
||||
if {{ $mutation }}.{{ $e.StructField }}Cleared() {
|
||||
{{- else }}
|
||||
for _, id := range {{ $mutation }}.Removed{{ $e.StructField }}IDs() {
|
||||
{{- end }}
|
||||
{{- if $e.Bidi }}
|
||||
tr := rv.Clone().BothE({{ $name }}){{ if not $e.Unique }}.Where(__.Or(__.InV().HasID(id), __.OutV().HasID(id))){{ end }}.Drop().Iterate()
|
||||
{{- else if $e.IsInverse }}
|
||||
tr := rv.Clone().InE({{ $name }}){{ if not $e.Unique }}.Where(__.OtherV().HasID(id)){{ end }}.Drop().Iterate()
|
||||
{{- else }}
|
||||
tr := rv.Clone().OutE({{ $name }}){{ if not $e.Unique }}.Where(__.OtherV().HasID(id)){{ end }}.Drop().Iterate()
|
||||
{{- end }}
|
||||
trs = append(trs, tr)
|
||||
}
|
||||
{{- /* update edges */}}
|
||||
for _, id := range {{ $mutation }}.{{ $e.StructField }}IDs() {
|
||||
{{- if $e.IsInverse }}
|
||||
v.AddE({{ $name }}).From(g.V(id)).InV()
|
||||
{{- else }}
|
||||
v.AddE({{ $name }}).To(g.V(id)).OutV()
|
||||
{{- end }}
|
||||
{{- if $e.HasConstraint }}
|
||||
{{- if $e.Bidi }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: rv.Clone().Both({{ $name }}).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueEdge({{ $.Package }}.Label, {{ $name }}, id)),
|
||||
})
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.E().HasLabel({{ $name }}).Where(__.Or(__.InV().HasID(id), __.OutV().HasID(id))).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueEdge({{ $.Package }}.Label, {{ $name }}, id)),
|
||||
})
|
||||
{{- else }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: g.E().HasLabel({{ $name }}).{{ $direction }}V().HasID(id).Count(),
|
||||
test: __.Is(p.NEQ(0)).Constant(NewErrUniqueEdge({{ $.Package }}.Label, {{ $name }}, id)),
|
||||
})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- if $one }}
|
||||
if len({{ $receiver }}.fields) > 0 {
|
||||
fields := make([]any, 0, len({{ $receiver }}.fields)+1)
|
||||
fields = append(fields, true)
|
||||
for _, f := range {{ $receiver }}.fields {
|
||||
fields = append(fields, f)
|
||||
}
|
||||
v.ValueMap(fields...)
|
||||
} else {
|
||||
v.ValueMap(true)
|
||||
}
|
||||
{{- else }}
|
||||
v.Count()
|
||||
{{- end }}
|
||||
{{- with .NumConstraint }}
|
||||
if len(constraints) > 0 {
|
||||
{{- /* make sure the traversal does not contain more than one vertex if we have constraint */}}
|
||||
{{- if not $one }}
|
||||
constraints = append(constraints, &constraint{
|
||||
pred: rv.Count(),
|
||||
test: __.Is(p.GT(1)).Constant(&ConstraintError{msg: "update traversal contains more than one vertex"}),
|
||||
})
|
||||
{{- end }}
|
||||
v = constraints[0].pred.Coalesce(constraints[0].test, v)
|
||||
for _, cr := range constraints[1:] {
|
||||
v = cr.pred.Coalesce(cr.test, v)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
trs = append(trs, v)
|
||||
return dsl.Join(trs...)
|
||||
}
|
||||
{{ end }}
|
||||
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 }}
|
||||
291
entc/gen/template/ent.tmpl
Normal file
291
entc/gen/template/ent.tmpl
Normal file
@@ -0,0 +1,291 @@
|
||||
{{/*
|
||||
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 "model" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ template "header" $ }}
|
||||
|
||||
{{ with extend $ "Imports" $.SiblingImports }}
|
||||
{{ template "import" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "model/comment" $ }}
|
||||
{{- with $tmpls := matchTemplate "model/comment/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
type {{ $.Name }} struct {
|
||||
config {{ template "model/omittags" $ }}
|
||||
{{- if $.HasOneFieldID }}
|
||||
// ID of the ent.
|
||||
{{- if $.ID.Comment }}
|
||||
{{- range $line := split $.ID.Comment "\n" }}
|
||||
// {{ $line }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ID {{ $.ID.Type }} {{ with $.Annotations.Fields.StructTag.id }}`{{ . }}`{{ else }}`{{ $.ID.StructTag }}`{{ end }}
|
||||
{{- end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- $tag := $f.StructTag }}{{ with $tags := $.Annotations.Fields.StructTag }}{{ with index $tags $f.Name }}{{ $tag = . }}{{ end }}{{ end }}
|
||||
{{- template "model/fieldcomment" $f }}
|
||||
{{ $f.StructField }} {{ if $f.NillableValue }}*{{ end }}{{ $f.Type }} {{ if not $f.Sensitive }}`{{ $tag }}`{{ else }}{{ template "model/omittags" $ }}{{ end }}
|
||||
{{- end }}
|
||||
{{- with $.Edges }}
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the {{ $.Name }}Query when eager-loading is set.
|
||||
Edges {{ $.Name }}Edges {{ template "model/edgetags" $ }}
|
||||
{{- end -}}
|
||||
{{- /* Additional fields to add by the storage driver. */}}
|
||||
{{- $tmpl := printf "dialect/%s/model/fields" $.Storage }}
|
||||
{{- if hasTemplate $tmpl }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}
|
||||
{{- /* Additional fields to add by the user. */}}
|
||||
{{- template "model/fields/additional" $ }}
|
||||
}
|
||||
|
||||
{{- with $.Edges }}
|
||||
{{- $edgesType := print $.Name "Edges"}}
|
||||
// {{ $.Name }}Edges holds the relations/edges for other nodes in the graph.
|
||||
type {{ $edgesType }} struct {
|
||||
{{- range $e := . }}
|
||||
{{- template "model/edgecomment" $e }}
|
||||
{{ $e.StructField }} {{ if not $e.Unique }}[]{{ end }}*{{ $e.Type.Name }} {{ with $e.StructTag }}`{{ . }}`{{ end }}
|
||||
{{- end }}
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [{{ len . }}]bool
|
||||
{{- /* Additional fields to add by the user. */}}
|
||||
{{- template "model/edges/fields/additional" $ }}
|
||||
}
|
||||
|
||||
{{- range $i, $e := . }}
|
||||
// {{ $e.StructField }}OrErr returns the {{ $e.StructField }} value or an error if the edge
|
||||
// was not loaded in eager-loading{{ if $e.Unique }}, or loaded but was not found{{ end }}.
|
||||
func (e {{ $edgesType }}) {{ $e.StructField }}OrErr() ({{ if not $e.Unique }}[]{{ end }}*{{ $e.Type.Name }}, error) {
|
||||
{{- if $e.Unique }}
|
||||
if e.{{ $e.StructField }} != nil {
|
||||
return e.{{ $e.StructField }}, nil
|
||||
} else if e.loadedTypes[{{ $i }}] {
|
||||
{{- /* Edge was loaded but was not found. */}}
|
||||
return nil, &NotFoundError{label: {{ $e.Type.Package }}.Label}
|
||||
}
|
||||
{{- else }}
|
||||
if e.loadedTypes[{{ $i }}] {
|
||||
return e.{{ $e.StructField }}, nil
|
||||
}
|
||||
{{- end }}
|
||||
return nil, &NotLoadedError{edge: "{{ $e.Name }}"}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/decode/one" $.Storage }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
|
||||
{{ $receiver := $.Receiver }}
|
||||
|
||||
{{ range $e := $.Edges }}
|
||||
{{ $func := print "Query" $e.StructField }}
|
||||
// {{ $func }} queries the "{{ $e.Name }}" edge of the {{ $.Name }} entity.
|
||||
func ({{ $receiver }} *{{ $.Name }}) {{ $func }}() *{{ $e.Type.QueryName }} {
|
||||
return New{{ $.ClientName }}({{ $receiver }}.config).{{ $func }}({{ $receiver }})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{- $blobModelTmpl := printf "dialect/%s/model/fields" $.Storage }}
|
||||
{{- if hasTemplate $blobModelTmpl }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
// {{ $f.StructField }}Reader opens a reader for the "{{ $f.Name }}" field from blob storage.
|
||||
// The caller must close the returned reader when done.
|
||||
func ({{ $receiver }} *{{ $.Name }}) {{ $f.StructField }}Reader(ctx context.Context) (io.ReadCloser, error) {
|
||||
if {{ $receiver }}.{{ $f.BlobKeyColumn }} == nil || *{{ $receiver }}.{{ $f.BlobKeyColumn }} == "" {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: {{ $.Name }}.{{ $f.BlobKeyColumn }} is nil or empty")
|
||||
}
|
||||
if {{ $receiver }}.blobOpeners.{{ $.Name }} == nil {
|
||||
return nil, fmt.Errorf("{{ $pkg }}: blob storage not configured (missing WithBlobOpeners)")
|
||||
}
|
||||
b, err := {{ $receiver }}.blobOpeners.{{ $.Name }}(ctx, {{ $.Package }}.{{ $f.Constant }})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ent.BlobReader(ctx, b, *{{ $receiver }}.{{ $f.BlobKeyColumn }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if not $.IsView }}
|
||||
// Update returns a builder for updating this {{ $.Name }}.
|
||||
// Note that you need to call {{ $.Name }}.Unwrap() before calling this method if this {{ $.Name }}
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
func ({{ $receiver }} *{{ $.Name }}) Update() *{{ $.UpdateOneName }} {
|
||||
return New{{ $.ClientName }}({{ $receiver }}.config).UpdateOne({{ $receiver }})
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// Unwrap unwraps the {{ $.Name }} entity that was returned from a transaction after it was closed,
|
||||
// so that all future queries will be executed through the driver which created the transaction.
|
||||
func ({{ $receiver }} *{{ $.Name }}) Unwrap() *{{ $.Name }} {
|
||||
_tx, ok := {{ $receiver }}.config.driver.(*txDriver)
|
||||
if !ok {
|
||||
panic("{{ $pkg }}: {{ $.Name }} is not a transactional entity")
|
||||
}
|
||||
{{ $receiver }}.config.driver = _tx.drv
|
||||
return {{ $receiver }}
|
||||
}
|
||||
|
||||
{{ template "model/stringer" $ }}
|
||||
|
||||
{{ template "model/additional" $ }}
|
||||
|
||||
{{ $slice := plural $.Name }}
|
||||
// {{ $slice }} is a parsable slice of {{ $.Name }}.
|
||||
type {{ $slice }} []*{{ $.Name }}
|
||||
|
||||
{{ with extend $ "Slice" $slice }}
|
||||
{{ $tmpl := printf "dialect/%s/decode/many" $.Storage }}
|
||||
{{ xtemplate $tmpl . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* A template to generate a fmt.Stringer implementation. */}}
|
||||
{{ define "model/stringer" }}
|
||||
{{ $receiver := $.Receiver }}
|
||||
|
||||
// String implements the fmt.Stringer.
|
||||
func ({{ $receiver }} *{{ $.Name }}) String() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("{{ $.Name }}(")
|
||||
{{- if $.HasOneFieldID }}
|
||||
builder.WriteString(fmt.Sprintf("id=%v{{ if $.Fields }}, {{ end }}", {{ $receiver }}.ID))
|
||||
{{- end }}
|
||||
{{- range $i, $f := $.Fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- if ne $i 0 }}
|
||||
builder.WriteString(", ")
|
||||
{{- end }}
|
||||
{{- if $f.Sensitive }}
|
||||
builder.WriteString("{{ $f.Name }}={{ print "<sensitive>" }}")
|
||||
{{- else }}
|
||||
{{- $sf := printf "%s.%s" $receiver $f.StructField }}
|
||||
{{- if $f.Nillable }}
|
||||
if v := {{ $sf }}; v != nil {
|
||||
builder.WriteString("{{ $f.Name }}=")
|
||||
{{- if and $f.IsTime (not $f.HasGoType) }}
|
||||
builder.WriteString(v.Format(time.ANSIC))
|
||||
{{- else if and $f.IsString (not $f.HasGoType) }}
|
||||
builder.WriteString(*v)
|
||||
{{- else }}
|
||||
builder.WriteString(fmt.Sprintf("%v", *v))
|
||||
{{- end }}
|
||||
}
|
||||
{{- else }}
|
||||
builder.WriteString("{{ $f.Name }}=")
|
||||
{{- if and $f.IsTime (not $f.HasGoType) }}
|
||||
builder.WriteString({{ $sf }}.Format(time.ANSIC))
|
||||
{{- else if and $f.IsString (not $f.HasGoType) }}
|
||||
builder.WriteString({{ $sf }})
|
||||
{{- else }}
|
||||
builder.WriteString(fmt.Sprintf("%v", {{ $sf }}))
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{/* A template for generating the Entity's comment. */}}
|
||||
{{- define "model/comment" }}
|
||||
{{- if and $.Annotations.Comment $.Annotations.Comment.Text }}
|
||||
{{- range $line := split $.Annotations.Comment.Text "\n" }}
|
||||
// {{ $line }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
// {{ $.Name }} is the model entity for the {{ $.Name }} schema.
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for generating the tag of the Edges struct-field. */}}
|
||||
{{- define "model/edgetags" }}
|
||||
{{- $tag := `json:"edges"` }}
|
||||
{{- with $.Annotations.Edges.StructTag }}
|
||||
{{- if tagLookup . "json" }}
|
||||
{{- $tag = . }}
|
||||
{{- else }}
|
||||
{{- $tag = print $tag " " . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- print "`" $tag "`" }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for omitting struct-tags. */}}
|
||||
{{ define "model/omittags" }}{{ with $.TagTypes }}`{{ range $i, $t := . }}{{ if ne $i 0 }} {{ end }}{{ $t }}:"-"{{ end }}`{{ end }}{{ end }}
|
||||
|
||||
{{/* A template that can be overridden in order to add additional fields to the each type.*/}}
|
||||
{{ define "model/fields/additional" }}{{ end }}
|
||||
|
||||
{{/* A template for setting the field comment. */}}
|
||||
{{- define "model/fieldcomment" }}
|
||||
{{- if $.Comment }}
|
||||
{{- range $line := split $.Comment "\n" }}
|
||||
// {{ $line }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
// {{ $.StructField }} holds the value of the "{{ $.Name }}" field.
|
||||
{{- end }}
|
||||
{{- if $.IsDeprecated }}
|
||||
//
|
||||
// Deprecated: {{ with $.DeprecationReason }}{{ . }}{{ else }}Field "{{ $.Name }}" was marked as deprecated in the schema.{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for setting the edge comment. */}}
|
||||
{{- define "model/edgecomment" }}
|
||||
{{- if $.Comment }}
|
||||
{{- range $line := split $.Comment "\n" }}
|
||||
// {{ $line }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
// {{ $.StructField }} holds the value of the {{ $.Name }} edge.
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for adding additional methods or helpers for the generated model. */}}
|
||||
{{ define "model/additional" }}
|
||||
{{- with $tmpls := matchTemplate "model/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $tmpls := matchTemplate (printf "dialect/%s/model/additional/*" $.Storage) }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* A template for adding additional fields for the Edges object of the generated model. */}}
|
||||
{{ define "model/edges/fields/additional" }}
|
||||
{{- with $tmpls := matchTemplate "model/edges/fields/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $tmpls := matchTemplate (printf "dialect/%s/model/edges/fields/additional/*" $.Storage) }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
111
entc/gen/template/enttest.tmpl
Normal file
111
entc/gen/template/enttest.tmpl
Normal file
@@ -0,0 +1,111 @@
|
||||
{{/*
|
||||
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 "enttest" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
{{ with extend $ "Package" "enttest" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
|
||||
"{{ $.Config.Package }}"
|
||||
// required by schema hooks.
|
||||
_ "{{ $.Config.Package }}/runtime"
|
||||
|
||||
{{ if $.SupportMigrate }}
|
||||
"{{ $.Config.Package }}/migrate"
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
{{ end }}
|
||||
)
|
||||
|
||||
type (
|
||||
// TestingT is the interface that is shared between
|
||||
// testing.T and testing.B and used by enttest.
|
||||
TestingT interface {
|
||||
FailNow()
|
||||
Error(...any)
|
||||
}
|
||||
|
||||
// Option configures client creation.
|
||||
Option func(*options)
|
||||
|
||||
options struct {
|
||||
opts []{{ $pkg }}.Option
|
||||
{{- if $.SupportMigrate }}
|
||||
migrateOpts []schema.MigrateOption
|
||||
{{- end }}
|
||||
}
|
||||
)
|
||||
|
||||
// WithOptions forwards options to client creation.
|
||||
func WithOptions(opts ...{{ $pkg }}.Option) Option {
|
||||
return func(o *options) {
|
||||
o.opts = append(o.opts, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
{{- if $.SupportMigrate }}
|
||||
// WithMigrateOptions forwards options to auto migration.
|
||||
func WithMigrateOptions(opts ...schema.MigrateOption) Option {
|
||||
return func(o *options) {
|
||||
o.migrateOpts = append(o.migrateOpts, opts...)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
func newOptions(opts []Option) *options {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// Open calls {{ $pkg }}.Open and auto-run migration.
|
||||
func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *{{ $pkg }}.Client {
|
||||
o := newOptions(opts)
|
||||
c, err := {{ $pkg }}.Open(driverName, dataSourceName, o.opts...)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
{{- if $.SupportMigrate }}
|
||||
migrateSchema(t, c, o)
|
||||
{{- end }}
|
||||
return c
|
||||
}
|
||||
|
||||
// NewClient calls {{ $pkg }}.NewClient and auto-run migration.
|
||||
func NewClient(t TestingT, opts ...Option) *{{ $pkg }}.Client {
|
||||
o := newOptions(opts)
|
||||
c := {{ $pkg }}.NewClient(o.opts...)
|
||||
{{- if $.SupportMigrate }}
|
||||
migrateSchema(t, c, o)
|
||||
{{- end }}
|
||||
return c
|
||||
}
|
||||
|
||||
{{- if $.SupportMigrate }}
|
||||
func migrateSchema(t TestingT, c *{{ $pkg }}.Client, o *options) {
|
||||
tables, err := schema.CopyTables(migrate.Tables)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
17
entc/gen/template/header.tmpl
Normal file
17
entc/gen/template/header.tmpl
Normal file
@@ -0,0 +1,17 @@
|
||||
{{/*
|
||||
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 "header" }}
|
||||
{{ with $.Header }}{{ . }}{{ else }}// Code generated by ent, DO NOT EDIT.{{ end }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
{{ if hasField $ "Scope" }}
|
||||
{{ $pkg = $.Scope.Package }}
|
||||
{{ end }}
|
||||
package {{ $pkg }}
|
||||
{{ end }}
|
||||
192
entc/gen/template/hook.tmpl
Normal file
192
entc/gen/template/hook.tmpl
Normal file
@@ -0,0 +1,192 @@
|
||||
{{/*
|
||||
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 "hook" }}
|
||||
|
||||
{{ with extend $ "Package" "hook" }}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import "{{ $.Config.Package }}"
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
{{ range $n := $.MutableNodes }}
|
||||
{{ $name := print $n.Name "Func" }}
|
||||
{{ $type := printf "*%s.%s" $pkg $n.MutationName }}
|
||||
|
||||
// The {{ $name }} type is an adapter to allow the use of ordinary
|
||||
// function as {{ $n.Name }} mutator.
|
||||
type {{ $name }} func(context.Context, {{ $type }}) ({{ $pkg }}.Value, error)
|
||||
|
||||
// Mutate calls f(ctx, m).
|
||||
func (f {{ $name }}) Mutate(ctx context.Context, m {{ $pkg }}.Mutation) ({{ $pkg }}.Value, error) {
|
||||
if mv, ok := m.({{ $type }}); ok {
|
||||
return f(ctx, mv)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect {{ $type }}", m)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// Condition is a hook condition function.
|
||||
type Condition func(context.Context, {{ $pkg }}.Mutation) bool
|
||||
|
||||
// And groups conditions with the AND operator.
|
||||
func And(first, second Condition, rest ...Condition) Condition {
|
||||
return func(ctx context.Context, m {{ $pkg }}.Mutation) bool {
|
||||
if !first(ctx, m) || !second(ctx, m) {
|
||||
return false
|
||||
}
|
||||
for _, cond := range rest {
|
||||
if !cond(ctx, m) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Or groups conditions with the OR operator.
|
||||
func Or(first, second Condition, rest ...Condition) Condition {
|
||||
return func(ctx context.Context, m {{ $pkg }}.Mutation) bool {
|
||||
if first(ctx, m) || second(ctx, m) {
|
||||
return true
|
||||
}
|
||||
for _, cond := range rest {
|
||||
if cond(ctx, m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Not negates a given condition.
|
||||
func Not(cond Condition) Condition {
|
||||
return func(ctx context.Context, m {{ $pkg }}.Mutation) bool {
|
||||
return !cond(ctx, m)
|
||||
}
|
||||
}
|
||||
|
||||
// HasOp is a condition testing mutation operation.
|
||||
func HasOp(op {{ $pkg }}.Op) Condition {
|
||||
return func(_ context.Context, m {{ $pkg }}.Mutation) bool {
|
||||
return m.Op().Is(op)
|
||||
}
|
||||
}
|
||||
|
||||
{{ $conds := dict "HasFields" "Field" "HasAddedFields" "AddedField" "HasClearedFields" "FieldCleared" -}}
|
||||
{{- range $cond := keys $conds }}
|
||||
{{ $method := get $conds $cond -}}
|
||||
// {{ $cond }} is a condition validating `.{{ $method }}` on fields.
|
||||
func {{ $cond }}(field string, fields ...string) Condition {
|
||||
return func(_ context.Context, m {{ $pkg }}.Mutation) bool {
|
||||
if {{ if ne $method "FieldCleared" }}_, {{ end }}exists := m.{{ $method }}(field); !exists {
|
||||
return false
|
||||
}
|
||||
for _, field := range fields {
|
||||
if {{ if ne $method "FieldCleared" }}_, {{ end }}exists := m.{{ $method }}(field); !exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// If executes the given hook under condition.
|
||||
//
|
||||
// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...)))
|
||||
//
|
||||
func If(hk {{ $pkg }}.Hook, cond Condition) {{ $pkg }}.Hook {
|
||||
return func(next {{ $pkg }}.Mutator) {{ $pkg }}.Mutator {
|
||||
return {{ $pkg }}.MutateFunc(func(ctx context.Context, m {{ $pkg }}.Mutation) ({{ $pkg }}.Value, error) {
|
||||
if cond(ctx, m) {
|
||||
return hk(next).Mutate(ctx, m)
|
||||
}
|
||||
return next.Mutate(ctx, m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// On executes the given hook only for the given operation.
|
||||
//
|
||||
// hook.On(Log, {{ $pkg }}.Delete|{{ $pkg }}.Create)
|
||||
//
|
||||
func On(hk {{ $pkg }}.Hook, op {{ $pkg }}.Op) {{ $pkg }}.Hook {
|
||||
return If(hk, HasOp(op))
|
||||
}
|
||||
|
||||
// Unless skips the given hook only for the given operation.
|
||||
//
|
||||
// hook.Unless(Log, {{ $pkg }}.Update|{{ $pkg }}.UpdateOne)
|
||||
//
|
||||
func Unless(hk {{ $pkg }}.Hook, op {{ $pkg }}.Op) {{ $pkg }}.Hook {
|
||||
return If(hk, Not(HasOp(op)))
|
||||
}
|
||||
|
||||
// FixedError is a hook returning a fixed error.
|
||||
func FixedError(err error) {{ $pkg }}.Hook {
|
||||
return func({{ $pkg }}.Mutator) {{ $pkg }}.Mutator {
|
||||
return {{ $pkg }}.MutateFunc(func(context.Context, {{ $pkg }}.Mutation) ({{ $pkg }}.Value, error) {
|
||||
return nil, err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reject returns a hook that rejects all operations that match op.
|
||||
//
|
||||
// func (T) Hooks() []{{ $pkg }}.Hook {
|
||||
// return []{{ $pkg }}.Hook{
|
||||
// Reject({{ $pkg }}.Delete|{{ $pkg }}.Update),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
func Reject(op {{ $pkg }}.Op) {{ $pkg }}.Hook {
|
||||
hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
|
||||
return On(hk, op)
|
||||
}
|
||||
|
||||
// Chain acts as a list of hooks and is effectively immutable.
|
||||
// Once created, it will always hold the same set of hooks in the same order.
|
||||
type Chain struct {
|
||||
hooks []{{ $pkg }}.Hook
|
||||
}
|
||||
|
||||
// NewChain creates a new chain of hooks.
|
||||
func NewChain(hooks ...{{ $pkg }}.Hook) Chain {
|
||||
return Chain{append([]{{ $pkg }}.Hook(nil), hooks...)}
|
||||
}
|
||||
|
||||
// Hook chains the list of hooks and returns the final hook.
|
||||
func (c Chain) Hook() {{ $pkg }}.Hook {
|
||||
return func(mutator {{ $pkg }}.Mutator) {{ $pkg }}.Mutator {
|
||||
for i := len(c.hooks) - 1; i >= 0; i-- {
|
||||
mutator = c.hooks[i](mutator)
|
||||
}
|
||||
return mutator
|
||||
}
|
||||
}
|
||||
|
||||
// Append extends a chain, adding the specified hook
|
||||
// as the last ones in the mutation flow.
|
||||
func (c Chain) Append(hooks ...{{ $pkg }}.Hook) Chain {
|
||||
newHooks := make([]{{ $pkg }}.Hook, 0, len(c.hooks)+len(hooks))
|
||||
newHooks = append(newHooks, c.hooks...)
|
||||
newHooks = append(newHooks, hooks...)
|
||||
return Chain{newHooks}
|
||||
}
|
||||
|
||||
// Extend extends a chain, adding the specified chain
|
||||
// as the last ones in the mutation flow.
|
||||
func (c Chain) Extend(chain Chain) Chain {
|
||||
return c.Append(chain.hooks...)
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
87
entc/gen/template/import.tmpl
Normal file
87
entc/gen/template/import.tmpl
Normal file
@@ -0,0 +1,87 @@
|
||||
{{/*
|
||||
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" }}
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
{{- /* Ignore generating on graph templates (e.g. config.tmpl, client.tmpl, etc). */}}
|
||||
{{- if ne $.Config.Package $.Package }}
|
||||
"{{ $.Config.Package }}/predicate"
|
||||
{{- if $.HasOneFieldID }}
|
||||
{{- with $.ID.Type.PkgPath }}
|
||||
{{- $name := $.ID.Type.PkgName }}
|
||||
{{ if ne $name (base $.ID.Type.PkgPath) }}{{ $name }} {{ end }}"{{ . }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* Import external packages */}}
|
||||
{{- template "import/types" $ }}
|
||||
{{- end }}
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/schema/field"
|
||||
{{- with $.Storage }}
|
||||
{{- range $import := .Imports }}
|
||||
"{{ $import }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and (hasField $ "Scope") ($.Scope.Imports) }}
|
||||
{{- range $import := $.Scope.Imports }}
|
||||
{{ $import.Alias }} "{{ $import.Path }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- template "import/additional" $ }}
|
||||
)
|
||||
{{ end }}
|
||||
|
||||
{{/* A template for importing fields with custom types */}}
|
||||
{{ define "import/types" -}}
|
||||
{{- $seen := dict }}
|
||||
{{- $selfPkg := "" }}{{ if ne $.Config.Package $.Package }}{{ $selfPkg = print $.Config.Package "/" $.PackageDir }}{{ end }}
|
||||
{{- $fields := $.Fields }}{{ if $.HasOneFieldID }}{{ if $.ID.UserDefined }}{{ $fields = append $fields $.ID }}{{ end }}{{ end }}
|
||||
{{- range $f := $fields }}
|
||||
{{- $pkg := $f.Type.PkgPath }}
|
||||
{{- if and $pkg (not (hasImport (base $pkg))) (not (hasKey $seen $pkg)) (ne $pkg $selfPkg) }}
|
||||
{{- $name := $f.Type.PkgName }}
|
||||
{{ if ne $name (base $pkg) }}{{ $name }} {{ end}}"{{ $pkg }}"
|
||||
{{- $seen = set $seen $pkg true }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* Import packages for edge target ID types referenced in mutation fields. */}}
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- $pkg := $e.Type.ID.Type.PkgPath }}
|
||||
{{- if and $pkg (not (hasImport (base $pkg))) (not (hasKey $seen $pkg)) (ne $pkg $selfPkg) }}
|
||||
{{- $name := $e.Type.ID.Type.PkgName }}
|
||||
{{ if ne $name (base $pkg) }}{{ $name }} {{ end}}"{{ $pkg }}"
|
||||
{{- $seen = set $seen $pkg true }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/* A template for allowing additional imports by ent extensions or user templates.*/}}
|
||||
{{ define "import/additional" }}
|
||||
{{- with $tmpls := matchTemplate "import/additional/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.Storage }}
|
||||
{{- with $tmpls := matchTemplate (printf "dialect/%s/import/additional/*" $.Storage) }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
171
entc/gen/template/intercept.tmpl
Normal file
171
entc/gen/template/intercept.tmpl
Normal file
@@ -0,0 +1,171 @@
|
||||
{{/*
|
||||
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 "intercept" }}
|
||||
|
||||
{{ with extend $ "Package" "intercept" }}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"{{ $.Config.Package }}"
|
||||
"{{ $.Config.Package }}/predicate"
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $n.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
// The Query interface represents an operation that queries a graph.
|
||||
// By using this interface, users can write generic code that manipulates
|
||||
// query builders of different types.
|
||||
type Query interface {
|
||||
// Type returns the string representation of the query type.
|
||||
Type() string
|
||||
// Limit the number of records to be returned by this query.
|
||||
Limit(int)
|
||||
// Offset to start from.
|
||||
Offset(int)
|
||||
// Unique configures the query builder to filter duplicate records.
|
||||
Unique(bool)
|
||||
// Order specifies how the records should be ordered.
|
||||
Order(...func({{ $.Storage.Builder }}))
|
||||
// WhereP appends storage-level predicates to the query builder. Using this method, users
|
||||
// can use type-assertion to append predicates that do not depend on any generated package.
|
||||
WhereP(...func({{ $.Storage.Builder }}))
|
||||
}
|
||||
|
||||
// The Func type is an adapter that allows ordinary functions to be used as interceptors.
|
||||
// Unlike traversal functions, interceptors are skipped during graph traversals. Note that the
|
||||
// implementation of Func is different from the one defined in entgo.io/ent.InterceptFunc.
|
||||
type Func func(context.Context, Query) error
|
||||
|
||||
// Intercept calls f(ctx, q) and then applied the next Querier.
|
||||
func (f Func) Intercept(next {{ $pkg }}.Querier) {{ $pkg }}.Querier {
|
||||
return {{ $pkg }}.QuerierFunc(func(ctx context.Context, q {{ $pkg }}.Query) ({{ $pkg }}.Value, error) {
|
||||
query, err := NewQuery(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := f(ctx, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return next.Query(ctx, q)
|
||||
})
|
||||
}
|
||||
|
||||
// The TraverseFunc type is an adapter to allow the use of ordinary function as Traverser.
|
||||
// If f is a function with the appropriate signature, TraverseFunc(f) is a Traverser that calls f.
|
||||
type TraverseFunc func(context.Context, Query) error
|
||||
|
||||
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||
func (f TraverseFunc) Intercept(next {{ $pkg }}.Querier) {{ $pkg }}.Querier {
|
||||
return next
|
||||
}
|
||||
|
||||
// Traverse calls f(ctx, q).
|
||||
func (f TraverseFunc) Traverse(ctx context.Context, q {{ $pkg }}.Query) error {
|
||||
query, err := NewQuery(q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(ctx, query)
|
||||
}
|
||||
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $name := print $n.Name "Func" }}
|
||||
{{ $type := printf "*%s.%s" $pkg $n.QueryName }}
|
||||
// The {{ $name }} type is an adapter to allow the use of ordinary function as a Querier.
|
||||
type {{ $name }} func(context.Context, {{ $type }}) ({{ $pkg }}.Value, error)
|
||||
|
||||
// Query calls f(ctx, q).
|
||||
func (f {{ $name }}) Query(ctx context.Context, q {{ $pkg }}.Query) ({{ $pkg }}.Value, error) {
|
||||
if q, ok := q.({{ $type }}); ok {
|
||||
return f(ctx, q)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected query type %T. expect {{ $type }}", q)
|
||||
}
|
||||
|
||||
{{ $name = print "Traverse" $n.Name }}
|
||||
// The {{ $name }} type is an adapter to allow the use of ordinary function as Traverser.
|
||||
type {{ $name }} func(context.Context, {{ $type }}) error
|
||||
|
||||
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||
func (f {{ $name }}) Intercept(next {{ $pkg }}.Querier) {{ $pkg }}.Querier {
|
||||
return next
|
||||
}
|
||||
|
||||
// Traverse calls f(ctx, q).
|
||||
func (f {{ $name }}) Traverse(ctx context.Context, q {{ $pkg }}.Query) error {
|
||||
if q, ok := q.({{ $type }}); ok {
|
||||
return f(ctx, q)
|
||||
}
|
||||
return fmt.Errorf("unexpected query type %T. expect {{ $type }}", q)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// NewQuery returns the generic Query interface for the given typed query.
|
||||
func NewQuery(q {{ $pkg }}.Query) (Query, error) {
|
||||
switch q := q.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $pkg }}.{{ $n.QueryName }}:
|
||||
return &query[*{{ $pkg }}.{{ $n.QueryName }}, predicate.{{ $n.Name }}, {{ $n.Package }}.OrderOption]{typ: {{ $pkg }}.{{ $n.TypeName }}, tq: q}, nil
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown query type %T", q)
|
||||
}
|
||||
}
|
||||
|
||||
type query[T any, P ~func({{ $.Storage.Builder }}), R ~func({{ $.Storage.Builder }})] struct {
|
||||
typ string
|
||||
tq interface {
|
||||
Limit(int) T
|
||||
Offset(int) T
|
||||
Unique(bool) T
|
||||
Order(...R) T
|
||||
Where(...P) T
|
||||
}
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) Type() string {
|
||||
return q.typ
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) Limit(limit int) {
|
||||
q.tq.Limit(limit)
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) Offset(offset int) {
|
||||
q.tq.Offset(offset)
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) Unique(unique bool) {
|
||||
q.tq.Unique(unique)
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) Order(orders ...func({{ $.Storage.Builder }})) {
|
||||
rs := make([]R, len(orders))
|
||||
for i := range orders {
|
||||
rs[i] = orders[i]
|
||||
}
|
||||
q.tq.Order(rs...)
|
||||
}
|
||||
|
||||
func (q query[T, P, R]) WhereP(ps ...func({{ $.Storage.Builder }})) {
|
||||
p := make([]P, len(ps))
|
||||
for i := range ps {
|
||||
p[i] = ps[i]
|
||||
}
|
||||
q.tq.Where(p...)
|
||||
}
|
||||
{{ end }}
|
||||
28
entc/gen/template/internal.tmpl
Normal file
28
entc/gen/template/internal.tmpl
Normal file
@@ -0,0 +1,28 @@
|
||||
{{/*
|
||||
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 "internal/schema" }}
|
||||
|
||||
{{ with $.Header }}{{ . }}{{ else }}// Code generated by ent, DO NOT EDIT.{{ end }}
|
||||
|
||||
// +build tools
|
||||
|
||||
// Package internal holds a loadable version of the latest schema.
|
||||
package internal
|
||||
|
||||
const Schema = {{ .SchemaSnapshot }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "internal/globalid" }}
|
||||
|
||||
{{ with $.Header }}{{ . }}{{ else }}// Code generated by ent, DO NOT EDIT.{{ end }}
|
||||
|
||||
package internal
|
||||
|
||||
const IncrementStarts = {{ .Annotations.IncrementStarts | json | quote }}
|
||||
{{ end }}
|
||||
189
entc/gen/template/meta.tmpl
Normal file
189
entc/gen/template/meta.tmpl
Normal file
@@ -0,0 +1,189 @@
|
||||
{{/*
|
||||
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 "meta" }}
|
||||
|
||||
{{- with extend $ "Package" $.Package -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "import" $ }}
|
||||
|
||||
const (
|
||||
// Label holds the string label denoting the {{ lower $.Name }} type in the database.
|
||||
Label = "{{ $.Label }}"
|
||||
{{- if $.HasOneFieldID }}
|
||||
// {{ $.ID.Constant }} holds the string denoting the id field in the database.
|
||||
{{ $.ID.Constant }} = "{{ $.ID.StorageKey }}"
|
||||
{{- end }}
|
||||
{{- range $f := $.Fields }}
|
||||
{{- $field := $f.Constant }}
|
||||
// {{ $field }} holds the string denoting the {{ lower $f.Name }} field in the database.
|
||||
{{ $field }} = "{{ $f.StorageKey }}"
|
||||
{{- end }}
|
||||
{{- range $e := $.Edges }}
|
||||
{{- $edge := $e.Constant }}
|
||||
// {{ $edge }} holds the string denoting the {{ lower $e.Name }} edge name in mutations.
|
||||
{{ $edge }} = "{{ $e.Name }}"
|
||||
{{- end }}
|
||||
{{- $tmpl := printf "dialect/%s/meta/constants" $.Storage }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
)
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/meta/variables" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/meta/functions" $.Storage }}
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Has at least one field (not enum) with default value */}}
|
||||
{{ $fields := $.Fields }}{{ if $.HasOneFieldID }}{{ if $.ID.UserDefined }}{{ $fields = append $fields $.ID }}{{ end }}{{ end }}
|
||||
{{ $hasDefault := false }}{{ range $f := $fields }}{{ if and $f.Default (not $f.IsEnum) }}{{ $hasDefault = true }}{{ end }}{{ end }}
|
||||
|
||||
{{/* Generate global variables for hooks, validators and policy checkers */}}
|
||||
{{ if or $hasDefault $.HasUpdateDefault $.HasValidators $.NumHooks $.NumPolicy $.NumInterceptors $.HasValueScanner $.HasBlobFields }}
|
||||
{{- $numHooks := $.NumHooks }}
|
||||
{{- if $.NumPolicy }}
|
||||
{{- $numHooks = add $numHooks 1 }}
|
||||
{{- end }}
|
||||
{{- if or $numHooks $.NumInterceptors }}
|
||||
// Note that the variables below are initialized by the runtime
|
||||
// package on the initialization of the application. Therefore,
|
||||
// it should be imported in the main as follows:
|
||||
//
|
||||
// import _ "{{ $.Config.Package }}/runtime"
|
||||
//
|
||||
{{- end }}
|
||||
var (
|
||||
{{- if $n := $numHooks }}
|
||||
Hooks [{{ $n }}]ent.Hook
|
||||
{{- end }}
|
||||
{{- if $n := $.NumInterceptors }}
|
||||
Interceptors [{{ $n }}]ent.Interceptor
|
||||
{{- end }}
|
||||
{{- if $.NumPolicy }}
|
||||
Policy ent.Policy
|
||||
{{- end }}
|
||||
{{- $fields := $.Fields }}{{ if $.HasOneFieldID }}{{ if $.ID.UserDefined }}{{ $fields = append $fields $.ID }}{{ end }}{{ end }}
|
||||
{{- range $f := $fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- if and $f.Default (not $f.IsEnum) }}
|
||||
{{- $default := $f.DefaultName }}
|
||||
// {{ $default }} holds the default value on creation for the "{{ $f.Name }}" field.
|
||||
{{ $default }} {{ if $f.DefaultFunc }}func() {{ end }}{{ $f.Type }}
|
||||
{{- end }}
|
||||
{{- if $f.UpdateDefault }}
|
||||
{{- $default := $f.UpdateDefaultName }}
|
||||
// {{ $default }} holds the default value on update for the "{{ $f.Name }}" field.
|
||||
{{ $default }} func() {{ $f.Type }}
|
||||
{{- end }}
|
||||
{{- with $f.Validators }}
|
||||
{{- $name := $f.Validator }}
|
||||
{{- $type := $f.Type.Type.String }}
|
||||
{{- if $f.IsJSON }}
|
||||
{{- $type = $f.Type.String }}
|
||||
{{- end }}
|
||||
// {{ $name }} is a validator for the "{{ $f.Name }}" field. It is called by the builders before save.
|
||||
{{ $name }} {{ printf "func (%s) error" $type }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if $f.HasBlobKey }}
|
||||
// {{ $f.BlobKeyName }} generates the blob storage key for the "{{ $f.Name }}" field.
|
||||
{{ $f.BlobKeyName }} ent.BlobKeyFunc
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $.HasValueScanner }}
|
||||
// ValueScanner of all {{ $.Name }} fields.
|
||||
ValueScanner struct {
|
||||
{{- $fields := $.Fields }}{{ if $.HasOneFieldID }}{{ if $.ID.UserDefined }}{{ $fields = append $fields $.ID }}{{ end }}{{ end }}
|
||||
{{- range $f := $fields }}
|
||||
{{- $idscan := and $.HasOneFieldID (eq $f.Name $.ID.Name) }}
|
||||
{{- if and $f.HasValueScanner (or (not $idscan) (eq $.Storage.Name "sql")) }}
|
||||
{{ $f.StructField }} field.TypeValueScanner[{{ $f.Type }}]
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
)
|
||||
{{ end }}
|
||||
|
||||
{{/* define custom type for enum fields */}}
|
||||
{{ range $f := $.EnumFields }}
|
||||
{{ $enum := $f.Type }}
|
||||
{{ $receiver := receiver $f.BuilderField }}
|
||||
{{ if $f.HasGoType }}
|
||||
{{ if $f.Default }}
|
||||
const {{ $f.DefaultName }} {{ $enum }} = "{{ $f.DefaultValue }}"
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{/* Omit the package name from the type. */}}
|
||||
{{ $enum = trimPackage $f.Type.String $.Package }}
|
||||
// {{ $enum }} defines the type for the "{{ $f.Name }}" enum field.
|
||||
type {{ $enum }} string
|
||||
|
||||
{{- if $f.Default }}
|
||||
{{- /* Find the enum that holds the default value. */ -}}
|
||||
{{- range $e := $f.Enums }}
|
||||
{{- if eq $e.Value $f.DefaultValue }}
|
||||
// {{ $e.Name }} is the default value of the {{ $enum }} enum.
|
||||
const {{ $f.DefaultName }} = {{ $e.Name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
// {{ $enum }} values.
|
||||
const (
|
||||
{{- range $e := $f.Enums }}
|
||||
{{ $e.Name }} {{ $enum }} = "{{ $e.Value }}"
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
func ({{ $receiver }} {{ $enum }}) String() string {
|
||||
return string({{ $receiver }})
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ $name := $f.Validator }}
|
||||
// {{ $name }} is a validator for the "{{ $f.Name }}" field enum values. It is called by the builders before save.
|
||||
func {{ $name }}({{ $receiver }} {{ $enum }}) error {
|
||||
switch {{ $receiver }}{{ if $f.Type.Stringer }}.String(){{ end }} {
|
||||
case {{ range $i, $e := $f.Enums }}{{ if ne $i 0 }},{{ end }}{{ if $f.HasGoType }}"{{ $e.Value }}"{{ else }}{{ $e.Name }}{{ end }}{{ end }}:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("{{ $.Package }}: invalid enum value for {{ $f.Name }} field: %q", {{ $receiver }})
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ $tmpl = printf "dialect/%s/meta/order" $.Storage }}
|
||||
// OrderOption defines the ordering options for the {{ $.Name }} queries.
|
||||
type OrderOption func({{ $.Config.Storage.Builder }})
|
||||
{{ if hasTemplate $tmpl }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "meta/additional" $ }}
|
||||
|
||||
{{ with $tmpls := matchTemplate "meta/additional/*" }}
|
||||
{{ range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{/* A template that can be overridden in order to add additional code for the type package. */}}
|
||||
{{ define "meta/additional" }}{{ end }}
|
||||
77
entc/gen/template/migrate/migrate.tmpl
Normal file
77
entc/gen/template/migrate/migrate.tmpl
Normal file
@@ -0,0 +1,77 @@
|
||||
{{/*
|
||||
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" }}
|
||||
|
||||
{{- with extend $ "Package" "migrate" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
// WithGlobalUniqueID sets the universal ids options to the migration.
|
||||
// If this option is enabled, ent migration will allocate a 1<<32 range
|
||||
// for the ids of each entity (table).
|
||||
// Note that this option cannot be applied on tables that already exist.
|
||||
WithGlobalUniqueID = schema.WithGlobalUniqueID
|
||||
// WithDropColumn sets the drop column option to the migration.
|
||||
// If this option is enabled, ent migration will drop old columns
|
||||
// that were used for both fields and edges. This defaults to false.
|
||||
WithDropColumn = schema.WithDropColumn
|
||||
// WithDropIndex sets the drop index option to the migration.
|
||||
// If this option is enabled, ent migration will drop old indexes
|
||||
// that were defined in the schema. This defaults to false.
|
||||
// Note that unique constraints are defined using `UNIQUE INDEX`,
|
||||
// and therefore, it's recommended to enable this option to get more
|
||||
// flexibility in the schema changes.
|
||||
WithDropIndex = schema.WithDropIndex
|
||||
// WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true.
|
||||
WithForeignKeys = schema.WithForeignKeys
|
||||
)
|
||||
|
||||
// Schema is the API for creating, migrating and dropping a schema.
|
||||
type Schema struct {
|
||||
drv dialect.Driver
|
||||
}
|
||||
|
||||
// NewSchema creates a new schema client.
|
||||
func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
|
||||
|
||||
// Create creates all schema resources.
|
||||
func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error {
|
||||
return Create(ctx, s, Tables, opts...)
|
||||
}
|
||||
|
||||
// Create creates all table resources using the given schema driver.
|
||||
func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error {
|
||||
migrate, err := schema.NewMigrate(s.drv, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ent/migrate: %w", err)
|
||||
}
|
||||
return migrate.Create(ctx, tables...)
|
||||
}
|
||||
|
||||
{{ if $.Config.FeatureEnabled "sql/versioned-migration" }}{{ template "migrate/diff" $ }}{{ end }}
|
||||
|
||||
// WriteTo writes the schema changes to w instead of running them against the database.
|
||||
//
|
||||
// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error {
|
||||
return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv,}}, Tables, opts...)
|
||||
}
|
||||
{{ end }}
|
||||
245
entc/gen/template/migrate/schema.tmpl
Normal file
245
entc/gen/template/migrate/schema.tmpl
Normal file
@@ -0,0 +1,245 @@
|
||||
{{/*
|
||||
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 "schema" }}
|
||||
|
||||
{{- with extend $ "Package" "migrate" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
{{ range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- end }}
|
||||
|
||||
"entgo.io/ent/dialect/entsql"
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
var (
|
||||
{{- range $t := $.Tables }}
|
||||
{{- /* find type node in the graph of this table, if exists */}}
|
||||
{{- $node := index $.Nodes 0 }}
|
||||
{{- range $n := $.Nodes }}{{ if eq $t.Name $n.Table }}{{ $node = $n }}{{ end }}{{ end }}
|
||||
{{- $columns := pascal $t.Name | printf "%sColumns" }}
|
||||
// {{ $columns }} holds the columns for the "{{ $t.Name }}" table.
|
||||
{{ $columns }} = []*schema.Column{
|
||||
{{- range $c := $t.Columns }}
|
||||
{ Name: "{{ $c.Name }}", Type: field.{{ $c.Type.ConstName }},
|
||||
{{- if $c.Unique }} Unique: true,{{ end }}
|
||||
{{- if $c.Increment }} Increment: true,{{ end }}
|
||||
{{- if $c.Nullable }} Nullable: {{ $c.Nullable }},{{ end }}
|
||||
{{- with $c.Size }} Size: {{ . }},{{ end }}
|
||||
{{- with $c.Comment }} Comment: "{{ $c.Comment }}",{{ end }}
|
||||
{{- with $c.Attr }} Attr: "{{ . }}",{{ end }}
|
||||
{{- with $c.Enums }} Enums: []string{ {{ range $e := . }}"{{ $e }}",{{ end }} },{{ end }}
|
||||
{{- if not (isNil $c.Default) -}}
|
||||
{{- $t := printf "%T" $c.Default -}}
|
||||
{{- if eq $t "schema.Expr" -}}
|
||||
Default: schema.Expr("{{ $c.Default }}"),
|
||||
{{- else if eq $t "map[string]schema.Expr" -}}
|
||||
Default: map[string]schema.Expr{ {{ range $k := keys $c.Default }} "{{ $k }}": "{{ index $c.Default $k }}",{{ end }} },
|
||||
{{- else -}}
|
||||
Default: {{ quote $c.Default }},
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
{{- if $c.Collation }} Collation: "{{ $c.Collation }}",{{ end }}
|
||||
{{- with $c.SchemaType }} SchemaType: map[string]string{ {{ range $k := keys . }}"{{ $k }}": "{{ index $c.SchemaType $k }}",{{ end }}}{{ end }}},
|
||||
{{- end }}
|
||||
}
|
||||
{{- $table := pascal $t.Name | printf "%sTable" }}
|
||||
// {{ $table }} holds the schema information for the "{{ $t.Name }}" table.
|
||||
{{ $table }} = &schema.Table{
|
||||
Name: "{{ $t.Name }}",
|
||||
{{- with $t.Comment }}
|
||||
Comment: "{{ . }}",
|
||||
{{- end }}
|
||||
Columns: {{ $columns }},
|
||||
PrimaryKey: []*schema.Column{
|
||||
{{- range $pk := $t.PrimaryKey }}
|
||||
{{- range $i, $c := $t.Columns }}
|
||||
{{- if eq $pk.Name $c.Name }}{{ $columns }}[{{ $i }}],{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- with $fks := $t.ForeignKeys }}
|
||||
ForeignKeys: []*schema.ForeignKey{
|
||||
{{- range $fk := $fks }}
|
||||
{
|
||||
Symbol: "{{ $fk.Symbol }}",
|
||||
Columns: []*schema.Column{
|
||||
{{- range $c1 := $fk.Columns }}
|
||||
{{- range $i, $c2 := $t.Columns }}
|
||||
{{- if eq $c1.Name $c2.Name }}{{ $columns }}[{{ $i }}],{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- /* postpone referencing to avoid typechecking loops */}}
|
||||
RefColumns: []*schema.Column{
|
||||
{{- range $c1 := $fk.RefColumns }}
|
||||
{{- range $i, $c2 := $fk.RefTable.Columns }}
|
||||
{{- if eq $c1.Name $c2.Name }}{{ pascal $fk.RefTable.Name | printf "%sColumns" }}[{{ $i }}],{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- with $fk.OnUpdate.ConstName }}
|
||||
OnUpdate: schema.{{ . }},
|
||||
{{- end }}
|
||||
{{- with $fk.OnDelete.ConstName }}
|
||||
OnDelete: schema.{{ . }},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- if $t.Indexes }}
|
||||
Indexes: []*schema.Index{
|
||||
{{- range $idx := $t.Indexes }}
|
||||
{
|
||||
Name: "{{ $idx.Name }}",
|
||||
Unique: {{ $idx.Unique }},
|
||||
Columns: []*schema.Column{
|
||||
{{- range $c1 := $idx.Columns }}
|
||||
{{- range $i, $c2 := $t.Columns }}
|
||||
{{- if eq $c1.Name $c2.Name }}{{ $columns }}[{{ $i }}],{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- with $ant := $idx.Annotation }}
|
||||
Annotation: &entsql.IndexAnnotation{
|
||||
{{- with $ant.Prefix }}
|
||||
Prefix: {{ . }},
|
||||
{{- end }}
|
||||
{{- with $keys := keys $ant.PrefixColumns }}
|
||||
PrefixColumns: map[string]uint{
|
||||
{{- range $k := $keys }}
|
||||
{{- /* Use the column reference instead of using raw string. */}}
|
||||
{{- range $i, $c := $t.Columns }}
|
||||
{{- if eq $k $c.Name }}
|
||||
{{ $columns }}[{{ $i }}].Name: {{ index $ant.PrefixColumns $k }},
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- with $ant.OpClass }}
|
||||
OpClass: "{{ . }}",
|
||||
{{- end }}
|
||||
{{- with $keys := keys $ant.OpClassColumns }}
|
||||
OpClassColumns: map[string]string{
|
||||
{{- range $k := $keys }}
|
||||
{{- /* Use the column reference instead of using raw string. */}}
|
||||
{{- range $i, $c := $t.Columns }}
|
||||
{{- if eq $k $c.Name }}
|
||||
{{ $columns }}[{{ $i }}].Name: "{{ index $ant.OpClassColumns $k }}",
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- with $ant.Desc }}
|
||||
Desc: {{ . }},
|
||||
{{- end }}
|
||||
{{- with $keys := keys $ant.DescColumns }}
|
||||
DescColumns: map[string]bool{
|
||||
{{- range $k := $keys }}
|
||||
{{- /* Use the column reference instead of using raw string. */}}
|
||||
{{- range $i, $c := $t.Columns }}
|
||||
{{- if eq $k $c.Name }}
|
||||
{{ $columns }}[{{ $i }}].Name: {{ index $ant.DescColumns $k }},
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- with $ant.IncludeColumns }}
|
||||
IncludeColumns: []string{
|
||||
{{- range $ic := . }}
|
||||
{{- range $i, $c := $t.Columns }}
|
||||
{{- if eq $ic $c.Name }}
|
||||
{{ $columns }}[{{ $i }}].Name,
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- with $ant.Type }}
|
||||
Type: "{{ . }}",
|
||||
{{- end }}
|
||||
{{- with $keys := keys $ant.Types }}
|
||||
Types: map[string]string{
|
||||
{{- range $k := $keys }}
|
||||
"{{ $k }}": "{{ index $ant.Types $k }}",
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
{{- with $ant.Where }}
|
||||
Where: {{ quote . }},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
},
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
// Tables holds all the tables in the schema.
|
||||
Tables = []*schema.Table{
|
||||
{{- range $t := $.Tables }}
|
||||
{{ pascal $t.Name | printf "%sTable" }},
|
||||
{{- end }}
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
{{- range $t := $.Tables }}
|
||||
{{- $table := pascal $t.Name | printf "%sTable" }}
|
||||
{{- range $i, $fk := $t.ForeignKeys }}
|
||||
{{ $table }}.ForeignKeys[{{ $i }}].RefTable = {{ pascal $fk.RefTable.Name | printf "%sTable" }}
|
||||
{{- end }}
|
||||
{{- with $ant := $t.Annotation }}
|
||||
{{- if not (allZero $ant.Table $ant.Charset $ant.Collation $ant.Options $ant.Check $ant.IncrementStart $ant.Incremental $ant.Checks) }}
|
||||
{{ $table }}.Annotation = &entsql.Annotation{
|
||||
{{- with $ant.Table }}
|
||||
Table: "{{ . }}",
|
||||
{{- end }}
|
||||
{{- with $ant.Charset }}
|
||||
Charset: "{{ . }}",
|
||||
{{- end }}
|
||||
{{- with $ant.Collation }}
|
||||
Collation: "{{ . }}",
|
||||
{{- end }}
|
||||
{{- with $ant.Options }}
|
||||
Options: {{ quote . }},
|
||||
{{- end }}
|
||||
{{- with $ant.Check }}
|
||||
Check: {{ quote . }},
|
||||
{{- end }}
|
||||
{{- with $ant.IncrementStart }}
|
||||
IncrementStart: func(i int) *int { return &i }({{ . }}),
|
||||
{{- end }}
|
||||
}
|
||||
{{- with $ant.Incremental }}
|
||||
{{ $table }}.Annotation.Incremental = new(bool)
|
||||
*{{ $table }}.Annotation.Incremental = {{ with indirect . }}true{{ else }}false{{ end }}
|
||||
{{- end }}
|
||||
{{- with $keys := keys $ant.Checks }}
|
||||
{{ $table }}.Annotation.Checks = map[string]string{
|
||||
{{- range $k := $keys }}
|
||||
"{{ $k }}": {{ index $ant.Checks $k | quote }},
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
654
entc/gen/template/mutation.tmpl
Normal file
654
entc/gen/template/mutation.tmpl
Normal file
@@ -0,0 +1,654 @@
|
||||
{{/*
|
||||
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 "mutation" }}
|
||||
|
||||
{{- with extend $ "Package" $.Package -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "import" $ }}
|
||||
|
||||
{{/* localType strips the current package prefix from type strings, so that
|
||||
types defined in this package (e.g. enums) are referenced without the
|
||||
package qualifier. */}}
|
||||
{{ $pkgPrefix := print $.Package "." }}
|
||||
{{ $blobSupported := hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
|
||||
// Mutation represents an operation that mutates the {{ $.Name }} nodes in the graph.
|
||||
type Mutation struct {
|
||||
op ent.Op
|
||||
typ string
|
||||
{{- range $f := $.MutationFields }}
|
||||
{{ $f.BuilderField }} *{{ replace $f.Type.String $pkgPrefix "" }}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
add{{ $f.BuilderField }} *{{ replace (print $f.SignedType) $pkgPrefix "" }}
|
||||
{{- end }}
|
||||
{{- if $f.SupportsMutationAppend }}
|
||||
append{{ $f.BuilderField }} {{ replace $f.Type.String $pkgPrefix "" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $f.BuilderField }} io.Reader
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
clearedFields map[string]struct{}
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- if $e.Unique }}
|
||||
{{ $e.BuilderField }} *{{ $e.Type.ID.Type }}
|
||||
{{- else }}
|
||||
{{ $e.BuilderField }} map[{{ $e.Type.ID.Type }}]struct{}
|
||||
removed{{ $e.BuilderField }} map[{{ $e.Type.ID.Type }}]struct{}
|
||||
{{- end }}
|
||||
cleared{{ $e.BuilderField }} bool
|
||||
{{- end }}
|
||||
predicates []predicate.{{ $.Name }}
|
||||
{{- /* Additional fields can be added by user templates. */}}
|
||||
{{- with $tmpls := matchTemplate "mutation/fields/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// NewMutation creates a new Mutation for the {{ $.Name }} entity.
|
||||
func NewMutation(op ent.Op) *Mutation {
|
||||
return &Mutation{
|
||||
op: op,
|
||||
typ: "{{ $.Name }}",
|
||||
clearedFields: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Predicates returns the list of predicates set on the mutation.
|
||||
func (m *Mutation) Predicates() []predicate.{{ $.Name }} {
|
||||
return m.predicates
|
||||
}
|
||||
|
||||
{{ range $f := $.Fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $const := $f.Constant }}
|
||||
{{ $type := replace $f.Type.String $pkgPrefix "" }}
|
||||
{{ $p := receiver $f.Type.String }}{{ if eq $p "m" }} {{ $p = "value" }} {{ end }}
|
||||
|
||||
{{ $func := $f.MutationSet }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $func }}({{ $p }} {{ $type }}) {
|
||||
m.{{ $f.BuilderField }} = &{{ $p }}
|
||||
{{- /* Setting numeric type override previous calls to Add. */}}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
m.add{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
{{- /* Setting JSON type override previous calls to Append. */}}
|
||||
{{- if $f.SupportsMutationAppend }}
|
||||
m.append{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// {{ $f.MutationGet }} returns the value of the "{{ $f.Name }}" field in the mutation.
|
||||
func (m *Mutation) {{ $f.MutationGet }}() (r {{ $type }}, exists bool) {
|
||||
v := m.{{ $f.BuilderField }}
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
{{ if $f.SupportsMutationAdd }}
|
||||
{{ $signedType := replace (print $f.SignedType) $pkgPrefix "" }}
|
||||
// {{ $f.MutationAdd }} adds {{ $p }} to the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationAdd }}({{ $p }} {{ $signedType }}) {
|
||||
{{- $structField := print "m.add" $f.BuilderField }}
|
||||
if {{ $structField }} != nil {
|
||||
{{ $f.MutationAddAssignExpr $structField $p }}
|
||||
} else {
|
||||
{{ $structField }} = &{{ $p }}
|
||||
}
|
||||
}
|
||||
|
||||
// {{ $f.MutationAdded }} returns the value that was added to the "{{ $f.Name }}" field in this mutation.
|
||||
func (m *Mutation) {{ $f.MutationAdded }}() (r {{ $signedType }}, exists bool) {
|
||||
v := m.add{{ $f.BuilderField }}
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $f.SupportsMutationAppend }}
|
||||
{{- $structField := print "m.append" $f.BuilderField }}
|
||||
// {{ $f.MutationAppend }} adds {{ $p }} to the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationAppend }}({{ $p }} {{ $type }}) {
|
||||
{{ $structField }} = append({{ $structField }}, {{ $p }}...)
|
||||
}
|
||||
|
||||
// {{ $f.MutationAppended }} returns the list of values that were appended to the "{{ $f.Name }}" field in this mutation.
|
||||
func (m *Mutation) {{ $f.MutationAppended }}() ({{ $type }}, bool) {
|
||||
if len({{ $structField }}) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return {{ $structField }}, true
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if $f.Optional }}
|
||||
// {{ $f.MutationClear }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationClear }}() {
|
||||
m.{{ $f.BuilderField }} = nil
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
m.add{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
{{- if $f.SupportsMutationAppend }}
|
||||
m.append{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
m.clearedFields[{{ $const }}] = struct{}{}
|
||||
}
|
||||
|
||||
// {{ $f.MutationCleared }} returns if the "{{ $f.Name }}" field was cleared in this mutation.
|
||||
func (m *Mutation) {{ $f.MutationCleared }}() bool {
|
||||
_, ok := m.clearedFields[{{ $const }}]
|
||||
return ok
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// {{ $f.MutationReset }} resets all changes to the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationReset }}() {
|
||||
m.{{ $f.BuilderField }} = nil
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
m.add{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
{{- if $f.SupportsMutationAppend }}
|
||||
m.append{{ $f.BuilderField }} = nil
|
||||
{{- end }}
|
||||
{{- if $f.Optional }}
|
||||
delete(m.clearedFields, {{ $const }})
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{- if hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
{{- range $f := $.BlobFields }}
|
||||
{{- if not $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $const := $f.Constant }}
|
||||
{{ $func := print "Set" $f.StructField }}
|
||||
// {{ $func }} sets the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $func }}(r io.Reader) {
|
||||
m.{{ $f.BuilderField }} = r
|
||||
}
|
||||
|
||||
// {{ $f.StructField }} returns the value of the "{{ $f.Name }}" field in the mutation.
|
||||
func (m *Mutation) {{ $f.StructField }}() (r io.Reader, exists bool) {
|
||||
v := m.{{ $f.BuilderField }}
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
{{- if $f.Optional }}
|
||||
// {{ $f.MutationClear }} clears the value of the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationClear }}() {
|
||||
m.{{ $f.BuilderField }} = nil
|
||||
m.clearedFields[{{ $const }}] = struct{}{}
|
||||
}
|
||||
|
||||
// {{ $f.MutationCleared }} returns if the "{{ $f.Name }}" field was cleared in this mutation.
|
||||
func (m *Mutation) {{ $f.MutationCleared }}() bool {
|
||||
_, ok := m.clearedFields[{{ $const }}]
|
||||
return ok
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// {{ $f.MutationReset }} resets all changes to the "{{ $f.Name }}" field.
|
||||
func (m *Mutation) {{ $f.MutationReset }}() {
|
||||
m.{{ $f.BuilderField }} = nil
|
||||
{{- if $f.Optional }}
|
||||
delete(m.clearedFields, {{ $const }})
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ range $e := $.EdgesWithID }}
|
||||
{{ $op := "add" }}{{ $idsFunc := $e.MutationAdd }}{{ if $e.Unique }}{{ $op = "set" }}{{ $idsFunc = $e.MutationSet }}{{ end }}
|
||||
{{/* Check if this setter was already defined by the field-setters (e.g. edge-field with the same name). */}}
|
||||
{{ $withSetGet := not $e.HasFieldSetter }}
|
||||
{{ if $withSetGet }}
|
||||
// {{ $idsFunc }} {{ $op }}s the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity by id{{ if not $e.Unique }}s{{ end }}.
|
||||
func (m *Mutation) {{ $idsFunc }}({{ if $e.Unique }}id{{ else }}ids ...{{ end }} {{ $e.Type.ID.Type }}) {
|
||||
{{- if $e.Unique }}
|
||||
m.{{ $e.BuilderField }} = &id
|
||||
{{- else }}
|
||||
if m.{{ $e.BuilderField }} == nil {
|
||||
m.{{ $e.BuilderField }} = make(map[{{ $e.Type.ID.Type }}]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
m.{{ $e.BuilderField }}[ids[i]] = struct{}{}
|
||||
}
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ $func := $e.MutationClear }}
|
||||
// {{ $func }} clears the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity.
|
||||
func (m *Mutation) {{ $func }}() {
|
||||
m.cleared{{ $e.BuilderField }} = true
|
||||
{{- if $e.Field }}
|
||||
{{- $const := $e.Field.Constant }}
|
||||
m.clearedFields[{{ $const }}] = struct{}{}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ $func = $e.MutationCleared }}
|
||||
// {{ $func }} reports if the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity was cleared.
|
||||
func (m *Mutation) {{ $func }}() bool {
|
||||
return {{ with $e.Field }}{{ if .Optional }}m.{{ .MutationCleared }}() || {{ end }}{{ end }}m.cleared{{ $e.BuilderField }}
|
||||
}
|
||||
|
||||
{{ if not $e.Unique }}
|
||||
{{ $p := lower (printf "%.1s" $e.Type.Name) }}
|
||||
// {{ $e.MutationRemove }} removes the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity by IDs.
|
||||
func (m *Mutation) {{ $e.MutationRemove }}(ids ...{{ $e.Type.ID.Type }}) {
|
||||
if m.removed{{ $e.BuilderField }} == nil {
|
||||
m.removed{{ $e.BuilderField }} = make(map[{{ $e.Type.ID.Type }}]struct{})
|
||||
}
|
||||
for i := range ids {
|
||||
delete(m.{{ $e.BuilderField }}, ids[i])
|
||||
m.removed{{ $e.BuilderField }}[ids[i]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
{{ $func := print "Removed" $e.StructField }}
|
||||
// {{ $func }} returns the removed IDs of the "{{ $e.Name }}" edge to the {{ $e.Type.Name }} entity.
|
||||
func (m *Mutation) {{ $func }}IDs() (ids []{{ $e.Type.ID.Type }}) {
|
||||
for id := range m.removed{{ $e.BuilderField }} {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
{{ else if and $e.Unique $withSetGet }}
|
||||
// {{ $e.StructField }}ID returns the "{{ $e.Name }}" edge ID in the mutation.
|
||||
func (m *Mutation) {{ $e.StructField }}ID() (id {{ $e.Type.ID.Type }}, exists bool) {
|
||||
if m.{{ $e.BuilderField }} != nil {
|
||||
return *m.{{ $e.BuilderField }}, true
|
||||
}
|
||||
return
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// {{ $e.StructField }}IDs returns the "{{ $e.Name }}" edge IDs in the mutation.
|
||||
{{- if $e.Unique }}
|
||||
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
|
||||
// {{ $e.StructField }}ID instead. It exists only for internal usage by the builders.
|
||||
{{- end }}
|
||||
func (m *Mutation) {{ $e.StructField }}IDs() (ids []{{ $e.Type.ID.Type }}) {
|
||||
{{- if $e.Unique }}
|
||||
if id := m.{{ $e.BuilderField }}; id != nil {
|
||||
ids = append(ids, *id)
|
||||
}
|
||||
{{- else }}
|
||||
for id := range m.{{ $e.BuilderField }} {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
{{- end}}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
{{ $func = $e.MutationReset }}
|
||||
// {{ $func }} resets all changes to the "{{ $e.Name }}" edge.
|
||||
func (m *Mutation) {{ $func }}() {
|
||||
m.{{ $e.BuilderField }} = nil
|
||||
m.cleared{{ $e.BuilderField }} = false
|
||||
{{- if not $e.Unique }}
|
||||
m.removed{{ $e.BuilderField }} = nil
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
// Where appends a list predicates to the Mutation builder.
|
||||
func (m *Mutation) Where(ps ...predicate.{{ $.Name }}) {
|
||||
m.predicates = append(m.predicates, ps...)
|
||||
}
|
||||
|
||||
// WhereP appends storage-level predicates to the Mutation builder. Using this method,
|
||||
// users can use type-assertion to append predicates that do not depend on any generated package.
|
||||
func (m *Mutation) WhereP(ps ...func({{ $.Storage.Builder }})) {
|
||||
p := make([]predicate.{{ $.Name }}, len(ps))
|
||||
for i := range ps {
|
||||
p[i] = ps[i]
|
||||
}
|
||||
m.Where(p...)
|
||||
}
|
||||
|
||||
// Op returns the operation name.
|
||||
func (m *Mutation) Op() ent.Op {
|
||||
return m.op
|
||||
}
|
||||
|
||||
// SetOp allows setting the mutation operation.
|
||||
func (m *Mutation) SetOp(op ent.Op) {
|
||||
m.op = op
|
||||
}
|
||||
|
||||
// Type returns the node type of this mutation ({{ $.Name }}).
|
||||
func (m *Mutation) Type() string {
|
||||
return m.typ
|
||||
}
|
||||
|
||||
// Fields returns all fields that were changed during this mutation. Note that in
|
||||
// order to get all numeric fields that were incremented/decremented, call
|
||||
// AddedFields().
|
||||
func (m *Mutation) Fields() []string {
|
||||
fields := make([]string, 0, {{ len $.Fields }})
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
if m.{{ $f.BuilderField }} != nil {
|
||||
fields = append(fields, {{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns the value of a field with the given name. The second boolean
|
||||
// return value indicates that this field was not set, or was not defined in the
|
||||
// schema.
|
||||
func (m *Mutation) Field(name string) (ent.Value, bool) {
|
||||
{{- with $.Fields }}
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
case {{ $f.Constant }}:
|
||||
return m.{{ $f.MutationGet }}()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// OldField returns the old value of the field from the database. An error is
|
||||
// returned if the mutation operation is not UpdateOne, or the query to the
|
||||
// database failed.
|
||||
func (m *Mutation) OldField(ctx context.Context, name string) (ent.Value, error) {
|
||||
{{- if $.HasCompositeID }}
|
||||
return nil, errors.New("edge schema {{ $.Name }} does not support getting old values")
|
||||
{{- else }}
|
||||
return nil, fmt.Errorf("unknown {{ $.Name }} field %s", name)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// SetField sets the value of a field with the given name. It returns an error if
|
||||
// the field is not defined in the schema, or if the type mismatched the field
|
||||
// type.
|
||||
func (m *Mutation) SetField(name string, value ent.Value) error {
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if not $f.IsBlobLazy }}
|
||||
{{- $type := replace $f.Type.String $pkgPrefix "" }}
|
||||
case {{ $f.Constant }}:
|
||||
v, ok := value.({{ $type }})
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.{{ $f.MutationSet }}(v)
|
||||
return nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
return fmt.Errorf("unknown {{ $.Name }} field %s", name)
|
||||
}
|
||||
|
||||
// AddedFields returns all numeric fields that were incremented/decremented during
|
||||
// this mutation.
|
||||
func (m *Mutation) AddedFields() []string {
|
||||
{{- if $.HasNumeric }}
|
||||
var fields []string
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
if m.add{{ $f.BuilderField }} != nil {
|
||||
fields = append(fields, {{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return fields
|
||||
{{- else }}
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// AddedField returns the numeric value that was incremented/decremented on a field
|
||||
// with the given name. The second boolean return value indicates that this field
|
||||
// was not set, or was not defined in the schema.
|
||||
func (m *Mutation) AddedField(name string) (ent.Value, bool) {
|
||||
{{- if $.HasNumeric }}
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
case {{ $f.Constant }}:
|
||||
return m.Added{{ $f.StructField }}()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// AddField adds the value to the field with the given name. It returns an error if
|
||||
// the field is not defined in the schema, or if the type mismatched the field
|
||||
// type.
|
||||
func (m *Mutation) AddField(name string, value ent.Value) error {
|
||||
{{- with $.Fields }}
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.SupportsMutationAdd }}
|
||||
{{- $signedType := replace (print $f.SignedType) $pkgPrefix "" }}
|
||||
case {{ $f.Constant }}:
|
||||
v, ok := value.({{ $signedType }})
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.Add{{ $f.StructField }}(v)
|
||||
return nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return fmt.Errorf("unknown {{ $.Name }} numeric field %s", name)
|
||||
}
|
||||
|
||||
// ClearedFields returns all nullable fields that were cleared during this
|
||||
// mutation.
|
||||
func (m *Mutation) ClearedFields() []string {
|
||||
{{- if $.HasOptional }}
|
||||
var fields []string
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if $f.Optional }}
|
||||
if m.FieldCleared({{ $f.Constant }}) {
|
||||
fields = append(fields, {{ $f.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return fields
|
||||
{{- else }}
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
// FieldCleared returns a boolean indicating if a field with the given name was
|
||||
// cleared in this mutation.
|
||||
func (m *Mutation) FieldCleared(name string) bool {
|
||||
_, ok := m.clearedFields[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ClearField clears the value of the field with the given name. It returns an
|
||||
// error if the field is not defined in the schema.
|
||||
func (m *Mutation) ClearField(name string) error {
|
||||
{{- if $.HasOptional }}
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if and $f.IsBlobLazy (not $blobSupported) }}{{ continue }}{{ end }}
|
||||
{{- if $f.Optional }}
|
||||
case {{ $f.Constant }}:
|
||||
m.Clear{{ $f.StructField }}()
|
||||
return nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return fmt.Errorf("unknown {{ $.Name }} nullable field %s", name)
|
||||
}
|
||||
|
||||
|
||||
// ResetField resets all changes in the mutation for the field with the given name.
|
||||
// It returns an error if the field is not defined in the schema.
|
||||
func (m *Mutation) ResetField(name string) error {
|
||||
{{- with $.Fields }}
|
||||
switch name {
|
||||
{{- range $f := $.Fields }}
|
||||
{{- if and $f.IsBlobLazy (not $blobSupported) }}{{ continue }}{{ end }}
|
||||
case {{ $f.Constant }}:
|
||||
m.{{ $f.MutationReset }}()
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return fmt.Errorf("unknown {{ $.Name }} field %s", name)
|
||||
}
|
||||
|
||||
// AddedEdges returns all edge names that were set/added in this mutation.
|
||||
func (m *Mutation) AddedEdges() []string {
|
||||
edges := make([]string, 0, {{ len $.EdgesWithID }})
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
if m.{{ $e.BuilderField }} != nil {
|
||||
edges = append(edges, {{ $e.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
return edges
|
||||
}
|
||||
|
||||
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
|
||||
// name in this mutation.
|
||||
func (m *Mutation) AddedIDs(name string) []ent.Value {
|
||||
{{- with $.EdgesWithID }}
|
||||
switch name {
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
case {{ $e.Constant }}:
|
||||
{{- if $e.Unique }}
|
||||
if id := m.{{ $e.BuilderField }}; id != nil {
|
||||
return []ent.Value{*id}
|
||||
}
|
||||
{{- else }}
|
||||
ids := make([]ent.Value, 0, len(m.{{ $e.BuilderField }}))
|
||||
for id := range m.{{ $e.BuilderField }} {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovedEdges returns all edge names that were removed in this mutation.
|
||||
func (m *Mutation) RemovedEdges() []string {
|
||||
edges := make([]string, 0, {{ len $.EdgesWithID }})
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- if not $e.Unique }}
|
||||
if m.removed{{ $e.BuilderField }} != nil {
|
||||
edges = append(edges, {{ $e.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return edges
|
||||
}
|
||||
|
||||
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
|
||||
// the given name in this mutation.
|
||||
func (m *Mutation) RemovedIDs(name string) []ent.Value {
|
||||
{{- $edges := list }}{{ range $e := $.EdgesWithID }}{{ if not $e.Unique }}{{ $edges = append $edges $e }}{{ end }}{{ end }}
|
||||
{{- with $edges }}
|
||||
switch name {
|
||||
{{- range $e := $edges }}
|
||||
case {{ $e.Constant }}:
|
||||
ids := make([]ent.Value, 0, len(m.removed{{ $e.BuilderField }}))
|
||||
for id := range m.removed{{ $e.BuilderField }} {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearedEdges returns all edge names that were cleared in this mutation.
|
||||
func (m *Mutation) ClearedEdges() []string {
|
||||
edges := make([]string, 0, {{ len $.EdgesWithID }})
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
if m.cleared{{ $e.BuilderField }} {
|
||||
edges = append(edges, {{ $e.Constant }})
|
||||
}
|
||||
{{- end }}
|
||||
return edges
|
||||
}
|
||||
|
||||
// EdgeCleared returns a boolean which indicates if the edge with the given name
|
||||
// was cleared in this mutation.
|
||||
func (m *Mutation) EdgeCleared(name string) bool {
|
||||
{{- with $.EdgesWithID }}
|
||||
switch name {
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
case {{ $e.Constant }}:
|
||||
return m.cleared{{ $e.BuilderField }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearEdge clears the value of the edge with the given name. It returns an error
|
||||
// if that edge is not defined in the schema.
|
||||
func (m *Mutation) ClearEdge(name string) error {
|
||||
{{- with $.EdgesWithID }}
|
||||
switch name {
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
{{- if $e.Unique }}
|
||||
case {{ $e.Constant }}:
|
||||
m.{{ $e.MutationClear }}()
|
||||
return nil
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return fmt.Errorf("unknown {{ $.Name }} unique edge %s", name)
|
||||
}
|
||||
|
||||
// ResetEdge resets all changes to the edge with the given name in this mutation.
|
||||
// It returns an error if the edge is not defined in the schema.
|
||||
func (m *Mutation) ResetEdge(name string) error {
|
||||
{{- with $.EdgesWithID }}
|
||||
switch name {
|
||||
{{- range $e := $.EdgesWithID }}
|
||||
case {{ $e.Constant }}:
|
||||
m.{{ $e.MutationReset }}()
|
||||
return nil
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
return fmt.Errorf("unknown {{ $.Name }} edge %s", name)
|
||||
}
|
||||
|
||||
{{ end }}
|
||||
39
entc/gen/template/predicate.tmpl
Normal file
39
entc/gen/template/predicate.tmpl
Normal file
@@ -0,0 +1,39 @@
|
||||
{{/*
|
||||
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 "predicate" }}
|
||||
|
||||
{{- with extend $ "Package" "predicate" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"entgo.io/ent/dialect/gremlin/graph/dsl"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
)
|
||||
|
||||
{{ range $n := $.Nodes -}}
|
||||
// {{ $n.Name }} is the predicate function for {{ $n.Package }} builders.
|
||||
type {{ $n.Name }} func({{ $.Storage.Builder }})
|
||||
|
||||
{{- with $n.HasValueScanner }}
|
||||
// {{ $n.Name }}OrErr calls the predicate only if the error is not nit.
|
||||
func {{ $n.Name }}OrErr(p {{ $n.Name }}, err error) {{ $n.Name }} {
|
||||
return func(s {{ $.Storage.Builder }}) {
|
||||
if err != nil {
|
||||
s.AddError(err)
|
||||
return
|
||||
}
|
||||
p(s)
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
69
entc/gen/template/privacy/filter.tmpl
Normal file
69
entc/gen/template/privacy/filter.tmpl
Normal file
@@ -0,0 +1,69 @@
|
||||
{{/*
|
||||
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 "privacy/filter" }}
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
type (
|
||||
// Filter is the interface that wraps the Where function
|
||||
// for filtering nodes in queries and mutations.
|
||||
Filter interface {
|
||||
// Where applies a filter on the executed query/mutation.
|
||||
Where(entql.P)
|
||||
}
|
||||
|
||||
// The FilterFunc type is an adapter that allows the use of ordinary
|
||||
// functions as filters for query and mutation types.
|
||||
FilterFunc func(context.Context, Filter) error
|
||||
)
|
||||
|
||||
// EvalQuery calls f(ctx, q) if the query implements the Filter interface, otherwise it is denied.
|
||||
func (f FilterFunc) EvalQuery(ctx context.Context, q {{ $pkg }}.Query) error {
|
||||
fr, err := queryFilter(q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(ctx, fr)
|
||||
}
|
||||
|
||||
// EvalMutation calls f(ctx, q) if the mutation implements the Filter interface, otherwise it is denied.
|
||||
func (f FilterFunc) EvalMutation(ctx context.Context, m {{ $pkg }}.Mutation) error {
|
||||
fr, err := mutationFilter(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f(ctx, fr)
|
||||
}
|
||||
|
||||
var _ QueryMutationRule = FilterFunc(nil)
|
||||
|
||||
func queryFilter(q {{ $pkg }}.Query) (Filter, error) {
|
||||
switch q := q.(type) {
|
||||
{{- range $n := $.Nodes }}
|
||||
case *{{ $pkg }}.{{ $n.QueryName }}:
|
||||
return q.Filter(), nil
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, Denyf("{{ $pkg }}/privacy: unexpected query type %T for query filter", q)
|
||||
}
|
||||
}
|
||||
|
||||
func mutationFilter(m {{ $pkg }}.Mutation) (Filter, error) {
|
||||
switch m := m.(type) {
|
||||
{{- range $n := $.MutableNodes }}
|
||||
case *{{ $pkg }}.{{ $n.MutationName }}:
|
||||
return m.Filter(), nil
|
||||
{{- end }}
|
||||
default:
|
||||
return nil, Denyf("{{ $pkg }}/privacy: unexpected mutation type %T for mutation filter", m)
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
|
||||
160
entc/gen/template/privacy/privacy.tmpl
Normal file
160
entc/gen/template/privacy/privacy.tmpl
Normal file
@@ -0,0 +1,160 @@
|
||||
{{/*
|
||||
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 "privacy" }}
|
||||
|
||||
{{ with extend $ "Package" "privacy" -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"{{ $.Config.Package }}"
|
||||
|
||||
"entgo.io/ent/privacy"
|
||||
"entgo.io/ent/entql"
|
||||
)
|
||||
|
||||
{{ $pkg := base $.Config.Package }}
|
||||
|
||||
var (
|
||||
// Allow may be returned by rules to indicate that the policy
|
||||
// evaluation should terminate with allow decision.
|
||||
Allow = privacy.Allow
|
||||
|
||||
// Deny may be returned by rules to indicate that the policy
|
||||
// evaluation should terminate with deny decision.
|
||||
Deny = privacy.Deny
|
||||
|
||||
// Skip may be returned by rules to indicate that the policy
|
||||
// evaluation should continue to the next rule.
|
||||
Skip = privacy.Skip
|
||||
)
|
||||
|
||||
{{- range $decision := list "Allow" "Deny" "Skip" }}
|
||||
// {{ $decision }}f returns a formatted wrapped {{ $decision }} decision.
|
||||
func {{ $decision }}f(format string, a ...any) error {
|
||||
return privacy.{{ $decision }}f(format, a...)
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// DecisionContext creates a new context from the given parent context with
|
||||
// a policy decision attach to it.
|
||||
func DecisionContext(parent context.Context, decision error) context.Context {
|
||||
return privacy.DecisionContext(parent, decision)
|
||||
}
|
||||
|
||||
// DecisionFromContext retrieves the policy decision from the context.
|
||||
func DecisionFromContext(ctx context.Context) (error, bool) {
|
||||
return privacy.DecisionFromContext(ctx)
|
||||
}
|
||||
|
||||
type (
|
||||
// Policy groups query and mutation policies.
|
||||
Policy = privacy.Policy
|
||||
|
||||
// QueryRule defines the interface deciding whether a
|
||||
// query is allowed and optionally modify it.
|
||||
QueryRule = privacy.QueryRule
|
||||
// QueryPolicy combines multiple query rules into a single policy.
|
||||
QueryPolicy = privacy.QueryPolicy
|
||||
|
||||
// MutationRule defines the interface which decides whether a
|
||||
// mutation is allowed and optionally modifies it.
|
||||
MutationRule = privacy.MutationRule
|
||||
// MutationPolicy combines multiple mutation rules into a single policy.
|
||||
MutationPolicy = privacy.MutationPolicy
|
||||
// MutationRuleFunc type is an adapter which allows the use of
|
||||
// ordinary functions as mutation rules.
|
||||
MutationRuleFunc = privacy.MutationRuleFunc
|
||||
|
||||
// QueryMutationRule is an interface which groups query and mutation rules.
|
||||
QueryMutationRule = privacy.QueryMutationRule
|
||||
)
|
||||
|
||||
// QueryRuleFunc type is an adapter to allow the use of
|
||||
// ordinary functions as query rules.
|
||||
type QueryRuleFunc func(context.Context, {{ $pkg }}.Query) error
|
||||
|
||||
// Eval returns f(ctx, q).
|
||||
func (f QueryRuleFunc) EvalQuery(ctx context.Context, q {{ $pkg }}.Query) error {
|
||||
return f(ctx, q)
|
||||
}
|
||||
|
||||
// AlwaysAllowRule returns a rule that returns an allow decision.
|
||||
func AlwaysAllowRule() QueryMutationRule {
|
||||
return privacy.AlwaysAllowRule()
|
||||
}
|
||||
|
||||
// AlwaysDenyRule returns a rule that returns a deny decision.
|
||||
func AlwaysDenyRule() QueryMutationRule {
|
||||
return privacy.AlwaysDenyRule()
|
||||
}
|
||||
|
||||
// ContextQueryMutationRule creates a query/mutation rule from a context eval func.
|
||||
func ContextQueryMutationRule(eval func(context.Context) error) QueryMutationRule {
|
||||
return privacy.ContextQueryMutationRule(eval)
|
||||
}
|
||||
|
||||
// OnMutationOperation evaluates the given rule only on a given mutation operation.
|
||||
func OnMutationOperation(rule MutationRule, op {{ $pkg }}.Op) MutationRule {
|
||||
return privacy.OnMutationOperation(rule, op)
|
||||
}
|
||||
|
||||
// DenyMutationOperationRule returns a rule denying specified mutation operation.
|
||||
func DenyMutationOperationRule(op {{ $pkg }}.Op) MutationRule {
|
||||
rule := MutationRuleFunc(func(_ context.Context, m {{ $pkg }}.Mutation) error {
|
||||
return Denyf("{{ $pkg }}/privacy: operation %s is not allowed", m.Op())
|
||||
})
|
||||
return OnMutationOperation(rule, op)
|
||||
}
|
||||
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $name := print $n.Name "QueryRuleFunc" }}
|
||||
{{ $type := printf "*%s.%s" $pkg $n.QueryName }}
|
||||
// The {{ $name }} type is an adapter to allow the use of ordinary
|
||||
// functions as a query rule.
|
||||
type {{ $name }} func(context.Context, {{ $type }}) error
|
||||
|
||||
// EvalQuery return f(ctx, q).
|
||||
func (f {{ $name }}) EvalQuery(ctx context.Context, q {{ $pkg }}.Query) error {
|
||||
if q, ok := q.({{ $type }}); ok {
|
||||
return f(ctx, q)
|
||||
}
|
||||
return Denyf("{{ $pkg }}/privacy: unexpected query type %T, expect {{ $type }}", q)
|
||||
}
|
||||
|
||||
{{ if not $n.IsView }}
|
||||
{{ $name = print $n.Name "MutationRuleFunc" }}
|
||||
{{ $type = printf "*%s.%s" $pkg $n.MutationName }}
|
||||
// The {{ $name }} type is an adapter to allow the use of ordinary
|
||||
// functions as a mutation rule.
|
||||
type {{ $name }} func(context.Context, {{ $type }}) error
|
||||
|
||||
// EvalMutation calls f(ctx, m).
|
||||
func (f {{ $name }}) EvalMutation(ctx context.Context, m {{ $pkg }}.Mutation) error {
|
||||
if m, ok := m.({{ $type }}); ok {
|
||||
return f(ctx, m)
|
||||
}
|
||||
return Denyf("{{ $pkg }}/privacy: unexpected mutation type %T, expect {{ $type }}", m)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if $.FeatureEnabled "entql" }}
|
||||
{{ template "privacy/filter" $ }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $tmpls := matchTemplate "privacy/additional/*" "privacy/additional/*/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ end }}
|
||||
250
entc/gen/template/runtime.tmpl
Normal file
250
entc/gen/template/runtime.tmpl
Normal file
@@ -0,0 +1,250 @@
|
||||
{{/*
|
||||
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 */}}
|
||||
|
||||
{{/*
|
||||
The runtime template generates the schema-stitching logic and holds the
|
||||
module version that was used to generate the assets.
|
||||
|
||||
It has 2 formats. A "runtime" package that should be empty-imported in the
|
||||
main package for schemas with hooks or policies (potential cyclic-import).
|
||||
The second format is generated under the "ent" package, and empty-import is
|
||||
not necessary (no option for cyclic-import). The second format used to keep
|
||||
backwards-compatibility with previous versions of ent.
|
||||
*/}}
|
||||
|
||||
{{ define "runtime/ent" }}
|
||||
{{ template "runtime" $ }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "runtime/pkg" }}
|
||||
{{ with extend $ "Package" "runtime" }}
|
||||
{{ template "runtime" . }}
|
||||
{{ end }}
|
||||
{{ $module := $.ModuleInfo }}
|
||||
{{ if or $module.Version $module.Sum }}
|
||||
const (
|
||||
{{- with $module.Version }}
|
||||
Version = "{{ . }}" // Version of ent codegen.
|
||||
{{- end }}
|
||||
{{- with $module.Sum }}
|
||||
Sum = "{{ . }}" // Sum of ent codegen.
|
||||
{{- end }}
|
||||
)
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "runtime" }}
|
||||
|
||||
{{ template "header" . }}
|
||||
|
||||
{{ $hooks := 0 }}
|
||||
{{ range $n := $.Nodes }}
|
||||
{{ $numHooks := add $n.NumHooks $n.NumInterceptors }}{{ if $n.NumPolicy }}{{ $numHooks = add $numHooks 1 }}{{ end }}
|
||||
{{ $hooks = add $hooks $numHooks }}
|
||||
{{ end }}
|
||||
{{ $rtpkg := false }}{{ if hasField $ "Scope" }}{{ $rtpkg = eq $.Scope.Package "runtime" }}{{ end }}
|
||||
|
||||
{{ if $rtpkg }} {{/* 1st format - runtime/runtime.go */}}
|
||||
{{ if $hooks }}
|
||||
{{ template "runtime/register" $ }}
|
||||
{{ else }}
|
||||
// The schema-stitching logic is generated in {{ $.Config.Package }}/runtime.go
|
||||
{{ end }}
|
||||
{{ else }}{{/* 2nd format - ent/runtime.go */}}
|
||||
{{ if not $hooks }}
|
||||
{{ template "runtime/register" $ }}
|
||||
{{ else }}
|
||||
// The schema-stitching logic is generated in {{ $.Config.Package }}/runtime/runtime.go
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{/* register schema handlers to type packages */}}
|
||||
{{ define "runtime/register" }}
|
||||
import (
|
||||
"time"
|
||||
"context"
|
||||
|
||||
{{- with $.Config.Schema }}
|
||||
"{{ . }}"
|
||||
{{- end }}
|
||||
{{- range $n := $.Nodes }}
|
||||
{{ $n.PackageAlias }} "{{ $.Config.Package }}/{{ $n.PackageDir }}"
|
||||
{{- /* Import external packages */}}
|
||||
{{- template "import/types" $n }}
|
||||
{{- end }}
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/privacy"
|
||||
"entgo.io/ent/schema/field"
|
||||
)
|
||||
|
||||
|
||||
// The init function reads all schema descriptors with runtime code
|
||||
// (default values, validators, hooks and policies) and stitches it
|
||||
// to their package variables.
|
||||
func init() {
|
||||
{{- range $n := $.Nodes }}
|
||||
{{- $pkg := $n.Package }}
|
||||
{{- $schema := base $.Config.Schema }}
|
||||
{{- with $n.RuntimeMixin }}
|
||||
{{ $pkg }}Mixin := {{ $schema }}.{{ $n.Name }}{}.Mixin()
|
||||
{{- end }}
|
||||
{{- with $policies := $n.PolicyPositions }}
|
||||
{{- /* Policies defined in schema and mixins. */}}
|
||||
{{ $pkg }}.Policy = privacy.NewPolicies({{ range $idx := $n.MixedInPolicies }}{{ $pkg }}Mixin[{{ $idx }}],{{ end }}{{ $schema }}.{{ $n.Name }}{})
|
||||
{{ $pkg }}.Hooks[0] = func(next ent.Mutator) ent.Mutator {
|
||||
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
if err := {{ $pkg }}.Policy.EvalMutation(ctx, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return next.Mutate(ctx, m)
|
||||
})
|
||||
}
|
||||
{{- end }}
|
||||
{{- with $hooks := $n.HookPositions }}
|
||||
{{- /* Hooks defined in schema mixins. */}}
|
||||
{{- with $idx := $n.MixedInHooks }}
|
||||
{{- range $i := $idx }}
|
||||
{{ print $pkg "MixinHooks" $i }} := {{ $pkg }}Mixin[{{ $i }}].Hooks()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* If there are hooks defined in the schema. */}}
|
||||
{{- $schemaHooks := false }}{{ range $p := $hooks }}{{ if not $p.MixedIn }}{{ $schemaHooks = true }}{{ end }}{{ end }}
|
||||
{{- if $schemaHooks }}
|
||||
{{ print $pkg "Hooks" }} := {{ $schema }}.{{ $n.Name }}{}.Hooks()
|
||||
{{- end }}
|
||||
{{- range $i, $p := $hooks }}
|
||||
{{- if $n.NumPolicy }}
|
||||
{{ $i = add $i 1 }}
|
||||
{{- end }}
|
||||
{{- if $p.MixedIn }}
|
||||
{{ print $pkg ".Hooks" }}[{{ $i }}] = {{ print $pkg "MixinHooks" $p.MixinIndex }}[{{ $p.Index }}]
|
||||
{{- else }}
|
||||
{{ print $pkg ".Hooks" }}[{{ $i }}] = {{ print $pkg "Hooks" }}[{{ $p.Index }}]
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with $inters := $n.InterceptorPositions }}
|
||||
{{- /* Interceptors defined in schema mixins. */}}
|
||||
{{- with $idx := $n.MixedInInterceptors }}
|
||||
{{- range $i := $idx }}
|
||||
{{ print $pkg "MixinInters" $i }} := {{ $pkg }}Mixin[{{ $i }}].Interceptors()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- /* If there are interceptors defined in the schema. */}}
|
||||
{{- $schemaInters := false }}{{ range $p := $inters }}{{ if not $p.MixedIn }}{{ $schemaInters = true }}{{ end }}{{ end }}
|
||||
{{- if $schemaInters }}
|
||||
{{ print $pkg "Inters" }} := {{ $schema }}.{{ $n.Name }}{}.Interceptors()
|
||||
{{- end }}
|
||||
{{- range $i, $p := $inters }}
|
||||
{{- if $p.MixedIn }}
|
||||
{{ print $pkg ".Interceptors" }}[{{ $i }}] = {{ print $pkg "MixinInters" $p.MixinIndex }}[{{ $p.Index }}]
|
||||
{{- else }}
|
||||
{{ print $pkg ".Interceptors" }}[{{ $i }}] = {{ print $pkg "Inters" }}[{{ $p.Index }}]
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if or $n.HasDefault $n.HasUpdateDefault $n.HasValidators $n.HasValueScanner $n.HasBlobFields }}
|
||||
{{- with $idx := $n.MixedInFields }}
|
||||
{{- range $i := $idx }}
|
||||
{{ print $pkg "MixinFields" $i }} := {{ $pkg }}Mixin[{{ $i }}].Fields()
|
||||
_ = {{ $pkg }}MixinFields{{ $i }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $fields := $n.Fields }}{{ if $n.HasOneFieldID }}{{ if $n.ID.UserDefined }}{{ $fields = append $fields $n.ID }}{{ end }}{{ end }}
|
||||
{{- with $fields }}
|
||||
{{ $pkg }}Fields := {{ $schema }}.{{ $n.Name }}{}.Fields()
|
||||
_ = {{ $pkg }}Fields
|
||||
{{- end }}
|
||||
{{- range $i, $f := $fields }}
|
||||
{{- if $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{- $desc := print $pkg "Desc" $f.StructField }}
|
||||
{{- $idscan := and $n.HasOneFieldID (eq $f.Name $n.ID.Name) }}
|
||||
{{- /* enum default values handled near their declarations (in type package). */}}
|
||||
{{- if or (and $f.Default (not $f.IsEnum)) $f.UpdateDefault $f.Validators (and $f.HasValueScanner (or (not $idscan) (eq $n.Storage.Name "sql"))) }}
|
||||
// {{ $desc }} is the schema descriptor for {{ $f.Name }} field.
|
||||
{{- if $f.Position.MixedIn }}
|
||||
{{ $desc }} := {{ print $pkg "MixinFields" $f.Position.MixinIndex }}[{{ $f.Position.Index }}].Descriptor()
|
||||
{{- else }}
|
||||
{{ $desc }} := {{ $pkg }}Fields[{{ $f.Position.Index }}].Descriptor()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and $f.Default (not $f.IsEnum) }}
|
||||
{{- $default := print $pkg "." $f.DefaultName }}
|
||||
// {{ $default }} holds the default value on creation for the {{ $f.Name }} field.
|
||||
{{- $defaultType := print $f.Type.Type }}{{ if $f.DefaultFunc }}{{ $defaultType = print "func() " $f.Type }}{{ end }}
|
||||
{{- if and $f.HasGoType (not (hasPrefix $defaultType "func")) }}
|
||||
{{- if or $f.IsJSON $f.IsOther }}
|
||||
{{ $default }} = {{ $desc }}.Default.({{ $f.Type }})
|
||||
{{- else }}
|
||||
{{ $default }} = {{ $f.Type }}({{ $desc }}.Default.({{ $defaultType }}))
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{ $default }} = {{ $desc }}.Default.({{ $defaultType }})
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $f.UpdateDefault }}
|
||||
{{- $default := print $pkg "." $f.UpdateDefaultName }}
|
||||
// {{ $default }} holds the default value on update for the {{ $f.Name }} field.
|
||||
{{ $default }} = {{ $desc }}.UpdateDefault.(func() {{ $f.Type }})
|
||||
{{- end }}
|
||||
{{- if and $f.HasValueScanner (or (not $idscan) (eq $n.Storage.Name "sql")) }}
|
||||
{{- $valuescan := print $pkg ".ValueScanner." $f.StructField }}
|
||||
{{ $valuescan }} = {{ $desc }}.ValueScanner.(field.TypeValueScanner[{{ $f.Type }}])
|
||||
{{- end }}
|
||||
{{- with $f.Validators }}
|
||||
{{- $name := print $pkg "." $f.Validator }}
|
||||
{{- $type := $f.Type.Type.String }}
|
||||
{{- if $f.IsJSON }}
|
||||
{{- $type = $f.Type.String }}
|
||||
{{- end }}
|
||||
// {{ $name }} is a validator for the "{{ $f.Name }}" field. It is called by the builders before save.
|
||||
{{- if eq $f.Validators 1 }}
|
||||
{{ $name }} = {{ $desc }}.Validators[0].({{ printf "func (%s) error" $type }})
|
||||
{{- else }}
|
||||
{{ $name }} = func() {{ printf "func (%s) error" $type }} {
|
||||
validators := {{ $desc }}.Validators
|
||||
fns := [...]func({{ $f.Type.Type }}) error {
|
||||
{{- range $j, $n := xrange $f.Validators }}
|
||||
validators[{{ $j }}].(func({{ $f.Type.Type }}) error),
|
||||
{{- end }}
|
||||
}
|
||||
return func({{ $f.BuilderField }} {{ $f.Type.Type }}) error {
|
||||
for _, fn := range fns {
|
||||
if err := fn({{ $f.BuilderField }}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if hasTemplate (printf "dialect/%s/model/fields" $.Storage) }}
|
||||
{{- range $i, $f := $n.BlobFields }}
|
||||
{{- if $f.HasBlobKey }}
|
||||
{{- $bkName := print $pkg "." $f.BlobKeyName }}
|
||||
{{- $desc := print $pkg "BlobDesc" $f.StructField }}
|
||||
// {{ $desc }} is the schema descriptor for {{ $f.Name }} blob field.
|
||||
{{- if $f.Position.MixedIn }}
|
||||
{{ $desc }} := {{ print $pkg "MixinFields" $f.Position.MixinIndex }}[{{ $f.Position.Index }}].Descriptor()
|
||||
{{- else }}
|
||||
{{ $desc }} := {{ $pkg }}Fields[{{ $f.Position.Index }}].Descriptor()
|
||||
{{- end }}
|
||||
// {{ $bkName }} generates the blob storage key for the {{ $f.Name }} field.
|
||||
{{ $bkName }} = ent.BlobKeyFunc({{ $desc }}.BlobKey)
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
182
entc/gen/template/tx.tmpl
Normal file
182
entc/gen/template/tx.tmpl
Normal file
@@ -0,0 +1,182 @@
|
||||
{{/*
|
||||
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 "tx" }}
|
||||
|
||||
{{ template "header" $ }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"entgo.io/ent/dialect"
|
||||
)
|
||||
|
||||
// Tx is a transactional client that is created by calling Client.Tx().
|
||||
type Tx struct {
|
||||
config
|
||||
{{- range $n := $.Nodes }}
|
||||
// {{ $n.Name }} is the client for interacting with the {{ $n.Name }} builders.
|
||||
{{ $n.Name }} *{{ $n.ClientName }}
|
||||
{{- end }}
|
||||
|
||||
// lazily loaded.
|
||||
client *Client
|
||||
clientOnce sync.Once
|
||||
// ctx lives for the life of the transaction. It is
|
||||
// the same context used by the underlying connection.
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
{{ $funcs := dict "Commit" "Committer" "Rollback" "Rollbacker" }}
|
||||
{{ range $func := keys $funcs }}
|
||||
{{ $iface := get $funcs $func }}
|
||||
type (
|
||||
// {{ $iface }} is the interface that wraps the {{ $func }} method.
|
||||
{{ $iface }} interface {
|
||||
{{ $func }}(context.Context, *Tx) error
|
||||
}
|
||||
|
||||
// The {{ $func }}Func type is an adapter to allow the use of ordinary
|
||||
// function as a {{ $iface }}. If f is a function with the appropriate
|
||||
// signature, {{ $func }}Func(f) is a {{ $iface }} that calls f.
|
||||
{{ $func }}Func func(context.Context, *Tx) error
|
||||
|
||||
// {{ $func }}Hook defines the "{{ lower $func }} middleware". A function that gets a {{ $iface }}
|
||||
// and returns a {{ $iface }}. For example:
|
||||
//
|
||||
// hook := func(next ent.{{ $iface }}) ent.{{ $iface }} {
|
||||
// return ent.{{ $func }}Func(func(ctx context.Context, tx *ent.Tx) error {
|
||||
// // Do some stuff before.
|
||||
// if err := next.{{ $func }}(ctx, tx); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // Do some stuff after.
|
||||
// return nil
|
||||
// })
|
||||
// }
|
||||
//
|
||||
{{ $func }}Hook func({{ $iface }} ) {{ $iface }}
|
||||
)
|
||||
|
||||
// {{ $func }} calls f(ctx, m).
|
||||
func (f {{ $func }}Func) {{ $func }}(ctx context.Context, tx *Tx) error {
|
||||
return f(ctx, tx)
|
||||
}
|
||||
|
||||
{{- $onFuncs := print "on" $func }}
|
||||
// {{ $func }} {{ lower $func }}s the transaction.
|
||||
func (tx *Tx) {{ $func }}() error {
|
||||
txDriver := tx.config.driver.(*txDriver)
|
||||
var fn {{ $iface }} = {{ $func }}Func(func(context.Context, *Tx) error {
|
||||
return txDriver.tx.{{ $func }}()
|
||||
})
|
||||
txDriver.mu.Lock()
|
||||
hooks := append([]{{ $func }}Hook(nil), txDriver.{{ $onFuncs }}...)
|
||||
txDriver.mu.Unlock()
|
||||
for i := len(hooks) - 1; i >= 0; i-- {
|
||||
fn = hooks[i](fn)
|
||||
}
|
||||
return fn.{{ $func }}(tx.ctx, tx)
|
||||
}
|
||||
|
||||
// On{{ $func }} adds a hook to call on {{ lower $func }}.
|
||||
func (tx *Tx) On{{ $func }}(f {{ $func }}Hook) {
|
||||
txDriver := tx.config.driver.(*txDriver)
|
||||
txDriver.mu.Lock()
|
||||
txDriver.{{ $onFuncs }} = append(txDriver.{{ $onFuncs }}, f)
|
||||
txDriver.mu.Unlock()
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
// Client returns a Client that binds to current transaction.
|
||||
func (tx *Tx) Client() *Client {
|
||||
tx.clientOnce.Do(func() {
|
||||
tx.client = &Client{config: tx.config}
|
||||
tx.client.init()
|
||||
})
|
||||
return tx.client
|
||||
}
|
||||
|
||||
func (tx *Tx) init() {
|
||||
{{- range $n := $.Nodes }}
|
||||
tx.{{ $n.Name }} = New{{ $n.ClientName }}(tx.config)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{/* first node for doc example */}}
|
||||
{{- $first := index $.Nodes 0 }}
|
||||
|
||||
// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation.
|
||||
// The idea is to support transactions without adding any extra code to the builders.
|
||||
// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance.
|
||||
// Commit and Rollback are nop for the internal builders and the user must call one
|
||||
// of them in order to commit or rollback the transaction.
|
||||
//
|
||||
// If a closed transaction is embedded in one of the generated entities, and the entity
|
||||
// applies a query, for example: {{ $first.Name }}.QueryXXX(), the query will be executed
|
||||
// through the driver which created this transaction.
|
||||
//
|
||||
// Note that txDriver is not goroutine safe.
|
||||
type txDriver struct {
|
||||
// the driver we started the transaction from.
|
||||
drv dialect.Driver
|
||||
// tx is the underlying transaction.
|
||||
tx dialect.Tx
|
||||
// completion hooks.
|
||||
mu sync.Mutex
|
||||
onCommit []CommitHook
|
||||
onRollback []RollbackHook
|
||||
}
|
||||
|
||||
// newTx creates a new transactional driver.
|
||||
func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) {
|
||||
tx, err := drv.Tx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &txDriver{tx: tx, drv: drv}, nil
|
||||
}
|
||||
|
||||
// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls
|
||||
// from the internal builders. Should be called only by the internal builders.
|
||||
func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil }
|
||||
|
||||
// Dialect returns the dialect of the driver we started the transaction from.
|
||||
func (tx *txDriver) Dialect() string { return tx.drv.Dialect() }
|
||||
|
||||
// Close is a nop close.
|
||||
func (*txDriver) Close() error { return nil }
|
||||
|
||||
// Commit is a nop commit for the internal builders.
|
||||
// User must call `Tx.Commit` in order to commit the transaction.
|
||||
func (*txDriver) Commit() error { return nil }
|
||||
|
||||
// Rollback is a nop rollback for the internal builders.
|
||||
// User must call `Tx.Rollback` in order to rollback the transaction.
|
||||
func (*txDriver) Rollback() error { return nil }
|
||||
|
||||
// Exec calls tx.Exec.
|
||||
func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error {
|
||||
return tx.tx.Exec(ctx, query, args, v)
|
||||
}
|
||||
|
||||
// Query calls tx.Query.
|
||||
func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error {
|
||||
return tx.tx.Query(ctx, query, args, v)
|
||||
}
|
||||
|
||||
var _ dialect.Driver = (*txDriver)(nil)
|
||||
|
||||
{{- with $tmpls := matchTemplate "tx/additional/*" "tx/additional/*/*" }}
|
||||
{{- range $tmpl := $tmpls }}
|
||||
{{- xtemplate $tmpl $ }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{ end }}
|
||||
237
entc/gen/template/where.tmpl
Normal file
237
entc/gen/template/where.tmpl
Normal file
@@ -0,0 +1,237 @@
|
||||
{{/*
|
||||
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 "where" }}
|
||||
|
||||
{{- with extend $ "Package" $.Package -}}
|
||||
{{ template "header" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ template "import" $ }}
|
||||
|
||||
{{ if .HasOneFieldID }}
|
||||
// ID filters vertices based on their ID field.
|
||||
func ID(id {{ $.ID.Type }}) predicate.{{ $.Name }} {
|
||||
{{- if and $.ID.HasValueScanner (eq $.Storage.Name "sql") }}
|
||||
vc, err := ValueScanner.{{ $.ID.StructField }}.Value(id)
|
||||
return predicate.{{ $.Name }}OrErr(
|
||||
{{- with extend $ "Arg" "vc" -}}
|
||||
{{- $tmpl := printf "dialect/%s/predicate/id" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
{{- end -}}, err)
|
||||
{{- else }}
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Arg" "id" -}}
|
||||
{{- $tmpl := printf "dialect/%s/predicate/id" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
{{- end -}}
|
||||
)
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
{{ range $op := $.ID.Ops }}
|
||||
{{ $arg := "id" }}{{ if $op.Variadic }}{{ $arg = "ids" }}{{ end }}
|
||||
{{ $stringOp := eq $op.Name "EqualFold" "Contains" "ContainsFold" "HasPrefix" "HasSuffix" }}
|
||||
{{ $func := printf "ID%s" $op.Name }}
|
||||
// {{ $func }} applies the {{ $op.Name }} predicate on the ID field.
|
||||
func {{ $func }}({{ $arg }} {{ if $op.Variadic }}...{{ end }}{{ $.ID.Type }}) predicate.{{ $.Name }} {
|
||||
{{- if and (eq $.Storage.Name "sql") $.ID.HasValueScanner (or $op.Variadic (not $op.Niladic)) }}
|
||||
{{- if $op.Variadic }}
|
||||
var (
|
||||
err error
|
||||
vcs = make([]any, len({{ $arg }}))
|
||||
)
|
||||
for i := range vcs {
|
||||
if vcs[i], err = ValueScanner.{{ $.ID.StructField }}.Value({{ print $arg "[i]" }}); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
{{- $arg = "vcs" }}
|
||||
{{- else }}
|
||||
vc, err := ValueScanner.{{ $.ID.StructField }}.Value({{ $arg }})
|
||||
{{- $arg = "vc" }}
|
||||
{{- if $stringOp }}
|
||||
{{- $arg = "vcs" }}
|
||||
vcs, ok := vc.(string)
|
||||
if err == nil && !ok {
|
||||
err = fmt.Errorf("{{ $.ID.Name }} value is not a string: %T", vc)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return predicate.{{ $.Name }}OrErr(
|
||||
{{- with extend $ "Arg" $arg "Op" $op "Storage" $.Storage -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/id/ops" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}, err)
|
||||
{{- else }}
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Arg" $arg "Op" $op "Storage" $.Storage -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/id/ops" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $f := $.Fields }}
|
||||
{{ if or $f.IsBlobNoColumn $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ $func := $f.StructField }}
|
||||
{{/* JSON cannot be compared using "=" and Enum has a type defined with the field name */}}
|
||||
{{ $hasP := not (or $f.IsJSON $f.IsEnum) }}
|
||||
{{ $comparable := or $f.ConvertedToBasic $f.Type.Valuer }}
|
||||
{{ $undeclared := (and (ne $func "Label") (ne $func "OrderOption") (ne $func "Hooks") (ne $func "Policy") (ne $func "Table") (ne $func "FieldID")) }}
|
||||
{{- if and $hasP $comparable $undeclared }}
|
||||
{{ $arg := "v" }}
|
||||
// {{ $func }} applies equality check predicate on the {{ quote $f.Name }} field. It's identical to {{ $func }}EQ.
|
||||
func {{ $func }}({{ $arg }} {{ $f.Type }}) predicate.{{ $.Name }} {
|
||||
{{- if $f.HasValueScanner }}
|
||||
vc, err := ValueScanner.{{ $f.StructField }}.Value({{ $arg }})
|
||||
{{- $arg = "vc" }}
|
||||
return predicate.{{ $.Name }}OrErr(
|
||||
{{- with extend $ "Arg" $arg "Field" $f -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/field" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}, err)
|
||||
{{- else }}
|
||||
{{- if and $f.HasGoType (not $f.Type.Valuer) }}
|
||||
vc := {{ $f.BasicType "v" }}
|
||||
{{- $arg = "vc" }}
|
||||
{{- end }}
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Arg" $arg "Field" $f -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/field" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
)
|
||||
{{- end }}
|
||||
}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $f := $.Fields }}
|
||||
{{ if or $f.IsBlobNoColumn $f.IsBlobLazy }}{{ continue }}{{ end }}
|
||||
{{ range $op := $f.Ops }}
|
||||
{{ $arg := "v" }}{{ if $op.Variadic }}{{ $arg = "vs" }}{{ end }}
|
||||
{{ $stringOp := eq $op.Name "EqualFold" "Contains" "ContainsFold" "HasPrefix" "HasSuffix" }}
|
||||
{{ $func := print $f.StructField $op.Name }}
|
||||
{{ $type := $f.Type.String }}{{ if $f.IsEnum }}{{ $type = trimPackage $type $.Package }}{{ end }}
|
||||
// {{ $func }} applies the {{ $op.Name }} predicate on the {{ quote $f.Name }} field.
|
||||
func {{ $func }}({{ if not $op.Niladic }}{{ $arg }} {{ if $op.Variadic }}...{{ end }}{{ $type }}{{ end }}) predicate.{{ $.Name }} {
|
||||
{{- if and $f.HasValueScanner (or $op.Variadic (not $op.Niladic)) }}
|
||||
{{- if $op.Variadic }}
|
||||
var (
|
||||
err error
|
||||
v = make([]any, len({{ $arg }}))
|
||||
)
|
||||
for i := range v {
|
||||
if v[i], err = ValueScanner.{{ $f.StructField }}.Value({{ print $arg "[i]" }}); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
{{- $arg = "v" }}
|
||||
{{- else }}
|
||||
vc, err := ValueScanner.{{ $f.StructField }}.Value({{ $arg }})
|
||||
{{- $arg = "vc" }}
|
||||
{{- if $stringOp }}
|
||||
{{- $arg = "vcs" }}
|
||||
vcs, ok := vc.(string)
|
||||
if err == nil && !ok {
|
||||
err = fmt.Errorf("{{ $f.Name }} value is not a string: %T", vc)
|
||||
}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return predicate.{{ $.Name }}OrErr(
|
||||
{{- with extend $ "Arg" $arg "Field" $f "Op" $op "Storage" $.Storage -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/field/ops" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end }}, err)
|
||||
{{- else }}
|
||||
{{- if and $f.HasGoType (or $stringOp (not $f.Type.Valuer)) }}
|
||||
{{- if $op.Variadic }}
|
||||
v := make([]any, len({{ $arg }}))
|
||||
for i := range v {
|
||||
v[i] = {{ $f.BasicType (printf "%s[i]" $arg) }}
|
||||
}
|
||||
{{- $arg = "v" }}
|
||||
{{- else if not $op.Niladic }}
|
||||
vc := {{ $f.BasicType "v" }}
|
||||
{{- $arg = "vc" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Arg" $arg "Field" $f "Op" $op "Storage" $.Storage -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/field/ops" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
)
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $e := $.Edges }}
|
||||
{{ $func := print "Has" $e.StructField }}
|
||||
// {{ $func }} applies the HasEdge predicate on the {{ quote $e.Name }} edge.
|
||||
func {{ $func }}() predicate.{{ $.Name }} {
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Edge" $e -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/edge/has" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
)
|
||||
}
|
||||
{{ $func = printf "%sWith" $func }}
|
||||
// {{ $func }} applies the HasEdge predicate on the {{ quote $e.Name }} edge with a given conditions (other predicates).
|
||||
func {{ $func }}(preds ...predicate.{{ $e.Type.Name }}) predicate.{{ $.Name }} {
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- with extend $ "Edge" $e -}}
|
||||
{{ $tmpl := printf "dialect/%s/predicate/edge/haswith" $.Storage }}
|
||||
{{- xtemplate $tmpl . }}
|
||||
{{- end -}}
|
||||
)
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.{{ $.Name }}) predicate.{{ $.Name }} {
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- $tmpl := printf "dialect/%s/predicate/and" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
)
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.{{ $.Name }}) predicate.{{ $.Name }} {
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- $tmpl = printf "dialect/%s/predicate/or" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
)
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.{{ $.Name }}) predicate.{{ $.Name }} {
|
||||
return predicate.{{ $.Name }}(
|
||||
{{- $tmpl = printf "dialect/%s/predicate/not" $.Storage }}
|
||||
{{- xtemplate $tmpl . -}}
|
||||
)
|
||||
}
|
||||
|
||||
{{ template "where/additional" $ }}
|
||||
|
||||
{{ with $tmpls := matchTemplate "where/additional/*" }}
|
||||
{{ range $tmpl := $tmpls }}
|
||||
{{ xtemplate $tmpl $ }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{/* A template that can be overridden in order to add additional code for the type package. */}}
|
||||
{{ define "where/additional" }}{{ end }}
|
||||
Reference in New Issue
Block a user