~bigbes/core-go

ref: c5355d172f2294af4dd172c2bba8f9d2b3418b35 core-go/database/sq.go -rw-r--r-- 2.6 KiB
c5355d17 — Conrad Hoffmann server: let AUTH_INTERNAL access @anoninternal 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package database

import (
	"context"
	"fmt"
	"sort"

	sq "github.com/Masterminds/squirrel"
)

// Provides a mapping between PostgreSQL columns, GQL fields, and Go struct
// fields for all of the data associated with a model.
type FieldMap struct {
	SQL string
	GQL string
	Ptr any
}

type ModelFields struct {
	Fields []*FieldMap

	byGQL map[string][]*FieldMap
	bySQL map[string][]*FieldMap
	anon  []*FieldMap
}

func (mf *ModelFields) buildCache() {
	if mf.byGQL != nil && mf.bySQL != nil {
		return
	}

	mf.byGQL = make(map[string][]*FieldMap)
	mf.bySQL = make(map[string][]*FieldMap)
	for _, f := range mf.Fields {
		if f.GQL != "" {
			if _, ok := mf.byGQL[f.GQL]; !ok {
				mf.byGQL[f.GQL] = nil
			}
			mf.byGQL[f.GQL] = append(mf.byGQL[f.GQL], f)
		} else {
			mf.anon = append(mf.anon, f)
		}
		if _, ok := mf.bySQL[f.SQL]; !ok {
			mf.bySQL[f.SQL] = nil
		}
		mf.bySQL[f.SQL] = append(mf.bySQL[f.SQL], f)
	}
}

func (mf *ModelFields) GQL(name string) ([]*FieldMap, bool) {
	mf.buildCache()
	if f, ok := mf.byGQL[name]; !ok {
		return nil, false
	} else {
		return f, true
	}
}

func (mf *ModelFields) SQL(name string) ([]*FieldMap, bool) {
	mf.buildCache()
	if f, ok := mf.bySQL[name]; !ok {
		return nil, false
	} else {
		return f, true
	}
}

func (mf *ModelFields) All() []*FieldMap {
	return mf.Fields
}

func (mf *ModelFields) Anonymous() []*FieldMap {
	mf.buildCache()
	return mf.anon
}

type Model interface {
	Alias() string
	Fields() *ModelFields
	Table() string
}

type ExtendedModel interface {
	Model
	Select(q sq.SelectBuilder) sq.SelectBuilder
}

func Select(ctx context.Context, cols ...any) sq.SelectBuilder {
	q := sq.Select().PlaceholderFormat(sq.Dollar)
	for _, col := range cols {
		switch col := col.(type) {
		case string:
			q = q.Columns(col)
		case []string:
			q = q.Columns(col...)
		case Model:
			if em, ok := col.(ExtendedModel); ok {
				q = em.Select(q.Columns(Columns(ctx, col)...))
			} else {
				q = q.Columns(Columns(ctx, col)...)
			}
		default:
			panic(fmt.Errorf("unknown selectable type %T", col))
		}
	}
	return q
}

func SelectAll(m Model) sq.SelectBuilder {
	mf := m.Fields()
	mf.buildCache()
	var cols []string
	for col, fields := range mf.bySQL {
		for range fields {
			cols = append(cols, WithAlias(m.Alias(), col))
		}
	}
	sort.Strings(cols)
	q := sq.Select().PlaceholderFormat(sq.Dollar)
	return q.Columns(cols...)
}

func ScanAll(m Model) []any {
	fms := m.Fields().All()
	sort.Slice(fms, func(a, b int) bool {
		return fms[a].SQL < fms[b].SQL
	})

	var fields []any
	for _, f := range fms {
		if f.SQL != "" {
			fields = append(fields, f.Ptr)
		}
	}
	return fields
}