forked from coocood/qbs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
base.go
155 lines (136 loc) · 4.29 KB
/
base.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package db
import (
"database/sql"
"fmt"
"strings"
)
/*
Base dialect implements default implementations of a Dialect
Each of the builtin dialects have a base embedded into them,
which simplifies their implementation to database specific
functions. An example implementation using Base would look like.
type OracleDialect struct {
Base
}
// Dialects need to be circular in structure, mainly for the
// FormatQuery to be able to be called from Base into your
// Dialect
func NewOracle() Dialect {
d := &OracleDialect{}
d.Base.Dialect = d
return d
}
func (d OracleDialect) CompatibleSqlTypes(f reflect.Type) []string {
...
}
func ...
*/
type Base struct {
Dialect Dialect
}
// The Base FormatQuery will not do anything, just returns the input string
// This works for databases that parameterize with ?, but postgres and similar
// database will need to implement a transformation for this function
func (d Base) FormatQuery(query string) string {
return query
}
// The Base CreateExec return value is true, so INSERT statements will
// be run by sql.Exec calls.
func (d Base) CreateExec() bool {
return true
}
// Create a basic SELECT query using ScopeInformation functions
func (d Base) Query(scope Scope) (string, []interface{}) {
output := "SELECT " + scope.SelectorSql() + " FROM " + scope.TableName()
output += scope.JoinsSql()
conditions, values := scope.ConditionSql()
if conditions != "" {
output += " WHERE " + conditions
}
ending, endValues := scope.EndingSql()
if len(endValues) > 0 {
values = append(values, endValues...)
}
output += ending
return d.Dialect.FormatQuery(output), values
}
// The Base Create function uses the syntax of INSERT INTO `table` (col...) VALUES (...)
// if this syntax will not work or you need a RETURNING predicate to get the id of
// the inserted records, you should override this
func (d Base) Create(mapper Mapper, values map[string]interface{}) (string, []interface{}) {
output := "INSERT INTO " + mapper.TableName() + " ("
sqlVals := make([]interface{}, len(values))
current := 0
var holders, cols []string
for col, val := range values {
sqlVals[current] = val
cols = append(cols, col)
holders = append(holders, "?")
current++
}
output += strings.Join(cols, ",") + ") VALUES (" + strings.Join(holders, ",") + ")"
return d.Dialect.FormatQuery(output), sqlVals
}
// The Base Update sql is of the form UPDATE table SET col = ? WHERE ...
func (d Base) Update(scope Scope, values map[string]interface{}) (string, []interface{}) {
output := "UPDATE " + scope.TableName() + " SET "
columns := make([]string, 0, len(values))
args := make([]interface{}, 0, len(values))
for c, v := range values {
columns = append(columns, c)
args = append(args, v)
}
output += strings.Join(columns, "= ?, ") + " = ?"
conditions, sqlArgs := scope.ConditionSql()
if conditions != "" {
output += " WHERE " + conditions
}
return d.Dialect.FormatQuery(output), append(args, sqlArgs...)
}
// The Base Delete sql takes the form of DELETE FROM `table` WHERE ...
func (d Base) Delete(scope Scope) (string, []interface{}) {
output := "DELETE FROM " + scope.TableName()
conditions, sqlArgs := scope.ConditionSql()
if conditions != "" {
output += " WHERE " + conditions
}
return d.Dialect.FormatQuery(output), sqlArgs
}
// The Base ColumnsInTable will attempt to use information_schema to
// retrieve column names, it will not try to guess types for columns
// It is in your best interest to implement this per database
func (d Base) ColumnsInTable(db *sql.DB, dbName string, table string) map[string]*ColumnInfo {
columns := make(map[string]*ColumnInfo)
query := "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?"
query = d.Dialect.FormatQuery(query)
rows, err := db.Query(query, dbName, table)
defer rows.Close()
if err != nil {
panic(err)
}
for rows.Next() {
column := ""
err := rows.Scan(&column)
if err == nil {
columns[column] = &ColumnInfo{
Name: column,
SqlTable: table,
SqlColumn: column,
Nullable: true,
}
}
}
return columns
}
func (d Base) printArg(v interface{}) string {
switch t := v.(type) {
case string:
return "'" + t + "'"
default:
return fmt.Sprint(v)
}
}
// The Base ExpandGroupBy will return true
func (d Base) ExpandGroupBy() bool {
return true
}