1package norm
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7)
8
9type 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
17type Compiler interface {
18 Compile() (string, []any, error)
19 MustCompile() (string, []any)
20}
21
22type Builder interface {
23 Build(db Database) (*sql.Stmt, []any, error)
24 MustBuild(db Database) (*sql.Stmt, []any)
25}
26
27// anything that is a Compiler, can also be Builder:
28func Build(c Compiler, p Database) (*sql.Stmt, []any, error) {
29 query, args, err := c.Compile()
30 if err != nil {
31 return nil, nil, err
32 }
33
34 stmt, err := p.Prepare(query)
35 if err != nil {
36 return nil, nil, fmt.Errorf("failed to prepare statement: `%s` : %w", query, err)
37 }
38
39 return stmt, args, nil
40}
41
42func MustBuild(c Compiler, p Database) (*sql.Stmt, []any) {
43 stmt, args, err := Build(c, p)
44 if err != nil {
45 panic(err)
46 }
47 return stmt, args
48}
49
50type Execer interface {
51 Exec(db Database) (sql.Result, error)
52 ExecContext(ctx context.Context, db Database) (sql.Result, error)
53
54 MustExec(db Database) sql.Result
55 MustExecContext(ctx context.Context, db Database) sql.Result
56}
57
58// anything that is a Builder can also be an Execer
59func Exec(b Builder, p Database) (sql.Result, error) {
60 return ExecContext(context.Background(), b, p)
61}
62
63func ExecContext(ctx context.Context, b Builder, p Database) (sql.Result, error) {
64 stmt, args, err := b.Build(p)
65 if err != nil {
66 return nil, err
67 }
68
69 return stmt.ExecContext(ctx, args...)
70}
71
72func MustExec(b Builder, p Database) sql.Result {
73 return unwrap(Exec(b, p))
74}
75
76func MustExecContext(ctx context.Context, b Builder, p Database) sql.Result {
77 return unwrap(ExecContext(ctx, b, p))
78}
79
80type Querier interface {
81 Query(db Database) (*sql.Rows, error)
82 QueryContext(ctx context.Context, db Database) (*sql.Rows, error)
83 QueryRow(db Database) (*sql.Row, error)
84 QueryRowContext(ctx context.Context, db Database) (*sql.Row, error)
85
86 MustQuery(db Database) *sql.Rows
87 MustQueryContext(ctx context.Context, db Database) *sql.Rows
88 MustQueryRow(db Database) *sql.Row
89 MustQueryRowContext(ctx context.Context, db Database) *sql.Row
90}
91
92// anything that is a Builder can also be an Querier
93func Query(b Builder, db Database) (*sql.Rows, error) {
94 return QueryContext(context.Background(), b, db)
95}
96
97func QueryContext(ctx context.Context, b Builder, db Database) (*sql.Rows, error) {
98 stmt, args, err := b.Build(db)
99 if err != nil {
100 return nil, err
101 }
102
103 return stmt.QueryContext(ctx, args...)
104}
105
106func QueryRow(b Builder, db Database) (*sql.Row, error) {
107 return QueryRowContext(context.Background(), b, db)
108}
109
110func QueryRowContext(ctx context.Context, b Builder, db Database) (*sql.Row, error) {
111 stmt, args, err := b.Build(db)
112 if err != nil {
113 return nil, err
114 }
115
116 return stmt.QueryRowContext(ctx, args...), nil
117}
118
119func MustQuery(b Builder, db Database) *sql.Rows {
120 return unwrap(Query(b, db))
121}
122
123func MustQueryContext(ctx context.Context, b Builder, db Database) *sql.Rows {
124 return unwrap(QueryContext(ctx, b, db))
125}
126
127func MustQueryRow(b Builder, db Database) *sql.Row {
128 return unwrap(QueryRow(b, db))
129}
130
131func MustQueryRowContext(ctx context.Context, b Builder, db Database) *sql.Row {
132 return unwrap(QueryRowContext(ctx, b, db))
133}
134
135type Direction string
136
137const (
138 Ascending Direction = "asc"
139 Descending Direction = "desc"
140)
141
142func Placeholder[T any]() T {
143 var zero T
144 return zero
145}
146
147func unwrap[P any](p P, e error) P {
148 if e != nil {
149 panic(e)
150 }
151 return p
152}