{{/*
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/query" }}
{{ $pkg := $.Scope.Package }}
{{ $builder := pascal $.Scope.Builder }}
{{ $receiver := receiver $builder }}

func ({{ $receiver }} *{{ $builder }}) sqlAll(ctx context.Context) ([]*{{ $.Name }}, error) {
	rows := &sql.Rows{}
	selector := {{ $receiver }}.sqlQuery()
	if unique := {{ $receiver }}.unique; len(unique) == 0 {
		selector.Distinct()
	}
	query, args := selector.Query()
	if err := {{ $receiver }}.driver.Query(ctx, query, args, rows); err != nil {
		return nil, err
	}
	defer rows.Close()
	{{- $ret := plural $.Receiver }}
	var {{ $ret }} {{ plural $.Name  }}
	if err := {{ $ret }}.FromRows(rows); err != nil {
		return nil, err
	}
	{{ $ret }}.config({{ $receiver }}.config)
	return {{ $ret }}, nil
}

func ({{ $receiver }} *{{ $builder }}) sqlCount(ctx context.Context) (int, error) {
	rows := &sql.Rows{}
	selector := {{ $receiver }}.sqlQuery()
	unique := []string{ {{ $.Package }}.{{ $.ID.Constant }} }
	if len({{ $receiver }}.unique) > 0 {
		unique = {{ $receiver }}.unique
	}
	selector.Count(sql.Distinct(selector.Columns(unique...)...))
	query, args := selector.Query()
	if err := {{ $receiver }}.driver.Query(ctx, query, args, rows); err != nil {
		return 0, err
	}
	defer rows.Close()
	if !rows.Next() {
		return 0, errors.New("{{ $pkg }}: no rows found")
	}
	var n int
	if err := rows.Scan(&n); err != nil {
		return 0, fmt.Errorf("{{ $pkg }}: failed reading count: %v", err)
	}
	return n, nil
}

func ({{ $receiver }} *{{ $builder }}) sqlExist(ctx context.Context) (bool, error) {
	n, err := {{ $receiver }}.sqlCount(ctx)
	if err != nil {
		return false, fmt.Errorf("{{ $pkg }}: check existence: %v", err)
	}
	return n > 0, nil
}

func ({{ $receiver }} *{{ $builder }}) sqlQuery() *sql.Selector {
	builder := sql.Dialect({{ $receiver }}.driver.Dialect())
	t1 := builder.Table({{ $.Package }}.Table)
	selector := builder.Select(t1.Columns({{ $.Package }}.Columns...)...).From(t1)
	if {{ $receiver }}.sql != nil {
		selector = {{ $receiver }}.sql
		selector.Select(selector.Columns({{ $.Package }}.Columns...)...)
	}
	for _, p := range {{ $receiver }}.predicates {
		p(selector)
	}
	for _, p := range {{ $receiver }}.order {
		p(selector)
	}
	if offset := {{ $receiver }}.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 }}.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 := $ }}
	{{- $e := $.Scope.Edge }} {{/* the edge we need to genegrate the path to. */}}
	{{- $receiver := $.Scope.Receiver }}
	step := &sql.Step{}
	step.From.V = {{ $receiver }}.sqlQuery()
	step.From.Table = {{ $n.Package }}.Table
	step.From.Column = {{ $n.Package }}.{{ $n.ID.Constant }}
	step.To.Table = {{ $e.Type.Package }}.Table
	step.To.Column = {{ $e.Type.Package }}.{{ $e.Type.ID.Constant }}
	step.Edge.Rel = sql.{{ $e.Rel.Type }}
	step.Edge.Inverse = {{ $e.IsInverse }}
	step.Edge.Table = {{ $n.Package }}.{{ $e.TableConstant }}
	step.Edge.Columns = append(step.Edge.Columns, {{ if $e.M2M }}{{ $n.Package }}.{{ $e.PKConstant }}...{{ else }}{{ $n.Package }}.{{ $e.ColumnConstant }}{{ end }})
	query.sql = sql.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. */}}
	{{- $receiver := $.Scope.Receiver -}}
	id := {{ $receiver }}.{{- if $n.ID.IsString }}id(){{ else }}ID{{ end }}
	step := &sql.Step{}
	step.From.V = id
	step.From.Table = {{ $n.Package }}.Table
	step.From.Column = {{ $n.Package }}.{{ $n.ID.Constant }}
	step.To.Table = {{ $e.Type.Package }}.Table
	step.To.Column = {{ $e.Type.Package }}.{{ $e.Type.ID.Constant }}
	step.Edge.Rel = sql.{{ $e.Rel.Type }}
	step.Edge.Inverse = {{ $e.IsInverse }}
	step.Edge.Table = {{ $n.Package }}.{{ $e.TableConstant }}
	step.Edge.Columns = append(step.Edge.Columns, {{ if $e.M2M }}{{ $n.Package }}.{{ $e.PKConstant }}...{{ else }}{{ $n.Package }}.{{ $e.ColumnConstant }}{{ end }})
	query.sql = sql.Neighbors({{ $receiver }}.driver.Dialect(), step)
{{ end }}
