an ORM-free SQL experience
at main 3.6 kB view raw
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}