{{/*
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.
*/}}

{{ define "dialect/sql/predicate/id" -}}
	func(s *sql.Selector) {
		{{- if $.ID.IsString }}id, _ := strconv.Atoi(id){{- end }}
		s.Where(sql.EQ(s.C({{ $.ID.Constant }}), id))
	}
{{- end }}

{{ define "dialect/sql/predicate/id/ops" -}}
	{{- $op := $.Scope.Op -}}
	{{- $arg := $.Scope.Arg -}}
	{{- $storage := $.Scope.Storage -}}
	func(s *sql.Selector) {
		{{- if $op.Variadic }}
			// if not arguments were provided, append the FALSE constants,
			// since we can't apply "IN ()". This will make this predicate falsy.
			if len({{ $arg }}) == 0 {
				s.Where(sql.False())
				return
			}
			v := make([]interface{}, len({{ $arg }}))
			for i := range v {
				{{ if $.ID.IsString }}v[i], _ = strconv.Atoi({{ $arg }}[i]){{ else }}v[i] = {{ $arg }}[i]{{ end }}
			}
		{{- else if $.ID.IsString }}
			id, _ := strconv.Atoi({{ $arg }})
		{{- end }}
		s.Where(sql.{{ call $storage.OpCode $op }}(s.C({{ $.ID.Constant }}), {{ if $op.Variadic }}v...{{ else }}id{{ end }}))
	}
{{- end }}

{{ define "dialect/sql/predicate/field" -}}
	{{- $f := $.Scope.Field -}}
	func(s *sql.Selector) {
		s.Where(sql.EQ(s.C({{ $f.Constant }}), v))
	}
{{- end }}

{{ define "dialect/sql/predicate/field/ops" -}}
	{{- $f := $.Scope.Field -}}
	{{- $op := $.Scope.Op -}}
	{{- $arg := $.Scope.Arg -}}
	{{- $storage := $.Scope.Storage -}}
	func(s *sql.Selector) {
		{{- if $op.Variadic }}
			// if not arguments were provided, append the FALSE constants,
			// since we can't apply "IN ()". This will make this predicate falsy.
			if len({{ $arg }}) == 0 {
				s.Where(sql.False())
				return
			}
		{{- end }}
		s.Where(sql.{{ call $storage.OpCode $op }}(s.C({{ $f.Constant }}){{ if not $op.Niladic }}, v{{ if $op.Variadic }}...{{ end }}{{ end }}))
	}
{{- end }}

{{ define "dialect/sql/predicate/edge/has" -}}
	{{- $e := $.Scope.Edge -}}
	func(s *sql.Selector) {
		{{- if $e.M2M  }}
			t1 := s.Table()
			builder := sql.Dialect(s.Dialect())
			s.Where(
				sql.In(
					t1.C({{ $.ID.Constant }}),
					builder.Select({{ $e.PKConstant }}[{{ if $e.IsInverse }}1{{ else }}0{{ end }}]).
						From(builder.Table({{ $e.TableConstant }})),
				),
			)
		{{- else if or $e.M2O (and $e.O2O $e.IsInverse) }}{{/* M2O || (O2O with inverse edge) */}}
			t1 := s.Table()
			s.Where(sql.NotNull(t1.C({{ $e.ColumnConstant }})))
		{{- else }}{{/* O2M || (O2O with assoc edge) */}}
			t1 := s.Table()
			builder := sql.Dialect(s.Dialect())
			s.Where(
				sql.In(
					t1.C({{ $.ID.Constant }}),
					builder.Select({{ $e.ColumnConstant }}).
						From(builder.Table({{ $e.TableConstant }})).
						Where(sql.NotNull({{ $e.ColumnConstant }})),
				),
			)
		{{- end }}
	}
{{- end }}

{{ define "dialect/sql/predicate/edge/haswith" -}}
	{{- $e := $.Scope.Edge -}}
	func(s *sql.Selector) {
		{{- if $e.M2M  }}
			{{ $i := 1 }}{{ $j := 0 }}{{- if $e.IsInverse }}{{ $i = 0 }}{{ $j = 1 }}{{ end -}}
			builder := sql.Dialect(s.Dialect())
			t1 := s.Table()
			t2 := builder.Table(
				{{- if ne $.Table $e.Type.Table -}}
					{{ $e.InverseTableConstant }}
				{{- else -}}
					Table
				{{- end -}}
			)
			t3 := builder.Table({{ $e.TableConstant }})
			t4 := builder.Select(t3.C({{ $e.PKConstant }}[{{ $j }}])).
				From(t3).
				Join(t2).
				On(t3.C({{ $e.PKConstant }}[{{ $i }}]), t2.C({{ $e.Type.ID.Constant }}))
			t5 := builder.Select().From(t2)
			for _, p := range preds {
				p(t5)
			}
			t4.FromSelect(t5)
			s.Where(sql.In(t1.C({{ $.ID.Constant }}), t4))
		{{- else if or $e.M2O (and $e.O2O $e.IsInverse) }}{{/* M2O || (O2O with inverse edge) */}}
			builder := sql.Dialect(s.Dialect())
			t1 := s.Table()
			t2 := builder.Select({{ $e.Type.ID.Constant }}).From(builder.Table(
				{{- if ne $.Table $e.Type.Table -}}
					{{ $e.InverseTableConstant }}
				{{- else -}}
					{{ $e.TableConstant }}
				{{- end -}}
			))
			for _, p := range preds {
				p(t2)
			}
			s.Where(sql.In(t1.C({{ $e.ColumnConstant }}), t2))
		{{- else }}{{/* O2M || (O2O with assoc edge) */}}
			builder := sql.Dialect(s.Dialect())
			t1 := s.Table()
			t2 := builder.Select({{ $e.ColumnConstant }}).From(builder.Table({{ $e.TableConstant }}))
			for _, p := range preds {
				p(t2)
			}
			s.Where(sql.In(t1.C({{ $.ID.Constant }}), t2))
		{{- end }}
	}
{{- end }}

{{ define "dialect/sql/predicate/and" -}}
	func(s *sql.Selector) {
		s1 := s.Clone().SetP(nil)
		for _, p := range predicates {
			p(s1)
		}
		s.Where(s1.P())
	}
{{- end }}

{{ define "dialect/sql/predicate/or" -}}
	func(s *sql.Selector) {
		s1 := s.Clone().SetP(nil)
		for i, p := range predicates {
			if i > 0 {
				s1.Or()
			}
			p(s1)
		}
		s.Where(s1.P())
	}
{{- end }}

{{ define "dialect/sql/predicate/not" -}}
	func(s *sql.Selector) {
		p(s.Not())
	}
{{- end }}
