/
sqlrun.go
156 lines (122 loc) · 3.11 KB
/
sqlrun.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
156
package httplog
import (
"database/sql"
"strings"
"time"
)
// ExecResult defines the result structure of sql execution.
type ExecResult struct {
Error error
CostTime time.Duration
Headers []string
Rows interface{} // [][]string or []YourStruct
RowsCount int
RowsAffected int64
LastInsertID int64
IsQuery bool
}
// StringRows return the string rows when using MapPreparer.
func (r ExecResult) StringRows() [][]string {
return r.Rows.([][]string)
}
// MiniDB wraps Exec method.
type MiniDB interface {
// Exec executes update.
Exec(query string, args ...interface{}) (sql.Result, error)
// Query performs query.
Query(query string, args ...interface{}) (*sql.Rows, error)
}
// SQLExec is used to execute only updates.
type SQLExec struct {
MiniDB
}
// SQLRun is used to execute queries and updates.
type SQLRun struct {
*SQLExec
Preparer // required only for query
MaxRows int
}
// NewSQLExec creates a new SQLExec for only updates.
func NewSQLExec(db MiniDB) *SQLExec {
return &SQLExec{MiniDB: db}
}
// NewSQLRun creates a new SQLRun for queries and updates.
func NewSQLRun(db MiniDB, preparer Preparer) *SQLRun {
return &SQLRun{Preparer: preparer, SQLExec: NewSQLExec(db)}
}
// DoExec executes a SQL.
func (s *SQLRun) DoExec(query string, args ...interface{}) ExecResult {
_, isQuerySQL := IsQuerySQL(query)
if isQuerySQL {
return s.DoQuery(query, args...)
}
return s.DoUpdate(query, args...)
}
// DoQuery does the query.
func (s *SQLRun) DoQuery(query string, args ...interface{}) (result ExecResult) {
start := time.Now()
result.IsQuery = true
defer func() {
result.CostTime = time.Since(start)
}()
rows, err := s.Query(query, args...)
if rows != nil {
defer rows.Close()
}
if err != nil || rows != nil && rows.Err() != nil {
if err == nil {
err = rows.Err()
}
result.Error = err
return result
}
columns, err := rows.Columns()
if err != nil {
result.Error = err
return result
}
mapping := s.Preparer.Prepare(rows, columns)
r := 0
for ; rows.Next() && (s.MaxRows <= 0 || r < s.MaxRows); r++ {
if err := mapping.Scan(r); err != nil {
result.Error = err
return result
}
}
result.Error = err
result.Headers = columns
result.Rows = mapping.RowsData()
result.RowsCount = r
return result
}
// DoUpdate does the update.
func (s *SQLExec) DoUpdate(query string, vars ...interface{}) (result ExecResult) {
start := time.Now()
r, err := s.Exec(query, vars...)
if r != nil {
result.RowsAffected, _ = r.RowsAffected()
result.LastInsertID, _ = r.LastInsertId()
}
result.Error = err
result.CostTime = time.Since(start)
return result
}
// IsQuerySQL tests a sql is a query or not.
func IsQuerySQL(sql string) (string, bool) {
key := FirstWord(sql)
switch strings.ToUpper(key) {
case "INSERT", "DELETE", "UPDATE", "SET", "REPLACE":
return key, false
case "SELECT", "SHOW", "DESC", "DESCRIBE", "EXPLAIN":
return key, true
default:
return key, false
}
}
// FirstWord returns the first word of the SQL statement s.
func FirstWord(s string) string {
if fields := strings.Fields(strings.TrimSpace(s)); len(fields) > 0 {
return fields[0]
}
return ""
}