-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathrunner.go
More file actions
204 lines (171 loc) · 7.52 KB
/
runner.go
File metadata and controls
204 lines (171 loc) · 7.52 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
Copyright 2025 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"
"github.com/go-juicedev/juice/eval"
"github.com/go-juicedev/juice/session"
"github.com/go-juicedev/juice/sql"
)
// Runner defines the interface for SQL operations.
// It provides methods for executing SELECT, INSERT, UPDATE, and DELETE operations.
// Implementations of this interface should handle SQL query execution and result processing.
type Runner interface {
// Select executes a SELECT query and returns the result rows.
Select(ctx context.Context, param eval.Param) (sql.Rows, error)
// Insert executes an INSERT statement and returns the result.
// The result includes the last insert ID and number of rows affected.
Insert(ctx context.Context, param eval.Param) (sql.Result, error)
// Update executes an UPDATE statement and returns the result.
// The result includes the number of rows affected by the update.
Update(ctx context.Context, param eval.Param) (sql.Result, error)
// Delete executes a DELETE statement and returns the result.
// The result includes the number of rows affected by the deletion.
Delete(ctx context.Context, param eval.Param) (sql.Result, error)
}
// ErrorRunner is a Runner implementation that always returns an error.
// It's useful for handling invalid states, configuration errors, or when operations
// should be prevented from executing. All methods return the same error that was
// provided during creation.
type ErrorRunner struct {
error
}
// Select implements Runner.Select by returning the stored error.
// It ignores the context and parameters, always returning nil for rows and the stored error.
func (r *ErrorRunner) Select(_ context.Context, _ eval.Param) (sql.Rows, error) {
return nil, r.error
}
// Insert implements Runner.Insert by returning the stored error.
// It ignores the context and parameters, always returning nil for result and the stored error.
func (r *ErrorRunner) Insert(_ context.Context, _ eval.Param) (sql.Result, error) {
return nil, r.error
}
// Update implements Runner.Update by returning the stored error.
// It ignores the context and parameters, always returning nil for result and the stored error.
func (r *ErrorRunner) Update(_ context.Context, _ eval.Param) (sql.Result, error) {
return nil, r.error
}
// Delete implements Runner.Delete by returning the stored error.
// It ignores the context and parameters, always returning nil for result and the stored error.
func (r *ErrorRunner) Delete(_ context.Context, _ eval.Param) (sql.Result, error) {
return nil, r.error
}
// NewErrorRunner creates a new ErrorRunner that always returns the specified error.
// This is useful for creating a Runner that represents a failed state, such as
// when initialization fails or when operations should be prevented.
func NewErrorRunner(err error) Runner {
return &ErrorRunner{error: err}
}
// Ensure ErrorRunner implements the Runner interface interface.
var (
_ Runner = (*ErrorRunner)(nil)
_ error = (*ErrorRunner)(nil)
)
// SQLRunner is the standard implementation of Runner interface.
// It holds the SQL query, engine configuration, and session information.
type SQLRunner struct {
query string
engine *Engine
session session.Session
}
// BuildExecutor creates a new SQL executor based on the given action.
// It configures the statement handler with the necessary driver and middleware.
func (r *SQLRunner) BuildExecutor(action sql.Action) Executor[sql.Rows] {
driver := r.engine.Driver()
statement := NewRawSQLStatement(r.query, action)
statementHandler := newQueryBuildStatementHandler(
driver,
r.session,
r.engine.GetConfiguration(),
r.engine.middlewares...,
)
return NewSQLRowsExecutor(statement, statementHandler, driver)
}
// queryContext executes a SELECT query with the given context and parameters.
// It returns the query results as sql.Rows and any error that occurred.
func (r *SQLRunner) queryContext(ctx context.Context, param eval.Param) (sql.Rows, error) {
executor := r.BuildExecutor(sql.Select)
return executor.QueryContext(ctx, param)
}
// execContext executes a non-query SQL operation (INSERT, UPDATE, DELETE)
// with the given context and parameters.
func (r *SQLRunner) execContext(action sql.Action, ctx context.Context, param eval.Param) (sql.Result, error) {
executor := r.BuildExecutor(action)
return executor.ExecContext(ctx, param)
}
// Select executes a SELECT query and returns the result rows.
func (r *SQLRunner) Select(ctx context.Context, param eval.Param) (sql.Rows, error) {
return r.queryContext(ctx, param)
}
// Insert executes an INSERT statement and returns the result.
func (r *SQLRunner) Insert(ctx context.Context, param eval.Param) (sql.Result, error) {
return r.execContext(sql.Insert, ctx, param)
}
// Update executes an UPDATE statement and returns the result.
func (r *SQLRunner) Update(ctx context.Context, param eval.Param) (sql.Result, error) {
return r.execContext(sql.Update, ctx, param)
}
// Delete executes a DELETE statement and returns the result.
func (r *SQLRunner) Delete(ctx context.Context, param eval.Param) (sql.Result, error) {
return r.execContext(sql.Delete, ctx, param)
}
// NewRunner creates a new SQLRunner instance with the specified query, engine, and session.
func NewRunner(query string, engine *Engine, session session.Session) *SQLRunner {
return &SQLRunner{
query: query,
engine: engine,
session: session,
}
}
var _ Runner = (*SQLRunner)(nil) // Ensure SQLRunner implements the Runner interface.
// GenericRunner is a generic Runner implementation that binds the result of a SELECT query to a value of type T.
type GenericRunner[T any] struct {
Runner
}
// Bind binds the result of a SELECT query to a single value of type T.
// It executes the query with the given context and parameters, then binds the result.
func (r *GenericRunner[T]) Bind(ctx context.Context, param eval.Param) (result T, err error) {
rows, err := r.Select(ctx, param)
if err != nil {
return result, err
}
defer func() { _ = rows.Close() }()
return sql.Bind[T](rows)
}
// List binds the result of a SELECT query to a list of values of type T.
// It executes the query with the given context and parameters, then binds the result.
func (r *GenericRunner[T]) List(ctx context.Context, param eval.Param) (result []T, err error) {
rows, err := r.Select(ctx, param)
if err != nil {
return result, err
}
defer func() { _ = rows.Close() }()
return sql.List[T](rows)
}
// List2 binds the result of a SELECT query to a list of pointers to values of type T.
// It executes the query with the given context and parameters, then binds the result.
func (r *GenericRunner[T]) List2(ctx context.Context, param eval.Param) (result []*T, err error) {
rows, err := r.Select(ctx, param)
if err != nil {
return result, err
}
defer func() { _ = rows.Close() }()
return sql.List2[T](rows)
}
// NewGenericRunner creates a new GenericRunner instance with the specified Runner.
func NewGenericRunner[T any](runner Runner) *GenericRunner[T] {
return &GenericRunner[T]{
Runner: runner,
}
}
var _ Runner = (*GenericRunner[any])(nil) // Ensure GenericRunner implements Runner interface.