-
Notifications
You must be signed in to change notification settings - Fork 8
/
executor.go
187 lines (150 loc) · 5.7 KB
/
executor.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
Copyright 2023 eatmoreapple
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package juice
import (
"context"
"database/sql"
"errors"
"github.com/eatmoreapple/juice/driver"
)
// GenericExecutor is a generic executor.
type GenericExecutor[T any] interface {
// QueryContext executes the query and returns the direct result.
// The args are for any placeholder parameters in the query.
QueryContext(ctx context.Context, param Param) (T, error)
// ExecContext executes a query without returning any rows.
// The args are for any placeholder parameters in the query.
ExecContext(ctx context.Context, param Param) (sql.Result, error)
// Statement returns the xmlSQLStatement of the current executor.
Statement() Statement
// Session returns the session of the current executor.
Session() Session
// Driver returns the driver of the current executor.
Driver() driver.Driver
}
// Executor defines the interface of the executor.
type Executor GenericExecutor[*sql.Rows]
// badExecutor wraps the error who implements the Executor interface.
type badExecutor struct{ error }
// QueryContext implements the Executor interface.
func (b badExecutor) QueryContext(_ context.Context, _ Param) (*sql.Rows, error) { return nil, b.error }
// ExecContext implements the Executor interface.
func (b badExecutor) ExecContext(_ context.Context, _ Param) (sql.Result, error) { return nil, b.error }
// Statement implements the Executor interface.
func (b badExecutor) Statement() Statement { return nil }
// Session implements the Executor interface.
func (b badExecutor) Session() Session { return nil }
func (b badExecutor) Driver() driver.Driver { return nil }
// inValidExecutor is an invalid executor.
func inValidExecutor(err error) Executor {
return &badExecutor{error: err}
}
// isBadExecutor checks if the executor is a badExecutor.
func isBadExecutor(e Executor) (*badExecutor, bool) {
i, ok := e.(*badExecutor)
return i, ok
}
// ensure that the defaultExecutor implements the Executor interface.
var _ Executor = (*badExecutor)(nil)
// executor is an executor of SQL.
type executor struct {
session Session
statement Statement
driver driver.Driver
middlewares MiddlewareGroup
}
// QueryContext executes the query and returns the result.
func (e *executor) QueryContext(ctx context.Context, param Param) (*sql.Rows, error) {
stmt := e.Statement()
query, args, err := stmt.Build(e.driver.Translator(), param)
if err != nil {
return nil, err
}
queryHandler := CombineQueryHandler(stmt, e.middlewares...)
return queryHandler(ctx, query, args...)
}
// ExecContext executes the query and returns the result.
func (e *executor) ExecContext(ctx context.Context, param Param) (sql.Result, error) {
stmt := e.Statement()
query, args, err := stmt.Build(e.driver.Translator(), param)
if err != nil {
return nil, err
}
execHandler := CombineExecHandler(stmt, e.middlewares...)
return execHandler(ctx, query, args...)
}
// Statement returns the xmlSQLStatement.
func (e *executor) Statement() Statement { return e.statement }
// Session returns the session of the executor.
func (e *executor) Session() Session { return e.session }
// Driver returns the driver of the executor.
func (e *executor) Driver() driver.Driver { return e.driver }
// ensure that the executor implements the Executor interface.
var _ Executor = (*executor)(nil)
// genericExecutor is a generic executor.
type genericExecutor[T any] struct {
Executor
// extra middlewares for the executor
middlewares GenericMiddlewareGroup[T]
}
// QueryContext executes the query and returns the scanner.
func (e *genericExecutor[T]) QueryContext(ctx context.Context, p Param) (result T, err error) {
// check the error of the executor
if exe, ok := isBadExecutor(e.Executor); ok {
return result, exe.error
}
statement := e.Statement()
// build the query and args
query, args, err := statement.Build(e.Driver().Translator(), p)
if err != nil {
return
}
// call the middleware
return e.middlewares.QueryContext(statement, e.queryContext(p))(ctx, query, args...)
}
func (e *genericExecutor[T]) queryContext(param Param) GenericQueryHandler[T] {
return func(ctx context.Context, query string, args ...any) (result T, err error) {
statement := e.Statement()
retMap, err := statement.ResultMap()
// ErrResultMapNotSet means the result map is not set, use the default result map.
if err != nil {
if !errors.Is(err, ErrResultMapNotSet) {
return result, err
}
}
// try to query the database.
rows, err := e.Executor.QueryContext(ctx, param)
if err != nil {
return result, err
}
defer func() { _ = rows.Close() }()
return BindWithResultMap[T](rows, retMap)
}
}
// ExecContext executes the query and returns the result.
func (e *genericExecutor[_]) ExecContext(ctx context.Context, p Param) (ret sql.Result, err error) {
// check the error of the executor
if exe, ok := isBadExecutor(e.Executor); ok {
return ret, exe
}
return e.Executor.ExecContext(ctx, p)
}
// Use adds a middleware to the current executor.
func (e *genericExecutor[T]) Use(middlewares ...GenericMiddleware[T]) {
if len(middlewares) == 0 {
return
}
e.middlewares = append(e.middlewares, middlewares...)
}
// ensure genericExecutor implements GenericExecutor.
var _ GenericExecutor[any] = (*genericExecutor[any])(nil)