-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexec.go
More file actions
151 lines (127 loc) · 4.76 KB
/
exec.go
File metadata and controls
151 lines (127 loc) · 4.76 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
package litesql
import (
"database/sql"
"errors"
"fmt"
"cattlecloud.net/go/scope"
)
const (
// ExpectAnything indicates there is no expectation for the number of
// rows that will be updated as a result of executing a statement.
ExpectAnything = -(iota + 1)
// ExpectNonZero indicates the expectation for the number of rows that will
// be updated is non-zero.
ExpectNonZero
// ExpectOneOrZero indicates the expectation for the number of rows that
// will be updated is exactly 0 or 1 (e.g. insert or ignore)
ExpectOneOrZero
)
// ExecID executes the given sql query statement with args, and returns the
// resulting row id. The query must be intended to insert/modify exactly one
// row.
func (ldb *LiteDB) ExecID(ctx scope.C, tx *sql.Tx, stmt string, args ...any) (ID, error) {
result, xerr := tx.ExecContext(ctx, stmt, args...)
if xerr != nil {
return ExecFailure, fmt.Errorf("litesql: failed to execute query: %w", xerr)
}
affected, aerr := result.RowsAffected()
if aerr != nil {
return ExecFailure, fmt.Errorf("litesql: failed to get rows affected: %w", aerr)
}
if affected != 1 {
return ExecFailure, fmt.Errorf("litesql: expected to affect 1 row, actual was %d", affected)
}
inserted, ierr := result.LastInsertId()
if ierr != nil {
return ExecFailure, fmt.Errorf("litesql: failed to get last insert id: %w", ierr)
}
return ID(inserted), nil
}
// Exec executes the given sql query statement with args, and compares the
// number of rows affected with the given expectation. An error is returned if
// the number of rows does not match the given expectation. The constant values
// ExpectAnything, ExpectNonZero, and ExpectOneOrZero can be used for more
// complex, but common expected behaviors.
func (ldb *LiteDB) Exec(ctx scope.C, tx *sql.Tx, expectation int, stmt string, args ...any) error {
result, xerr := tx.ExecContext(ctx, stmt, args...)
if xerr != nil {
return fmt.Errorf("litesql: failed to execute query: %w", xerr)
}
affected, aerr := result.RowsAffected()
if aerr != nil {
return fmt.Errorf("litesql: failed to get rows affected: %w", aerr)
}
switch expectation {
case ExpectNonZero:
if affected == 0 {
return errors.New("litesql: expected to affect at least one row")
}
case ExpectOneOrZero:
if affected != 0 && affected != 1 {
return fmt.Errorf("litesql: expected to affect 0 or 1 row, actual: %d", affected)
}
case ExpectAnything:
return nil
default:
if affected != int64(expectation) {
return fmt.Errorf("litesql: expected to affect %d rows, actual: %d", expectation, affected)
}
}
return nil
}
// QueryRow executes the given sql query statement with the expectation of
// returning exactly one row.
func (ldb *LiteDB) QueryRow(ctx scope.C, tx *sql.Tx, stmt string, args ...any) *sql.Row {
return tx.QueryRowContext(ctx, stmt, args...)
}
// QueryRows executes the given sql query statement with the expectation of
// returning any number of rows.
//
// Must call the returned CloseFunc when finished; otherwise a connection will
// be consumed and not returned to the connection pool, causing future operations
// to hang indefinitely.
func (ldb *LiteDB) QueryRows(ctx scope.C, tx *sql.Tx, stmt string, args ...any) (*sql.Rows, CloseFunc, error) {
cursor, cerr := tx.QueryContext(ctx, stmt, args...)
closer := func() { _ = cursor.Close() }
return cursor, closer, cerr
}
// ScanFunc represents the sql.Scan function from a sql.Rows object. This is
// used as the scan argument of the QueryRows package function, invoked on
// each element in the result set of the query to build the list of resulting
// items.
type ScanFunc func(args ...any) error
// QueryRow uses the given transaction tx and the scan function to extract a
// single row from the database, using the given stmnt query with args.
//
// The scan function is provided by the caller for custom extraction of column
// values into some type T.
func QueryRow[T any](ctx scope.C, tx *sql.Tx, scan func(ScanFunc) (T, error), stmt string, args ...any) (T, error) {
row := tx.QueryRowContext(ctx, stmt, args...)
t, terr := scan(row.Scan)
if terr != nil {
var zero T
return zero, terr
}
return t, nil
}
// QueryRows uses the given transaction tx and the scan function to extract a
// set of rows from the database, using the given stmnt query with args.
//
// The scan function is provided by the caller for custom extraction of column
// values into some type T.
func QueryRows[T any](ctx scope.C, tx *sql.Tx, scan func(ScanFunc) (T, error), stmt string, args ...any) ([]T, error) {
rows, rerr := tx.QueryContext(ctx, stmt, args...)
if rerr != nil {
return nil, rerr
}
defer func() { _ = rows.Close() }()
items := make([]T, 0, 8)
for rows.Next() {
t, terr := scan(rows.Scan)
if terr != nil {
return nil, terr
}
items = append(items, t)
}
return items, rows.Err()
}