+83
delete.go
+83
delete.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
"context"
5
+
"database/sql"
6
+
"fmt"
7
+
"strings"
8
+
)
9
+
10
+
type delete_ struct {
11
+
from string
12
+
where *Expr
13
+
}
14
+
15
+
func Delete() delete_ {
16
+
return delete_{}
17
+
}
18
+
19
+
type DeleteOpt func(s *delete_)
20
+
21
+
func (s delete_) From(table string) delete_ {
22
+
s.from = table
23
+
return s
24
+
}
25
+
26
+
func (s delete_) Where(expr Expr) delete_ {
27
+
s.where = &expr
28
+
return s
29
+
}
30
+
31
+
func (s delete_) Compile() (string, []any, error) {
32
+
var sql strings.Builder
33
+
var args []any
34
+
35
+
sql.WriteString("DELETE ")
36
+
37
+
if s.from == "" {
38
+
return "", nil, fmt.Errorf("FROM clause is required")
39
+
}
40
+
sql.WriteString(" FROM ")
41
+
sql.WriteString(s.from)
42
+
43
+
if s.where != nil {
44
+
sql.WriteString(" WHERE ")
45
+
sql.WriteString(s.where.String())
46
+
47
+
args = s.where.Binds()
48
+
}
49
+
50
+
return sql.String(), args, nil
51
+
}
52
+
53
+
func (s delete_) Build(p Database) (*sql.Stmt, []any, error) {
54
+
return Build(s, p)
55
+
}
56
+
57
+
func (s delete_) MustBuild(p Database) (*sql.Stmt, []any) {
58
+
return MustBuild(s, p)
59
+
}
60
+
61
+
func (s delete_) Exec(p Database) (sql.Result, error) {
62
+
return Exec(s, p)
63
+
}
64
+
65
+
func (s delete_) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
66
+
return ExecContext(ctx, s, p)
67
+
}
68
+
69
+
func (s delete_) Query(p Database) (*sql.Rows, error) {
70
+
return Query(s, p)
71
+
}
72
+
73
+
func (s delete_) QueryContext(ctx context.Context, p Database) (*sql.Rows, error) {
74
+
return QueryContext(ctx, s, p)
75
+
}
76
+
77
+
func (s delete_) QueryRow(p Database) (*sql.Row, error) {
78
+
return QueryRow(s, p)
79
+
}
80
+
81
+
func (s delete_) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) {
82
+
return QueryRowContext(ctx, s, p)
83
+
}
+69
delete_test.go
+69
delete_test.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
_ "github.com/mattn/go-sqlite3"
5
+
"testing"
6
+
)
7
+
8
+
func TestDeleteIntegration_BasicQueries(t *testing.T) {
9
+
tests := []struct {
10
+
name string
11
+
stmt Execer
12
+
expectedRows int64
13
+
}{
14
+
{
15
+
name: "Delete all users",
16
+
stmt: Delete().From("users"),
17
+
expectedRows: 6,
18
+
},
19
+
{
20
+
name: "Delete active users only",
21
+
stmt: Delete().
22
+
From("users").
23
+
Where(Eq("active", true)),
24
+
expectedRows: 4,
25
+
},
26
+
{
27
+
name: "Select users in Engineering",
28
+
stmt: Delete().
29
+
From("users").
30
+
Where(Eq("department", "Engineering")),
31
+
expectedRows: 3,
32
+
},
33
+
{
34
+
name: "Delete users with age > 30",
35
+
stmt: Delete().
36
+
From("users").
37
+
Where(Gt("age", 30)),
38
+
expectedRows: 2,
39
+
},
40
+
{
41
+
name: "Delete users with salary between 70000 and 80000",
42
+
stmt: Delete().
43
+
From("users").
44
+
Where(Gte("salary", 70000.0).And(Lte("salary", 80000.0))),
45
+
expectedRows: 3,
46
+
},
47
+
}
48
+
49
+
for _, test := range tests {
50
+
t.Run(test.name, func(t *testing.T) {
51
+
db := setupTestDB(t)
52
+
defer db.Close()
53
+
54
+
res, err := test.stmt.Exec(db)
55
+
if err != nil {
56
+
t.Fatalf("Failed to execute query: %v", err)
57
+
}
58
+
59
+
count, err := res.RowsAffected()
60
+
if err != nil {
61
+
t.Fatalf("Failed to execute query: %v", err)
62
+
}
63
+
64
+
if count != test.expectedRows {
65
+
t.Errorf("Expected %d rows, got %d", test.expectedRows, count)
66
+
}
67
+
})
68
+
}
69
+
}
+120
expr.go
+120
expr.go
···
···
1
+
package norm
2
+
3
+
import "fmt"
4
+
5
+
type op string
6
+
7
+
type Expr struct {
8
+
kind ExprKind
9
+
10
+
ident *IdentExpr
11
+
binary *BinExpr
12
+
value *ValueExpr
13
+
}
14
+
15
+
func (e Expr) String() string {
16
+
switch e.kind {
17
+
case exprKindIdent:
18
+
return e.ident.String()
19
+
case exprKindBinary:
20
+
return e.binary.String()
21
+
case exprKindValue:
22
+
return e.value.String()
23
+
}
24
+
25
+
// unreachable
26
+
return ""
27
+
}
28
+
29
+
func (e Expr) Binds() []any {
30
+
switch e.kind {
31
+
case exprKindIdent:
32
+
return e.ident.Binds()
33
+
case exprKindBinary:
34
+
return e.binary.Binds()
35
+
case exprKindValue:
36
+
return e.value.Binds()
37
+
}
38
+
39
+
return nil
40
+
}
41
+
42
+
type ExprKind int
43
+
44
+
const (
45
+
exprKindIdent ExprKind = iota
46
+
exprKindBinary
47
+
exprKindValue
48
+
)
49
+
50
+
type IdentExpr string
51
+
52
+
func (i IdentExpr) String() string {
53
+
return string(i)
54
+
}
55
+
56
+
func (i IdentExpr) Binds() []any {
57
+
return nil
58
+
}
59
+
60
+
func (i IdentExpr) AsExpr() Expr {
61
+
return Expr{
62
+
kind: exprKindIdent,
63
+
ident: &i,
64
+
}
65
+
}
66
+
67
+
type ValueExpr struct {
68
+
inner any
69
+
}
70
+
71
+
func (v ValueExpr) String() string {
72
+
return "?"
73
+
}
74
+
75
+
func (v ValueExpr) Binds() []any {
76
+
return []any{v.inner}
77
+
}
78
+
79
+
func (v ValueExpr) AsExpr() Expr {
80
+
return Expr{
81
+
kind: exprKindValue,
82
+
value: &v,
83
+
}
84
+
}
85
+
86
+
type BinExpr struct {
87
+
left Expr
88
+
op op
89
+
right Expr
90
+
}
91
+
92
+
func (b BinExpr) String() string {
93
+
return fmt.Sprintf("(%s) %s (%s)", b.left.String(), b.op, b.right.String())
94
+
}
95
+
96
+
func (b BinExpr) Binds() []any {
97
+
binds := b.left.Binds()
98
+
binds = append(binds, b.right.Binds()...)
99
+
return binds
100
+
}
101
+
102
+
func (b BinExpr) AsExpr() Expr {
103
+
return Expr{
104
+
kind: exprKindBinary,
105
+
binary: &b,
106
+
}
107
+
}
108
+
109
+
func buildBinExpr(left IdentExpr, op op, right any) Expr {
110
+
return BinExpr{left.AsExpr(), op, ValueExpr{right}.AsExpr()}.AsExpr()
111
+
}
112
+
func Eq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "=", right) }
113
+
func Neq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<>", right) }
114
+
func Gt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">", right) }
115
+
func Gte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">=", right) }
116
+
func Lt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<", right) }
117
+
func Lte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<=", right) }
118
+
119
+
func (l Expr) And(r Expr) Expr { return BinExpr{l, "and", r}.AsExpr() }
120
+
func (l Expr) Or(r Expr) Expr { return BinExpr{l, "or", r}.AsExpr() }
+102
expr_test.go
+102
expr_test.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
"testing"
5
+
)
6
+
7
+
func TestIdentExpr(t *testing.T) {
8
+
ident := IdentExpr("username")
9
+
10
+
if ident.String() != "username" {
11
+
t.Errorf("Expected 'username', got '%s'", ident.String())
12
+
}
13
+
14
+
if ident.Binds() != nil {
15
+
t.Errorf("Expected nil binds, got %v", ident.Binds())
16
+
}
17
+
18
+
expr := ident.AsExpr()
19
+
if expr.kind != exprKindIdent {
20
+
t.Errorf("Expected exprKindIdent, got %d", expr.kind)
21
+
}
22
+
23
+
if expr.String() != "username" {
24
+
t.Errorf("Expected 'username', got '%s'", expr.String())
25
+
}
26
+
}
27
+
28
+
func TestValueExpr(t *testing.T) {
29
+
value := ValueExpr{inner: "test"}
30
+
31
+
if value.String() != "?" {
32
+
t.Errorf("Expected '?', got '%s'", value.String())
33
+
}
34
+
35
+
if value.Binds()[0] != "test" {
36
+
t.Errorf("Expected %q, got %v", []any{"test"}, value.Binds())
37
+
}
38
+
39
+
expr := value.AsExpr()
40
+
if expr.kind != exprKindValue {
41
+
t.Errorf("Expected exprKindValue, got %d", expr.kind)
42
+
}
43
+
}
44
+
45
+
func TestBinaryExpressions(t *testing.T) {
46
+
tests := []struct {
47
+
name string
48
+
expr Expr
49
+
expected string
50
+
}{
51
+
{"Eq", Eq("age", 25), "(age) = (?)"},
52
+
{"Neq", Neq("status", "active"), "(status) <> (?)"},
53
+
{"Gt", Gt("score", 100), "(score) > (?)"},
54
+
{"Gte", Gte("rating", 4.5), "(rating) >= (?)"},
55
+
{"Lt", Lt("count", 10), "(count) < (?)"},
56
+
{"Lte", Lte("price", 99.99), "(price) <= (?)"},
57
+
}
58
+
59
+
for _, test := range tests {
60
+
t.Run(test.name, func(t *testing.T) {
61
+
if test.expr.String() != test.expected {
62
+
t.Errorf("Expected '%s', got '%s'", test.expected, test.expr.String())
63
+
}
64
+
65
+
if test.expr.kind != exprKindBinary {
66
+
t.Errorf("Expected exprKindBinary, got %d", test.expr.kind)
67
+
}
68
+
})
69
+
}
70
+
}
71
+
72
+
func TestLogicalOperators(t *testing.T) {
73
+
left := Eq("age", 25)
74
+
right := Eq("status", "active")
75
+
76
+
andExpr := left.And(right)
77
+
expectedAnd := "((age) = (?)) and ((status) = (?))"
78
+
if andExpr.String() != expectedAnd {
79
+
t.Errorf("Expected '%s', got '%s'", expectedAnd, andExpr.String())
80
+
}
81
+
82
+
orExpr := left.Or(right)
83
+
expectedOr := "((age) = (?)) or ((status) = (?))"
84
+
if orExpr.String() != expectedOr {
85
+
t.Errorf("Expected '%s', got '%s'", expectedOr, orExpr.String())
86
+
}
87
+
}
88
+
89
+
func TestComplexExpressions(t *testing.T) {
90
+
age := Eq("age", 25)
91
+
status := Eq("status", "active")
92
+
score := Gt("score", 100)
93
+
94
+
complex := age.
95
+
And(status).
96
+
Or(score)
97
+
expected := "(((age) = (?)) and ((status) = (?))) or ((score) > (?))"
98
+
99
+
if complex.String() != expected {
100
+
t.Errorf("Expected '%s', got '%s'", expected, complex.String())
101
+
}
102
+
}
+143
insert.go
+143
insert.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
"context"
5
+
"database/sql"
6
+
"fmt"
7
+
"strings"
8
+
)
9
+
10
+
type insert struct {
11
+
into string
12
+
or InsertOr
13
+
values []struct {
14
+
col string
15
+
val any
16
+
}
17
+
}
18
+
19
+
type InsertOr int
20
+
21
+
const (
22
+
None InsertOr = iota
23
+
Abort
24
+
Fail
25
+
Ignore
26
+
Replace
27
+
Rollback
28
+
)
29
+
30
+
func (i InsertOr) String() string {
31
+
switch i {
32
+
case Abort:
33
+
return "ABORT"
34
+
case Fail:
35
+
return "FAIL"
36
+
case Ignore:
37
+
return "IGNORE"
38
+
case Replace:
39
+
return "REPLACE"
40
+
case Rollback:
41
+
return "ROLLBACK"
42
+
default:
43
+
return ""
44
+
}
45
+
}
46
+
47
+
func Insert() insert {
48
+
return insert{}
49
+
}
50
+
51
+
type InsertOpt func(s *insert)
52
+
53
+
func (s insert) Into(table string) insert {
54
+
s.into = table
55
+
return s
56
+
}
57
+
58
+
func (s insert) Or(option InsertOr) insert {
59
+
s.or = option
60
+
return s
61
+
}
62
+
63
+
func (s insert) Value(column string, arg any) insert {
64
+
s.values = append(s.values, struct {
65
+
col string
66
+
val any
67
+
}{
68
+
column, arg,
69
+
})
70
+
return s
71
+
}
72
+
73
+
func (s insert) Compile() (string, []any, error) {
74
+
var sql strings.Builder
75
+
var args []any
76
+
77
+
sql.WriteString("INSERT ")
78
+
79
+
if s.or != None {
80
+
sql.WriteString("OR ")
81
+
sql.WriteString(s.or.String())
82
+
sql.WriteString(" ")
83
+
}
84
+
85
+
if s.into == "" {
86
+
return "", nil, fmt.Errorf("INTO clause is required")
87
+
}
88
+
sql.WriteString("INTO ")
89
+
sql.WriteString(s.into)
90
+
91
+
if len(s.values) == 0 {
92
+
return "", nil, fmt.Errorf("no values supplied")
93
+
}
94
+
95
+
sql.WriteString(" (")
96
+
97
+
for i, v := range s.values {
98
+
if i != 0 {
99
+
sql.WriteString(", ")
100
+
}
101
+
102
+
sql.WriteString(v.col)
103
+
args = append(args, v.val)
104
+
}
105
+
106
+
sql.WriteString(") VALUES (")
107
+
sql.WriteString(strings.TrimSuffix(strings.Repeat("?, ", len(s.values)), ", "))
108
+
sql.WriteString(")")
109
+
110
+
return sql.String(), args, nil
111
+
}
112
+
113
+
func (s insert) Build(p Database) (*sql.Stmt, []any, error) {
114
+
return Build(s, p)
115
+
}
116
+
117
+
func (s insert) MustBuild(p Database) (*sql.Stmt, []any) {
118
+
return MustBuild(s, p)
119
+
}
120
+
121
+
func (s insert) Exec(p Database) (sql.Result, error) {
122
+
return Exec(s, p)
123
+
}
124
+
125
+
func (s insert) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
126
+
return ExecContext(ctx, s, p)
127
+
}
128
+
129
+
func (s insert) Query(p Database) (*sql.Rows, error) {
130
+
return Query(s, p)
131
+
}
132
+
133
+
func (s insert) QueryContext(ctx context.Context, p Database) (*sql.Rows, error) {
134
+
return QueryContext(ctx, s, p)
135
+
}
136
+
137
+
func (s insert) QueryRow(p Database) (*sql.Row, error) {
138
+
return QueryRow(s, p)
139
+
}
140
+
141
+
func (s insert) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) {
142
+
return QueryRowContext(ctx, s, p)
143
+
}
+58
insert_test.go
+58
insert_test.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
_ "github.com/mattn/go-sqlite3"
5
+
"testing"
6
+
)
7
+
8
+
func TestInsertBuild_Success(t *testing.T) {
9
+
tests := []struct {
10
+
name string
11
+
stmt Compiler
12
+
expectedSql string
13
+
expectedArgs []any
14
+
}{
15
+
{
16
+
name: "Simple insert",
17
+
stmt: Insert().Into("users").Value("name", "John"),
18
+
expectedSql: "INSERT INTO users (name) VALUES (?)",
19
+
expectedArgs: []any{"John"},
20
+
},
21
+
{
22
+
name: "Replace clause",
23
+
stmt: Insert().Or(Replace).Into("users").Value("name", "John"),
24
+
expectedSql: "INSERT OR REPLACE INTO users (name) VALUES (?)",
25
+
expectedArgs: []any{"John"},
26
+
},
27
+
{
28
+
name: "More values",
29
+
stmt: Insert().Into("users").Value("name", "John").Value("age", 35),
30
+
expectedSql: "INSERT INTO users (name, age) VALUES (?, ?)",
31
+
expectedArgs: []any{"John", 35},
32
+
},
33
+
}
34
+
35
+
for _, test := range tests {
36
+
t.Run(test.name, func(t *testing.T) {
37
+
sql, args, err := test.stmt.Compile()
38
+
39
+
if err != nil {
40
+
t.Errorf("Expected no error, got %v", err)
41
+
}
42
+
43
+
if sql != test.expectedSql {
44
+
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
45
+
}
46
+
47
+
if len(args) != len(test.expectedArgs) {
48
+
t.Errorf("Expected '%d' args, got '%d' args", len(test.expectedArgs), len(args))
49
+
}
50
+
51
+
for i := range len(args) {
52
+
if args[i] != test.expectedArgs[i] {
53
+
t.Errorf("Expected '%v', got '%v' at index %d", test.expectedArgs[i], args[i], i)
54
+
}
55
+
}
56
+
})
57
+
}
58
+
}
-434
query.go
-434
query.go
···
1
-
package norm
2
-
3
-
import (
4
-
"fmt"
5
-
"strconv"
6
-
"strings"
7
-
)
8
-
9
-
// use with prepared statements
10
-
func Placeholder[T any]() T {
11
-
var zero T
12
-
return zero
13
-
}
14
-
15
-
type op string
16
-
17
-
type Expr struct {
18
-
kind ExprKind
19
-
20
-
ident *IdentExpr
21
-
binary *BinExpr
22
-
value *ValueExpr
23
-
}
24
-
25
-
func (e Expr) String() string {
26
-
switch e.kind {
27
-
case exprKindIdent:
28
-
return e.ident.String()
29
-
case exprKindBinary:
30
-
return e.binary.String()
31
-
case exprKindValue:
32
-
return e.value.String()
33
-
}
34
-
35
-
// unreachable
36
-
return ""
37
-
}
38
-
39
-
func (e Expr) Binds() []any {
40
-
switch e.kind {
41
-
case exprKindIdent:
42
-
return e.ident.Binds()
43
-
case exprKindBinary:
44
-
return e.binary.Binds()
45
-
case exprKindValue:
46
-
return e.value.Binds()
47
-
}
48
-
49
-
return nil
50
-
}
51
-
52
-
type ExprKind int
53
-
54
-
const (
55
-
exprKindIdent ExprKind = iota
56
-
exprKindBinary
57
-
exprKindValue
58
-
)
59
-
60
-
type IdentExpr string
61
-
62
-
func (i IdentExpr) String() string {
63
-
return string(i)
64
-
}
65
-
66
-
func (i IdentExpr) Binds() []any {
67
-
return nil
68
-
}
69
-
70
-
func (i IdentExpr) AsExpr() Expr {
71
-
return Expr{
72
-
kind: exprKindIdent,
73
-
ident: &i,
74
-
}
75
-
}
76
-
77
-
type ValueExpr struct {
78
-
inner any
79
-
}
80
-
81
-
func (v ValueExpr) String() string {
82
-
return "?"
83
-
}
84
-
85
-
func (v ValueExpr) Binds() []any {
86
-
return []any{v.inner}
87
-
}
88
-
89
-
func (v ValueExpr) AsExpr() Expr {
90
-
return Expr{
91
-
kind: exprKindValue,
92
-
value: &v,
93
-
}
94
-
}
95
-
96
-
type BinExpr struct {
97
-
left Expr
98
-
op op
99
-
right Expr
100
-
}
101
-
102
-
func (b BinExpr) String() string {
103
-
return fmt.Sprintf("(%s) %s (%s)", b.left.String(), b.op, b.right.String())
104
-
}
105
-
106
-
func (b BinExpr) Binds() []any {
107
-
binds := b.left.Binds()
108
-
binds = append(binds, b.right.Binds()...)
109
-
return binds
110
-
}
111
-
112
-
func (b BinExpr) AsExpr() Expr {
113
-
return Expr{
114
-
kind: exprKindBinary,
115
-
binary: &b,
116
-
}
117
-
}
118
-
119
-
func buildBinExpr(left IdentExpr, op op, right any) Expr {
120
-
return BinExpr{left.AsExpr(), op, ValueExpr{right}.AsExpr()}.AsExpr()
121
-
}
122
-
func Eq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "=", right) }
123
-
func Neq(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<>", right) }
124
-
func Gt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">", right) }
125
-
func Gte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), ">=", right) }
126
-
func Lt(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<", right) }
127
-
func Lte(left string, right any) Expr { return buildBinExpr(IdentExpr(left), "<=", right) }
128
-
129
-
func (l Expr) And(r Expr) Expr { return BinExpr{l, "and", r}.AsExpr() }
130
-
func (l Expr) Or(r Expr) Expr { return BinExpr{l, "or", r}.AsExpr() }
131
-
132
-
type select_ struct {
133
-
resultColumns []string // TODO: strongly type result columns
134
-
from string // table-or-subquery expr
135
-
where *Expr
136
-
orderBy []orderBy
137
-
groupBy []groupBy
138
-
limit *limit
139
-
}
140
-
141
-
type orderBy struct {
142
-
field string
143
-
direction Direction
144
-
}
145
-
146
-
type groupBy struct {
147
-
field string
148
-
}
149
-
150
-
type limit struct {
151
-
limit int
152
-
}
153
-
154
-
func Select(cols ...string) select_ {
155
-
return select_{
156
-
resultColumns: cols,
157
-
}
158
-
}
159
-
160
-
type SelectOpt func(s *select_)
161
-
162
-
func (s select_) From(table string) select_ {
163
-
s.from = table
164
-
return s
165
-
}
166
-
167
-
func (s select_) Where(expr Expr) select_ {
168
-
s.where = &expr
169
-
return s
170
-
}
171
-
172
-
type Direction string
173
-
174
-
const (
175
-
Ascending Direction = "asc"
176
-
Descending Direction = "desc"
177
-
)
178
-
179
-
func (s select_) OrderBy(field string, direction Direction) select_ {
180
-
s.orderBy = append(s.orderBy, orderBy{
181
-
field: field,
182
-
direction: direction,
183
-
})
184
-
return s
185
-
}
186
-
187
-
func (s select_) GroupBy(field string) select_ {
188
-
s.groupBy = append(s.groupBy, groupBy{
189
-
field: field,
190
-
})
191
-
return s
192
-
}
193
-
194
-
func (s select_) Limit(i int) select_ {
195
-
s.limit = &limit{
196
-
limit: i,
197
-
}
198
-
return s
199
-
}
200
-
201
-
func (s select_) Build() (string, []any, error) {
202
-
var sql strings.Builder
203
-
var args []any
204
-
205
-
sql.WriteString("SELECT ")
206
-
if len(s.resultColumns) == 0 {
207
-
return "", nil, fmt.Errorf("result columns empty")
208
-
} else {
209
-
for i, col := range s.resultColumns {
210
-
if i > 0 {
211
-
sql.WriteString(", ")
212
-
}
213
-
sql.WriteString(col)
214
-
}
215
-
}
216
-
217
-
if s.from == "" {
218
-
return "", nil, fmt.Errorf("FROM clause is required")
219
-
}
220
-
sql.WriteString(" FROM ")
221
-
sql.WriteString(s.from)
222
-
223
-
if s.where != nil {
224
-
sql.WriteString(" WHERE ")
225
-
sql.WriteString(s.where.String())
226
-
227
-
args = s.where.Binds()
228
-
}
229
-
230
-
// GROUP BY clause
231
-
if len(s.groupBy) > 0 {
232
-
sql.WriteString(" GROUP BY ")
233
-
for i, gb := range s.groupBy {
234
-
if i > 0 {
235
-
sql.WriteString(", ")
236
-
}
237
-
sql.WriteString(gb.field)
238
-
}
239
-
}
240
-
241
-
// ORDER BY clause
242
-
if len(s.orderBy) > 0 {
243
-
sql.WriteString(" ORDER BY ")
244
-
for i, ob := range s.orderBy {
245
-
if i > 0 {
246
-
sql.WriteString(", ")
247
-
}
248
-
sql.WriteString(ob.field)
249
-
sql.WriteString(" ")
250
-
sql.WriteString(string(ob.direction))
251
-
}
252
-
}
253
-
254
-
// LIMIT clause
255
-
if s.limit != nil {
256
-
if s.limit.limit <= 0 {
257
-
return "", nil, fmt.Errorf("LIMIT must be positive, got %d", s.limit.limit)
258
-
}
259
-
sql.WriteString(" LIMIT ")
260
-
sql.WriteString(strconv.Itoa(s.limit.limit))
261
-
}
262
-
263
-
return sql.String(), args, nil
264
-
}
265
-
266
-
func (s select_) MustBuild() (string, []any) {
267
-
sql, args, err := s.Build()
268
-
if err != nil {
269
-
panic(err)
270
-
}
271
-
return sql, args
272
-
}
273
-
274
-
type delete_ struct {
275
-
from string
276
-
where *Expr
277
-
}
278
-
279
-
func Delete() delete_ {
280
-
return delete_{}
281
-
}
282
-
283
-
type DeleteOpt func(s *delete_)
284
-
285
-
func (s delete_) From(table string) delete_ {
286
-
s.from = table
287
-
return s
288
-
}
289
-
290
-
func (s delete_) Where(expr Expr) delete_ {
291
-
s.where = &expr
292
-
return s
293
-
}
294
-
295
-
func (s delete_) Build() (string, []any, error) {
296
-
var sql strings.Builder
297
-
var args []any
298
-
299
-
sql.WriteString("DELETE ")
300
-
301
-
if s.from == "" {
302
-
return "", nil, fmt.Errorf("FROM clause is required")
303
-
}
304
-
sql.WriteString(" FROM ")
305
-
sql.WriteString(s.from)
306
-
307
-
if s.where != nil {
308
-
sql.WriteString(" WHERE ")
309
-
sql.WriteString(s.where.String())
310
-
311
-
args = s.where.Binds()
312
-
}
313
-
314
-
return sql.String(), args, nil
315
-
}
316
-
317
-
func (s delete_) MustBuild() (string, []any) {
318
-
sql, args, err := s.Build()
319
-
if err != nil {
320
-
panic(err)
321
-
}
322
-
return sql, args
323
-
}
324
-
325
-
type insert struct {
326
-
into string
327
-
or InsertOr
328
-
values []struct {
329
-
col string
330
-
val any
331
-
}
332
-
}
333
-
334
-
type InsertOr int
335
-
336
-
const (
337
-
None InsertOr = iota
338
-
Abort
339
-
Fail
340
-
Ignore
341
-
Replace
342
-
Rollback
343
-
)
344
-
345
-
func (i InsertOr) String() string {
346
-
switch i {
347
-
case Abort:
348
-
return "ABORT"
349
-
case Fail:
350
-
return "FAIL"
351
-
case Ignore:
352
-
return "IGNORE"
353
-
case Replace:
354
-
return "REPLACE"
355
-
case Rollback:
356
-
return "ROLLBACK"
357
-
default:
358
-
return ""
359
-
}
360
-
}
361
-
362
-
func Insert() insert {
363
-
return insert{}
364
-
}
365
-
366
-
type InsertOpt func(s *insert)
367
-
368
-
func (s insert) Into(table string) insert {
369
-
s.into = table
370
-
return s
371
-
}
372
-
373
-
func (s insert) Or(option InsertOr) insert {
374
-
s.or = option
375
-
return s
376
-
}
377
-
378
-
func (s insert) Value(column string, arg any) insert {
379
-
s.values = append(s.values, struct {
380
-
col string
381
-
val any
382
-
}{
383
-
column, arg,
384
-
})
385
-
return s
386
-
}
387
-
388
-
func (s insert) Build() (string, []any, error) {
389
-
var sql strings.Builder
390
-
var args []any
391
-
392
-
sql.WriteString("INSERT ")
393
-
394
-
if s.or != None {
395
-
sql.WriteString("OR ")
396
-
sql.WriteString(s.or.String())
397
-
sql.WriteString(" ")
398
-
}
399
-
400
-
if s.into == "" {
401
-
return "", nil, fmt.Errorf("INTO clause is required")
402
-
}
403
-
sql.WriteString("INTO ")
404
-
sql.WriteString(s.into)
405
-
406
-
if len(s.values) == 0 {
407
-
return "", nil, fmt.Errorf("no values supplied")
408
-
}
409
-
410
-
sql.WriteString(" (")
411
-
412
-
for i, v := range s.values {
413
-
if i != 0 {
414
-
sql.WriteString(", ")
415
-
}
416
-
417
-
sql.WriteString(v.col)
418
-
args = append(args, v.val)
419
-
}
420
-
421
-
sql.WriteString(") VALUES (")
422
-
sql.WriteString(strings.TrimSuffix(strings.Repeat("?, ", len(s.values)), ", "))
423
-
sql.WriteString(")")
424
-
425
-
return sql.String(), args, nil
426
-
}
427
-
428
-
func (s insert) MustBuild() (string, []any) {
429
-
sql, args, err := s.Build()
430
-
if err != nil {
431
-
panic(err)
432
-
}
433
-
return sql, args
434
-
}
···
+13
-261
query_test.go
select_test.go
+13
-261
query_test.go
select_test.go
···
1
package norm
2
3
import (
4
"testing"
5
-
6
-
_ "github.com/mattn/go-sqlite3"
7
)
8
9
-
func TestIdentExpr(t *testing.T) {
10
-
ident := IdentExpr("username")
11
-
12
-
if ident.String() != "username" {
13
-
t.Errorf("Expected 'username', got '%s'", ident.String())
14
-
}
15
-
16
-
if ident.Binds() != nil {
17
-
t.Errorf("Expected nil binds, got %v", ident.Binds())
18
-
}
19
-
20
-
expr := ident.AsExpr()
21
-
if expr.kind != exprKindIdent {
22
-
t.Errorf("Expected exprKindIdent, got %d", expr.kind)
23
-
}
24
-
25
-
if expr.String() != "username" {
26
-
t.Errorf("Expected 'username', got '%s'", expr.String())
27
-
}
28
-
}
29
-
30
-
func TestValueExpr(t *testing.T) {
31
-
value := ValueExpr{inner: "test"}
32
-
33
-
if value.String() != "?" {
34
-
t.Errorf("Expected '?', got '%s'", value.String())
35
-
}
36
-
37
-
if value.Binds()[0] != "test" {
38
-
t.Errorf("Expected %q, got %v", []any{"test"}, value.Binds())
39
-
}
40
-
41
-
expr := value.AsExpr()
42
-
if expr.kind != exprKindValue {
43
-
t.Errorf("Expected exprKindValue, got %d", expr.kind)
44
-
}
45
-
}
46
-
47
-
func TestBinaryExpressions(t *testing.T) {
48
-
tests := []struct {
49
-
name string
50
-
expr Expr
51
-
expected string
52
-
}{
53
-
{"Eq", Eq("age", 25), "(age) = (?)"},
54
-
{"Neq", Neq("status", "active"), "(status) <> (?)"},
55
-
{"Gt", Gt("score", 100), "(score) > (?)"},
56
-
{"Gte", Gte("rating", 4.5), "(rating) >= (?)"},
57
-
{"Lt", Lt("count", 10), "(count) < (?)"},
58
-
{"Lte", Lte("price", 99.99), "(price) <= (?)"},
59
-
}
60
-
61
-
for _, test := range tests {
62
-
t.Run(test.name, func(t *testing.T) {
63
-
if test.expr.String() != test.expected {
64
-
t.Errorf("Expected '%s', got '%s'", test.expected, test.expr.String())
65
-
}
66
-
67
-
if test.expr.kind != exprKindBinary {
68
-
t.Errorf("Expected exprKindBinary, got %d", test.expr.kind)
69
-
}
70
-
})
71
-
}
72
-
}
73
-
74
-
func TestLogicalOperators(t *testing.T) {
75
-
left := Eq("age", 25)
76
-
right := Eq("status", "active")
77
-
78
-
andExpr := left.And(right)
79
-
expectedAnd := "((age) = (?)) and ((status) = (?))"
80
-
if andExpr.String() != expectedAnd {
81
-
t.Errorf("Expected '%s', got '%s'", expectedAnd, andExpr.String())
82
-
}
83
-
84
-
orExpr := left.Or(right)
85
-
expectedOr := "((age) = (?)) or ((status) = (?))"
86
-
if orExpr.String() != expectedOr {
87
-
t.Errorf("Expected '%s', got '%s'", expectedOr, orExpr.String())
88
-
}
89
-
}
90
-
91
-
func TestComplexExpressions(t *testing.T) {
92
-
age := Eq("age", 25)
93
-
status := Eq("status", "active")
94
-
score := Gt("score", 100)
95
-
96
-
complex := age.
97
-
And(status).
98
-
Or(score)
99
-
expected := "(((age) = (?)) and ((status) = (?))) or ((score) > (?))"
100
-
101
-
if complex.String() != expected {
102
-
t.Errorf("Expected '%s', got '%s'", expected, complex.String())
103
-
}
104
-
}
105
-
106
func TestSelectBasic(t *testing.T) {
107
s := Select("name", "age")
108
···
159
func TestSelectBuild_Success(t *testing.T) {
160
tests := []struct {
161
name string
162
-
stmt select_
163
expectedSql string
164
expectedArgs []any
165
}{
···
225
226
for _, test := range tests {
227
t.Run(test.name, func(t *testing.T) {
228
-
sql, args, err := test.stmt.Build()
229
230
if err != nil {
231
t.Errorf("Expected no error, got %v", err)
···
251
func TestSelectBuild_Errors(t *testing.T) {
252
tests := []struct {
253
name string
254
-
stmt select_
255
expectedError string
256
}{
257
{
···
282
283
for _, test := range tests {
284
t.Run(test.name, func(t *testing.T) {
285
-
sql, args, err := test.stmt.Build()
286
287
if err == nil {
288
t.Error("Expected error, got nil")
···
300
t.Errorf("Expected empty args on error, got '%q'", args)
301
}
302
})
303
-
}
304
-
}
305
-
306
-
func TestDirections(t *testing.T) {
307
-
if Ascending != "asc" {
308
-
t.Errorf("Expected ASC to be 'asc', got '%s'", Ascending)
309
-
}
310
-
311
-
if Descending != "desc" {
312
-
t.Errorf("Expected DESC to be 'desc', got '%s'", Descending)
313
}
314
}
315
···
402
403
for _, test := range tests {
404
t.Run(test.name, func(t *testing.T) {
405
-
sql, args, err := test.stmt.Build()
406
if err != nil {
407
t.Fatalf("Failed to build query: %v", err)
408
}
409
410
-
rows, err := db.Query(sql, args...)
411
-
if err != nil {
412
-
t.Fatalf("Failed to execute query: %v", err)
413
-
}
414
-
defer rows.Close()
415
-
416
count := 0
417
for rows.Next() {
418
count++
···
438
Or(Eq("department", "Marketing").And(Lt("age", 30))),
439
)
440
441
-
sql, args := s.MustBuild()
442
-
443
-
rows, err := db.Query(sql, args...)
444
if err != nil {
445
t.Fatalf("Failed to execute query: %v", err)
446
}
···
479
OrderBy("department", Ascending).
480
OrderBy("age", Descending)
481
482
-
sql, args := s.MustBuild()
483
-
484
-
rows, err := db.Query(sql, args...)
485
if err != nil {
486
t.Fatalf("Failed to execute query: %v", err)
487
}
···
522
GroupBy("department").
523
OrderBy("user_count", Descending)
524
525
-
sql, args := s.MustBuild()
526
-
527
-
rows, err := db.Query(sql, args...)
528
if err != nil {
529
t.Fatalf("Failed to execute query: %v", err)
530
}
···
563
OrderBy("salary", Descending).
564
Limit(2)
565
566
-
sql, args := s.MustBuild()
567
-
568
-
rows, err := db.Query(sql, args...)
569
if err != nil {
570
t.Fatalf("Failed to execute query: %v", err)
571
}
···
610
From("users").
611
Where(Eq("name", "John Doe"))
612
613
-
sql, args := s.MustBuild()
614
-
615
-
rows, err := db.Query(sql, args...)
616
if err != nil {
617
t.Fatalf("Failed to execute query: %v", err)
618
}
···
633
From("users").
634
Where(Eq("active", false))
635
636
-
sql, args := s.MustBuild()
637
-
638
-
rows, err := db.Query(sql, args...)
639
if err != nil {
640
t.Fatalf("Failed to execute query: %v", err)
641
}
···
656
From("users").
657
Where(Gte("salary", 80000.0))
658
659
-
sql, args := s.MustBuild()
660
-
661
-
rows, err := db.Query(sql, args...)
662
if err != nil {
663
t.Fatalf("Failed to execute query: %v", err)
664
}
···
674
}
675
})
676
}
677
-
678
-
func TestDeleteIntegration_BasicQueries(t *testing.T) {
679
-
tests := []struct {
680
-
name string
681
-
stmt delete_
682
-
expectedRows int64
683
-
}{
684
-
{
685
-
name: "Delete all users",
686
-
stmt: Delete().From("users"),
687
-
expectedRows: 6,
688
-
},
689
-
{
690
-
name: "Delete active users only",
691
-
stmt: Delete().
692
-
From("users").
693
-
Where(Eq("active", true)),
694
-
expectedRows: 4,
695
-
},
696
-
{
697
-
name: "Select users in Engineering",
698
-
stmt: Delete().
699
-
From("users").
700
-
Where(Eq("department", "Engineering")),
701
-
expectedRows: 3,
702
-
},
703
-
{
704
-
name: "Delete users with age > 30",
705
-
stmt: Delete().
706
-
From("users").
707
-
Where(Gt("age", 30)),
708
-
expectedRows: 2,
709
-
},
710
-
{
711
-
name: "Delete users with salary between 70000 and 80000",
712
-
stmt: Delete().
713
-
From("users").
714
-
Where(Gte("salary", 70000.0).And(Lte("salary", 80000.0))),
715
-
expectedRows: 3,
716
-
},
717
-
}
718
-
719
-
for _, test := range tests {
720
-
t.Run(test.name, func(t *testing.T) {
721
-
db := setupTestDB(t)
722
-
defer db.Close()
723
-
724
-
sql, args, err := test.stmt.Build()
725
-
if err != nil {
726
-
t.Fatalf("Failed to build query: %v", err)
727
-
}
728
-
729
-
res, err := db.Exec(sql, args...)
730
-
if err != nil {
731
-
t.Fatalf("Failed to execute query: %v", err)
732
-
}
733
-
734
-
count, err := res.RowsAffected()
735
-
if err != nil {
736
-
t.Fatalf("Failed to execute query: %v", err)
737
-
}
738
-
739
-
if count != test.expectedRows {
740
-
t.Errorf("Expected %d rows, got %d", test.expectedRows, count)
741
-
}
742
-
})
743
-
}
744
-
}
745
-
746
-
func TestInsertBuild_Success(t *testing.T) {
747
-
tests := []struct {
748
-
name string
749
-
stmt insert
750
-
expectedSql string
751
-
expectedArgs []any
752
-
}{
753
-
{
754
-
name: "Simple insert",
755
-
stmt: Insert().Into("users").Value("name", "John"),
756
-
expectedSql: "INSERT INTO users (name) VALUES (?)",
757
-
expectedArgs: []any{"John"},
758
-
},
759
-
{
760
-
name: "Replace clause",
761
-
stmt: Insert().Or(Replace).Into("users").Value("name", "John"),
762
-
expectedSql: "INSERT OR REPLACE INTO users (name) VALUES (?)",
763
-
expectedArgs: []any{"John"},
764
-
},
765
-
{
766
-
name: "More values",
767
-
stmt: Insert().Into("users").Value("name", "John").Value("age", 35),
768
-
expectedSql: "INSERT INTO users (name, age) VALUES (?, ?)",
769
-
expectedArgs: []any{"John", 35},
770
-
},
771
-
}
772
-
773
-
for _, test := range tests {
774
-
t.Run(test.name, func(t *testing.T) {
775
-
sql, args, err := test.stmt.Build()
776
-
777
-
if err != nil {
778
-
t.Errorf("Expected no error, got %v", err)
779
-
}
780
-
781
-
if sql != test.expectedSql {
782
-
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
783
-
}
784
-
785
-
if len(args) != len(test.expectedArgs) {
786
-
t.Errorf("Expected '%d' args, got '%d' args", len(test.expectedArgs), len(args))
787
-
}
788
-
789
-
for i := range len(args) {
790
-
if args[i] != test.expectedArgs[i] {
791
-
t.Errorf("Expected '%v', got '%v' at index %d", test.expectedArgs[i], args[i], i)
792
-
}
793
-
}
794
-
})
795
-
}
796
-
}
···
1
package norm
2
3
import (
4
+
_ "github.com/mattn/go-sqlite3"
5
"testing"
6
)
7
8
func TestSelectBasic(t *testing.T) {
9
s := Select("name", "age")
10
···
61
func TestSelectBuild_Success(t *testing.T) {
62
tests := []struct {
63
name string
64
+
stmt Compiler
65
expectedSql string
66
expectedArgs []any
67
}{
···
127
128
for _, test := range tests {
129
t.Run(test.name, func(t *testing.T) {
130
+
sql, args, err := test.stmt.Compile()
131
132
if err != nil {
133
t.Errorf("Expected no error, got %v", err)
···
153
func TestSelectBuild_Errors(t *testing.T) {
154
tests := []struct {
155
name string
156
+
stmt Compiler
157
expectedError string
158
}{
159
{
···
184
185
for _, test := range tests {
186
t.Run(test.name, func(t *testing.T) {
187
+
sql, args, err := test.stmt.Compile()
188
189
if err == nil {
190
t.Error("Expected error, got nil")
···
202
t.Errorf("Expected empty args on error, got '%q'", args)
203
}
204
})
205
}
206
}
207
···
294
295
for _, test := range tests {
296
t.Run(test.name, func(t *testing.T) {
297
+
rows, err := test.stmt.Query(db)
298
if err != nil {
299
t.Fatalf("Failed to build query: %v", err)
300
}
301
302
count := 0
303
for rows.Next() {
304
count++
···
324
Or(Eq("department", "Marketing").And(Lt("age", 30))),
325
)
326
327
+
rows, err := s.Query(db)
328
if err != nil {
329
t.Fatalf("Failed to execute query: %v", err)
330
}
···
363
OrderBy("department", Ascending).
364
OrderBy("age", Descending)
365
366
+
rows, err := s.Query(db)
367
if err != nil {
368
t.Fatalf("Failed to execute query: %v", err)
369
}
···
404
GroupBy("department").
405
OrderBy("user_count", Descending)
406
407
+
rows, err := s.Query(db)
408
if err != nil {
409
t.Fatalf("Failed to execute query: %v", err)
410
}
···
443
OrderBy("salary", Descending).
444
Limit(2)
445
446
+
rows, err := s.Query(db)
447
if err != nil {
448
t.Fatalf("Failed to execute query: %v", err)
449
}
···
488
From("users").
489
Where(Eq("name", "John Doe"))
490
491
+
rows, err := s.Query(db)
492
if err != nil {
493
t.Fatalf("Failed to execute query: %v", err)
494
}
···
509
From("users").
510
Where(Eq("active", false))
511
512
+
rows, err := s.Query(db)
513
if err != nil {
514
t.Fatalf("Failed to execute query: %v", err)
515
}
···
530
From("users").
531
Where(Gte("salary", 80000.0))
532
533
+
rows, err := s.Query(db)
534
if err != nil {
535
t.Fatalf("Failed to execute query: %v", err)
536
}
···
546
}
547
})
548
}
+168
select.go
+168
select.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
"context"
5
+
"database/sql"
6
+
"fmt"
7
+
"strconv"
8
+
"strings"
9
+
)
10
+
11
+
type select_ struct {
12
+
resultColumns []string // TODO: strongly type result columns
13
+
from string // table-or-subquery expr
14
+
where *Expr
15
+
orderBy []orderBy
16
+
groupBy []groupBy
17
+
limit *limit
18
+
}
19
+
20
+
type orderBy struct {
21
+
field string
22
+
direction Direction
23
+
}
24
+
25
+
type groupBy struct {
26
+
field string
27
+
}
28
+
29
+
type limit struct {
30
+
limit int
31
+
}
32
+
33
+
func Select(cols ...string) select_ {
34
+
return select_{
35
+
resultColumns: cols,
36
+
}
37
+
}
38
+
39
+
type SelectOpt func(s *select_)
40
+
41
+
func (s select_) From(table string) select_ {
42
+
s.from = table
43
+
return s
44
+
}
45
+
46
+
func (s select_) Where(expr Expr) select_ {
47
+
s.where = &expr
48
+
return s
49
+
}
50
+
51
+
func (s select_) OrderBy(field string, direction Direction) select_ {
52
+
s.orderBy = append(s.orderBy, orderBy{
53
+
field: field,
54
+
direction: direction,
55
+
})
56
+
return s
57
+
}
58
+
59
+
func (s select_) GroupBy(field string) select_ {
60
+
s.groupBy = append(s.groupBy, groupBy{
61
+
field: field,
62
+
})
63
+
return s
64
+
}
65
+
66
+
func (s select_) Limit(i int) select_ {
67
+
s.limit = &limit{
68
+
limit: i,
69
+
}
70
+
return s
71
+
}
72
+
73
+
func (s select_) Compile() (string, []any, error) {
74
+
var sql strings.Builder
75
+
var args []any
76
+
77
+
sql.WriteString("SELECT ")
78
+
if len(s.resultColumns) == 0 {
79
+
return "", nil, fmt.Errorf("result columns empty")
80
+
} else {
81
+
for i, col := range s.resultColumns {
82
+
if i > 0 {
83
+
sql.WriteString(", ")
84
+
}
85
+
sql.WriteString(col)
86
+
}
87
+
}
88
+
89
+
if s.from == "" {
90
+
return "", nil, fmt.Errorf("FROM clause is required")
91
+
}
92
+
sql.WriteString(" FROM ")
93
+
sql.WriteString(s.from)
94
+
95
+
if s.where != nil {
96
+
sql.WriteString(" WHERE ")
97
+
sql.WriteString(s.where.String())
98
+
99
+
args = s.where.Binds()
100
+
}
101
+
102
+
// GROUP BY clause
103
+
if len(s.groupBy) > 0 {
104
+
sql.WriteString(" GROUP BY ")
105
+
for i, gb := range s.groupBy {
106
+
if i > 0 {
107
+
sql.WriteString(", ")
108
+
}
109
+
sql.WriteString(gb.field)
110
+
}
111
+
}
112
+
113
+
// ORDER BY clause
114
+
if len(s.orderBy) > 0 {
115
+
sql.WriteString(" ORDER BY ")
116
+
for i, ob := range s.orderBy {
117
+
if i > 0 {
118
+
sql.WriteString(", ")
119
+
}
120
+
sql.WriteString(ob.field)
121
+
sql.WriteString(" ")
122
+
sql.WriteString(string(ob.direction))
123
+
}
124
+
}
125
+
126
+
// LIMIT clause
127
+
if s.limit != nil {
128
+
if s.limit.limit <= 0 {
129
+
return "", nil, fmt.Errorf("LIMIT must be positive, got %d", s.limit.limit)
130
+
}
131
+
sql.WriteString(" LIMIT ")
132
+
sql.WriteString(strconv.Itoa(s.limit.limit))
133
+
}
134
+
135
+
return sql.String(), args, nil
136
+
}
137
+
138
+
func (s select_) Build(p Database) (*sql.Stmt, []any, error) {
139
+
return Build(s, p)
140
+
}
141
+
142
+
func (s select_) MustBuild(p Database) (*sql.Stmt, []any) {
143
+
return MustBuild(s, p)
144
+
}
145
+
146
+
func (s select_) Exec(p Database) (sql.Result, error) {
147
+
return Exec(s, p)
148
+
}
149
+
150
+
func (s select_) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
151
+
return ExecContext(ctx, s, p)
152
+
}
153
+
154
+
func (s select_) Query(p Database) (*sql.Rows, error) {
155
+
return Query(s, p)
156
+
}
157
+
158
+
func (s select_) QueryContext(ctx context.Context, p Database) (*sql.Rows, error) {
159
+
return QueryContext(ctx, s, p)
160
+
}
161
+
162
+
func (s select_) QueryRow(p Database) (*sql.Row, error) {
163
+
return QueryRow(s, p)
164
+
}
165
+
166
+
func (s select_) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) {
167
+
return QueryRowContext(ctx, s, p)
168
+
}
+112
types.go
+112
types.go
···
···
1
+
package norm
2
+
3
+
import (
4
+
"context"
5
+
"database/sql"
6
+
"fmt"
7
+
)
8
+
9
+
type Database interface {
10
+
Prepare(sql string) (*sql.Stmt, error)
11
+
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
12
+
13
+
Exec(query string, args ...any) (sql.Result, error)
14
+
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
15
+
}
16
+
17
+
type Compiler interface {
18
+
Compile() (string, []any, error)
19
+
}
20
+
21
+
type Builder interface {
22
+
Build(db Database) (*sql.Stmt, []any, error)
23
+
MustBuild(db Database) (*sql.Stmt, []any)
24
+
}
25
+
26
+
// anything that is a Compiler, can also be Builder:
27
+
func Build(c Compiler, p Database) (*sql.Stmt, []any, error) {
28
+
query, args, err := c.Compile()
29
+
if err != nil {
30
+
return nil, nil, err
31
+
}
32
+
33
+
stmt, err := p.Prepare(query)
34
+
if err != nil {
35
+
return nil, nil, fmt.Errorf("failed to prepare statement: `%s` : %w", query, err)
36
+
}
37
+
38
+
return stmt, args, nil
39
+
}
40
+
41
+
func MustBuild(c Compiler, p Database) (*sql.Stmt, []any) {
42
+
stmt, args, err := Build(c, p)
43
+
if err != nil {
44
+
panic(err)
45
+
}
46
+
return stmt, args
47
+
}
48
+
49
+
type Execer interface {
50
+
Exec(db Database) (sql.Result, error)
51
+
ExecContext(ctx context.Context, db Database) (sql.Result, error)
52
+
}
53
+
54
+
// anything that is a Builder can also be an Execer
55
+
func Exec(b Builder, p Database) (sql.Result, error) {
56
+
return ExecContext(context.Background(), b, p)
57
+
}
58
+
59
+
func ExecContext(ctx context.Context, b Builder, p Database) (sql.Result, error) {
60
+
stmt, args, err := b.Build(p)
61
+
if err != nil {
62
+
return nil, err
63
+
}
64
+
65
+
return stmt.ExecContext(ctx, args...)
66
+
}
67
+
68
+
type Querier interface {
69
+
Query(db Database) (*sql.Rows, error)
70
+
QueryContext(ctx context.Context, db Database) (*sql.Rows, error)
71
+
QueryRow(db Database) (*sql.Row, error)
72
+
QueryRowContext(ctx context.Context, db Database) (*sql.Row, error)
73
+
}
74
+
75
+
// anything that is a Builder can also be an Querier
76
+
func Query(b Builder, p Database) (*sql.Rows, error) {
77
+
return QueryContext(context.Background(), b, p)
78
+
}
79
+
80
+
func QueryContext(ctx context.Context, b Builder, p Database) (*sql.Rows, error) {
81
+
stmt, args, err := b.Build(p)
82
+
if err != nil {
83
+
return nil, err
84
+
}
85
+
86
+
return stmt.QueryContext(ctx, args...)
87
+
}
88
+
89
+
func QueryRow(b Builder, p Database) (*sql.Row, error) {
90
+
return QueryRowContext(context.Background(), b, p)
91
+
}
92
+
93
+
func QueryRowContext(ctx context.Context, b Builder, p Database) (*sql.Row, error) {
94
+
stmt, args, err := b.Build(p)
95
+
if err != nil {
96
+
return nil, err
97
+
}
98
+
99
+
return stmt.QueryRowContext(ctx, args...), nil
100
+
}
101
+
102
+
type Direction string
103
+
104
+
const (
105
+
Ascending Direction = "asc"
106
+
Descending Direction = "desc"
107
+
)
108
+
109
+
func Placeholder[T any]() T {
110
+
var zero T
111
+
return zero
112
+
}