-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquery.go
120 lines (96 loc) · 2.55 KB
/
query.go
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
package kin
import (
"fmt"
_ "github.com/lib/pq" // Needed to initialize the Postgres SQL driver.
"reflect"
"strings"
)
// Query is an interface for generating query queries. It supports
// a few common and simple querying operations and then allows the user
// to solve more complex problems using SQL.
type Query[T Model] interface {
// Get runs a select and returns all results.
Get(table string) ([]*T, error)
// Insert adds a record and returns the result.
Insert(model T) (*T, error)
// Raw generates a new statement to be executed at a later time.
Raw(stmt string, params ...interface{}) *Statement[T]
}
// NewQuery creates a new wrapper around an existing DB connection.
func NewQuery[T Model](db DatabaseConnection) Query[T] {
return &query[T]{db: db}
}
type query[T Model] struct {
db DatabaseConnection
}
func (q *query[T]) Get(table string) ([]*T, error) {
query := fmt.Sprintf("SELECT * FROM %s", table)
return q.Raw(query).Many()
}
func (q *query[T]) Insert(model T) (*T, error) {
return q.insertQuery(model).One()
}
func (q *query[T]) insertQuery(m Model) *Statement[T] {
var columns string
var values string
var params []interface{}
for _, details := range columnsWithValues(m) {
if details.IsPrimaryKey {
continue
}
separator := ""
if len(params) > 0 {
separator = ", "
}
params = append(params, details.Value)
columns = fmt.Sprintf("%s%s%s", columns, separator, details.Name)
values = fmt.Sprintf("%s%s$%d", values, separator, len(params))
}
stmt := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s) RETURNING *", m.TableName(), columns, values)
return q.Raw(stmt, params...)
}
func (q *query[T]) Raw(stmt string, params ...interface{}) *Statement[T] {
return &Statement[T]{
db: q.db,
stmt: stmt,
params: params,
}
}
type columnDetails struct {
Name string
Value interface{}
IsPrimaryKey bool
}
func columnsWithValues[T Model](model T) []columnDetails {
var details []columnDetails
t := reflect.TypeOf(model)
v := reflect.ValueOf(model)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tags := field.Tag.Get("db")
if tags == "" {
continue
}
name := ""
isPrimaryKey := false
for _, tag := range strings.Split(tags, ",") {
if tag == "primaryKey" {
isPrimaryKey = true
} else {
name = tag
}
}
details = append(details, columnDetails{
Name: name,
Value: v.Field(i).Interface(),
IsPrimaryKey: isPrimaryKey,
})
}
return details
}