tangled
alpha
login
or
join now
oppi.li
/
norm
9
fork
atom
an ORM-free SQL experience
9
fork
atom
overview
issues
pulls
pipelines
improve interfaces
Signed-off-by: oppiliappan <me@oppi.li>
oppi.li
7 months ago
805ffaaf
0b277d57
verified
This commit was signed with the committer's
known signature
.
oppi.li
SSH Key Fingerprint:
SHA256:yQs05DbrlPDC2pBXLxqOdLYEswq3oEBnHaJiBP7bOlM=
+374
-418
10 changed files
expand all
collapse all
unified
split
.gitignore
delete.go
delete_test.go
expr.go
expr_test.go
insert.go
insert_test.go
select.go
select_test.go
types.go
+2
.gitignore
···
0
0
···
1
+
*.out
2
+
*.html
+13
-24
delete.go
···
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")
···
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
-
}
0
0
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
0
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
}
···
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")
···
50
return sql.String(), args, nil
51
}
52
53
+
func (s delete_) MustCompile() (string, []any) {
54
+
sql, args, err := s.Compile()
55
+
if err != nil {
56
+
panic(err)
57
+
}
58
59
+
return sql, args
0
60
}
61
62
+
func (s delete_) Build(p Database) (*sql.Stmt, []any, error) { return Build(s, p) }
63
+
func (s delete_) MustBuild(p Database) (*sql.Stmt, []any) { return MustBuild(s, p) }
0
64
65
+
func (s delete_) Exec(p Database) (sql.Result, error) { return Exec(s, p) }
66
func (s delete_) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
67
return ExecContext(ctx, s, p)
68
}
69
+
func (s delete_) MustExec(p Database) sql.Result { return MustExec(s, p) }
70
+
func (s delete_) MustExecContext(ctx context.Context, p Database) sql.Result {
71
+
return MustExecContext(ctx, s, p)
0
0
0
0
0
0
0
0
0
0
0
0
72
}
+95
-1
delete_test.go
···
5
"testing"
6
)
7
8
-
func TestDeleteIntegration_BasicQueries(t *testing.T) {
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
9
tests := []struct {
10
name string
11
stmt Execer
···
5
"testing"
6
)
7
8
+
func TestDeleteCompileSuccess(t *testing.T) {
9
+
tests := []struct {
10
+
name string
11
+
stmt Compiler
12
+
expectedSql string
13
+
expectedArgs []any
14
+
}{
15
+
{
16
+
name: "Delete all users",
17
+
stmt: Delete().From("users"),
18
+
expectedSql: "DELETE FROM users",
19
+
},
20
+
{
21
+
name: "Delete active users only",
22
+
stmt: Delete().
23
+
From("users").
24
+
Where(Eq("active", true)),
25
+
expectedSql: "DELETE FROM users WHERE (active) = (?)",
26
+
expectedArgs: []any{true},
27
+
},
28
+
{
29
+
name: "Delete users in Engineering",
30
+
stmt: Delete().
31
+
From("users").
32
+
Where(Eq("department", "Engineering")),
33
+
expectedSql: "DELETE FROM users WHERE (department) = (?)",
34
+
expectedArgs: []any{"Engineering"},
35
+
},
36
+
{
37
+
name: "Delete users with age > 30",
38
+
stmt: Delete().
39
+
From("users").
40
+
Where(Gt("age", 30)),
41
+
expectedSql: "DELETE FROM users WHERE (age) > (?)",
42
+
expectedArgs: []any{30},
43
+
},
44
+
}
45
+
46
+
for _, test := range tests {
47
+
t.Run(test.name, func(t *testing.T) {
48
+
sql, args := test.stmt.MustCompile()
49
+
50
+
if sql != test.expectedSql {
51
+
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
52
+
}
53
+
54
+
if len(args) != len(test.expectedArgs) {
55
+
t.Errorf("Expected '%d' args, got '%d' args", len(test.expectedArgs), len(args))
56
+
}
57
+
58
+
for i := range len(args) {
59
+
if args[i] != test.expectedArgs[i] {
60
+
t.Errorf("Expected '%s', got '%s' at index %d", test.expectedArgs[i], args[i], i)
61
+
}
62
+
}
63
+
})
64
+
}
65
+
}
66
+
67
+
func TestDeleteCompileFail(t *testing.T) {
68
+
tests := []struct {
69
+
name string
70
+
stmt Compiler
71
+
expectedError string
72
+
}{
73
+
{
74
+
name: "No from clause",
75
+
stmt: Delete(),
76
+
expectedError: "FROM clause is required",
77
+
},
78
+
}
79
+
80
+
for _, test := range tests {
81
+
t.Run(test.name, func(t *testing.T) {
82
+
sql, args, err := test.stmt.Compile()
83
+
if err == nil {
84
+
t.Error("Expected error, got nil")
85
+
}
86
+
87
+
if err.Error() != test.expectedError {
88
+
t.Errorf("Expected error '%s', got '%s'", test.expectedError, err.Error())
89
+
}
90
+
91
+
if sql != "" {
92
+
t.Errorf("Expected empty SQL on error, got '%s'", sql)
93
+
}
94
+
95
+
if args != nil {
96
+
t.Errorf("Expected empty args on error, got '%q'", args)
97
+
}
98
+
})
99
+
}
100
+
}
101
+
102
+
func TestDeleteIntegration(t *testing.T) {
103
tests := []struct {
104
name string
105
stmt Execer
+1
expr.go
···
36
return e.value.Binds()
37
}
38
0
39
return nil
40
}
41
···
36
return e.value.Binds()
37
}
38
39
+
// unreachable
40
return nil
41
}
42
+17
expr_test.go
···
100
t.Errorf("Expected '%s', got '%s'", expected, complex.String())
101
}
102
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
100
t.Errorf("Expected '%s', got '%s'", expected, complex.String())
101
}
102
}
103
+
104
+
// this just needs to compile
105
+
func TestImplsInterfaces(t *testing.T) {
106
+
sel := Select()
107
+
_ = isCompiler(sel) & isBuilder(sel) & isExecer(sel) & isQuerier(sel)
108
+
109
+
del := Delete()
110
+
_ = isCompiler(del) & isBuilder(del) & isExecer(del)
111
+
112
+
ins := Insert()
113
+
_ = isCompiler(ins) & isBuilder(ins) & isExecer(ins)
114
+
}
115
+
116
+
func isCompiler[S Compiler](S) int { return 0 }
117
+
func isBuilder[S Builder](S) int { return 0 }
118
+
func isExecer[S Execer](S) int { return 0 }
119
+
func isQuerier[S Querier](S) int { return 0 }
+14
-24
insert.go
···
76
77
sql.WriteString("INSERT ")
78
79
-
if s.or != None {
0
80
sql.WriteString("OR ")
81
sql.WriteString(s.or.String())
82
sql.WriteString(" ")
···
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
-
}
0
0
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
0
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
}
···
76
77
sql.WriteString("INSERT ")
78
79
+
orKw := s.or.String()
80
+
if orKw != "" {
81
sql.WriteString("OR ")
82
sql.WriteString(s.or.String())
83
sql.WriteString(" ")
···
111
return sql.String(), args, nil
112
}
113
114
+
func (s insert) MustCompile() (string, []any) {
115
+
sql, args, err := s.Compile()
116
+
if err != nil {
117
+
panic(err)
118
+
}
119
120
+
return sql, args
0
121
}
122
123
+
func (s insert) Build(p Database) (*sql.Stmt, []any, error) { return Build(s, p) }
124
+
func (s insert) MustBuild(p Database) (*sql.Stmt, []any) { return MustBuild(s, p) }
0
125
126
+
func (s insert) Exec(p Database) (sql.Result, error) { return Exec(s, p) }
127
func (s insert) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
128
return ExecContext(ctx, s, p)
129
}
130
+
func (s insert) MustExec(p Database) sql.Result { return MustExec(s, p) }
131
+
func (s insert) MustExecContext(ctx context.Context, p Database) sql.Result {
132
+
return MustExecContext(ctx, s, p)
0
0
0
0
0
0
0
0
0
0
0
0
133
}
+134
-5
insert_test.go
···
19
expectedArgs: []any{"John"},
20
},
21
{
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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
{
0
0
0
0
0
0
0
0
0
0
0
0
28
name: "More values",
29
stmt: Insert().Into("users").Value("name", "John").Value("age", 35),
30
expectedSql: "INSERT INTO users (name, age) VALUES (?, ?)",
···
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)
···
56
})
57
}
58
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
19
expectedArgs: []any{"John"},
20
},
21
{
22
+
name: "Abort clause",
23
+
stmt: Insert().Or(Abort).Into("users").Value("name", "John"),
24
+
expectedSql: "INSERT OR ABORT INTO users (name) VALUES (?)",
25
+
expectedArgs: []any{"John"},
26
+
},
27
+
{
28
+
name: "Ignore clause",
29
+
stmt: Insert().Or(Ignore).Into("users").Value("name", "John"),
30
+
expectedSql: "INSERT OR IGNORE INTO users (name) VALUES (?)",
31
+
expectedArgs: []any{"John"},
32
+
},
33
+
{
34
+
name: "Fail clause",
35
+
stmt: Insert().Or(Fail).Into("users").Value("name", "John"),
36
+
expectedSql: "INSERT OR FAIL INTO users (name) VALUES (?)",
37
+
expectedArgs: []any{"John"},
38
+
},
39
+
{
40
name: "Replace clause",
41
stmt: Insert().Or(Replace).Into("users").Value("name", "John"),
42
expectedSql: "INSERT OR REPLACE INTO users (name) VALUES (?)",
43
expectedArgs: []any{"John"},
44
},
45
{
46
+
name: "Rollback clause",
47
+
stmt: Insert().Or(Rollback).Into("users").Value("name", "John"),
48
+
expectedSql: "INSERT OR ROLLBACK INTO users (name) VALUES (?)",
49
+
expectedArgs: []any{"John"},
50
+
},
51
+
{
52
+
name: "Default clause",
53
+
stmt: Insert().Or(InsertOr(10)).Into("users").Value("name", "John"),
54
+
expectedSql: "INSERT INTO users (name) VALUES (?)",
55
+
expectedArgs: []any{"John"},
56
+
},
57
+
{
58
name: "More values",
59
stmt: Insert().Into("users").Value("name", "John").Value("age", 35),
60
expectedSql: "INSERT INTO users (name, age) VALUES (?, ?)",
···
64
65
for _, test := range tests {
66
t.Run(test.name, func(t *testing.T) {
67
+
sql, args := test.stmt.MustCompile()
0
0
0
0
68
69
if sql != test.expectedSql {
70
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
···
82
})
83
}
84
}
85
+
86
+
func TestInsertCompileFail(t *testing.T) {
87
+
tests := []struct {
88
+
name string
89
+
stmt Compiler
90
+
expectedError string
91
+
}{
92
+
{
93
+
name: "No into clause",
94
+
stmt: Insert(),
95
+
expectedError: "INTO clause is required",
96
+
},
97
+
{
98
+
name: "No into clause",
99
+
stmt: Insert().Into("users"),
100
+
expectedError: "no values supplied",
101
+
},
102
+
}
103
+
104
+
for _, test := range tests {
105
+
t.Run(test.name, func(t *testing.T) {
106
+
sql, args, err := test.stmt.Compile()
107
+
if err == nil {
108
+
t.Error("Expected error, got nil")
109
+
}
110
+
111
+
if err.Error() != test.expectedError {
112
+
t.Errorf("Expected error '%s', got '%s'", test.expectedError, err.Error())
113
+
}
114
+
115
+
if sql != "" {
116
+
t.Errorf("Expected empty SQL on error, got '%s'", sql)
117
+
}
118
+
119
+
if args != nil {
120
+
t.Errorf("Expected empty args on error, got '%q'", args)
121
+
}
122
+
})
123
+
}
124
+
}
125
+
126
+
func TestInsertIntegration(t *testing.T) {
127
+
tests := []struct {
128
+
name string
129
+
stmt Execer
130
+
expectedRows int64
131
+
}{
132
+
{
133
+
name: "Delete all users",
134
+
stmt: Delete().From("users"),
135
+
expectedRows: 6,
136
+
},
137
+
{
138
+
name: "Delete active users only",
139
+
stmt: Delete().
140
+
From("users").
141
+
Where(Eq("active", true)),
142
+
expectedRows: 4,
143
+
},
144
+
{
145
+
name: "Select users in Engineering",
146
+
stmt: Delete().
147
+
From("users").
148
+
Where(Eq("department", "Engineering")),
149
+
expectedRows: 3,
150
+
},
151
+
{
152
+
name: "Delete users with age > 30",
153
+
stmt: Delete().
154
+
From("users").
155
+
Where(Gt("age", 30)),
156
+
expectedRows: 2,
157
+
},
158
+
{
159
+
name: "Delete users with salary between 70000 and 80000",
160
+
stmt: Delete().
161
+
From("users").
162
+
Where(Gte("salary", 70000.0).And(Lte("salary", 80000.0))),
163
+
expectedRows: 3,
164
+
},
165
+
}
166
+
167
+
for _, test := range tests {
168
+
t.Run(test.name, func(t *testing.T) {
169
+
db := setupTestDB(t)
170
+
defer db.Close()
171
+
172
+
res, err := test.stmt.Exec(db)
173
+
if err != nil {
174
+
t.Fatalf("Failed to execute query: %v", err)
175
+
}
176
+
177
+
count, err := res.RowsAffected()
178
+
if err != nil {
179
+
t.Fatalf("Failed to execute query: %v", err)
180
+
}
181
+
182
+
if count != test.expectedRows {
183
+
t.Errorf("Expected %d rows, got %d", test.expectedRows, count)
184
+
}
185
+
})
186
+
}
187
+
}
+23
-16
select.go
···
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
-
}
0
0
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
0
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
0
158
func (s select_) QueryContext(ctx context.Context, p Database) (*sql.Rows, error) {
159
return QueryContext(ctx, s, p)
160
}
0
0
0
0
161
162
-
func (s select_) QueryRow(p Database) (*sql.Row, error) {
163
-
return QueryRow(s, p)
0
164
}
165
-
166
-
func (s select_) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) {
167
-
return QueryRowContext(ctx, s, p)
168
}
···
135
return sql.String(), args, nil
136
}
137
138
+
func (s select_) MustCompile() (string, []any) {
139
+
sql, args, err := s.Compile()
140
+
if err != nil {
141
+
panic(err)
142
+
}
143
144
+
return sql, args
0
145
}
146
147
+
func (s select_) Build(p Database) (*sql.Stmt, []any, error) { return Build(s, p) }
148
+
func (s select_) MustBuild(p Database) (*sql.Stmt, []any) { return MustBuild(s, p) }
0
149
150
+
func (s select_) Exec(p Database) (sql.Result, error) { return Exec(s, p) }
151
func (s select_) ExecContext(ctx context.Context, p Database) (sql.Result, error) {
152
return ExecContext(ctx, s, p)
153
}
154
+
func (s select_) MustExec(p Database) sql.Result { return MustExec(s, p) }
155
+
func (s select_) MustExecContext(ctx context.Context, p Database) sql.Result {
156
+
return MustExecContext(ctx, s, p)
157
}
158
159
+
func (s select_) Query(p Database) (*sql.Rows, error) { return Query(s, p) }
160
func (s select_) QueryContext(ctx context.Context, p Database) (*sql.Rows, error) {
161
return QueryContext(ctx, s, p)
162
}
163
+
func (s select_) QueryRow(p Database) (*sql.Row, error) { return QueryRow(s, p) }
164
+
func (s select_) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) {
165
+
return QueryRowContext(ctx, s, p)
166
+
}
167
168
+
func (s select_) MustQuery(p Database) *sql.Rows { return MustQuery(s, p) }
169
+
func (s select_) MustQueryContext(ctx context.Context, p Database) *sql.Rows {
170
+
return MustQueryContext(ctx, s, p)
171
}
172
+
func (s select_) MustQueryRow(p Database) *sql.Row { return MustQueryRow(s, p) }
173
+
func (s select_) MustQueryRowContext(ctx context.Context, p Database) *sql.Row {
174
+
return MustQueryRowContext(ctx, s, p)
175
}
+4
-340
select_test.go
···
5
"testing"
6
)
7
8
-
func TestSelectBasic(t *testing.T) {
9
-
s := Select("name", "age")
10
-
11
-
if len(s.resultColumns) != 2 {
12
-
t.Errorf("Expected 2 columns, got %d", len(s.resultColumns))
13
-
}
14
-
15
-
if s.resultColumns[0] != "name" || s.resultColumns[1] != "age" {
16
-
t.Errorf("Expected columns [name, age], got %v", s.resultColumns)
17
-
}
18
-
}
19
-
20
-
func TestSelectAPI(t *testing.T) {
21
-
s := Select("*").
22
-
From("users").
23
-
Where(Eq("id", 1)).
24
-
OrderBy("name", Ascending).
25
-
GroupBy("department").
26
-
Limit(10)
27
-
28
-
if s.from != "users" {
29
-
t.Errorf("Expected from to be 'users', got '%s'", s.from)
30
-
}
31
-
32
-
if s.where == nil {
33
-
t.Error("Expected where clause to be set")
34
-
}
35
-
36
-
if len(s.orderBy) != 1 {
37
-
t.Errorf("Expected 1 order by clause, got %d", len(s.orderBy))
38
-
}
39
-
40
-
if s.orderBy[0].field != "name" || s.orderBy[0].direction != Ascending {
41
-
t.Errorf("Expected order by name ASC, got %v", s.orderBy[0])
42
-
}
43
-
44
-
if len(s.groupBy) != 1 {
45
-
t.Errorf("Expected 1 group by clause, got %d", len(s.groupBy))
46
-
}
47
-
48
-
if s.groupBy[0].field != "department" {
49
-
t.Errorf("Expected group by department, got %s", s.groupBy[0].field)
50
-
}
51
-
52
-
if s.limit == nil {
53
-
t.Error("Expected limit to be set")
54
-
}
55
-
56
-
if s.limit.limit != 10 {
57
-
t.Errorf("Expected limit 10, got %d", s.limit.limit)
58
-
}
59
-
}
60
-
61
-
func TestSelectBuild_Success(t *testing.T) {
62
tests := []struct {
63
name string
64
stmt Compiler
···
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)
134
-
}
135
136
if sql != test.expectedSql {
137
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
···
150
}
151
}
152
153
-
func TestSelectBuild_Errors(t *testing.T) {
154
tests := []struct {
155
name string
156
stmt Compiler
···
205
}
206
}
207
208
-
func TestMultipleOrderBy(t *testing.T) {
209
-
s := Select("name", "age").
210
-
From("users").
211
-
OrderBy("name", Ascending).
212
-
OrderBy("age", Descending).
213
-
OrderBy("created_at", Descending)
214
-
215
-
if len(s.orderBy) != 3 {
216
-
t.Errorf("Expected 3 order by clauses, got %d", len(s.orderBy))
217
-
}
218
-
219
-
expected := []orderBy{
220
-
{"name", Ascending},
221
-
{"age", Descending},
222
-
{"created_at", Descending},
223
-
}
224
-
225
-
for i, ob := range s.orderBy {
226
-
if ob.field != expected[i].field || ob.direction != expected[i].direction {
227
-
t.Errorf("Expected order by %v, got %v", expected[i], ob)
228
-
}
229
-
}
230
-
}
231
-
232
-
func TestMultipleGroupBy(t *testing.T) {
233
-
s := Select("department", "status", "COUNT(*)").
234
-
From("users").
235
-
GroupBy("department").
236
-
GroupBy("status")
237
-
238
-
if len(s.groupBy) != 2 {
239
-
t.Errorf("Expected 2 group by clauses, got %d", len(s.groupBy))
240
-
}
241
-
242
-
expected := []string{"department", "status"}
243
-
244
-
for i, gb := range s.groupBy {
245
-
if gb.field != expected[i] {
246
-
t.Errorf("Expected group by %s, got %s", expected[i], gb.field)
247
-
}
248
-
}
249
-
}
250
-
251
-
func TestSelectIntegration_BasicQueries(t *testing.T) {
252
db := setupTestDB(t)
253
defer db.Close()
254
···
310
})
311
}
312
}
313
-
314
-
func TestSelectIntegration_ComplexQueries(t *testing.T) {
315
-
db := setupTestDB(t)
316
-
defer db.Close()
317
-
318
-
t.Run("Complex WHERE with AND/OR", func(t *testing.T) {
319
-
s := Select("name", "department", "age").
320
-
From("users").
321
-
Where(
322
-
Eq("department", "Engineering").
323
-
And(Gt("age", 30)).
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
-
}
331
-
defer rows.Close()
332
-
333
-
var results []struct {
334
-
name, department string
335
-
age int
336
-
}
337
-
338
-
for rows.Next() {
339
-
var name, department string
340
-
var age int
341
-
if err := rows.Scan(&name, &department, &age); err != nil {
342
-
t.Fatalf("Failed to scan row: %v", err)
343
-
}
344
-
results = append(results, struct {
345
-
name, department string
346
-
age int
347
-
}{name, department, age})
348
-
}
349
-
350
-
// Should get:
351
-
// - Bob Johnson (Engineering, 35)
352
-
// - Charlie Wilson (Engineering, 32)
353
-
// - Jane Smith (Marketing, 25)
354
-
// - Diana Prince (Marketing, 29)
355
-
if len(results) != 4 {
356
-
t.Errorf("Expected 4 results, got %d", len(results))
357
-
}
358
-
})
359
-
360
-
t.Run("ORDER BY multiple columns", func(t *testing.T) {
361
-
s := Select("name", "department", "age").
362
-
From("users").
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
-
}
370
-
defer rows.Close()
371
-
372
-
var results []struct {
373
-
name, department string
374
-
age int
375
-
}
376
-
377
-
for rows.Next() {
378
-
var name, department string
379
-
var age int
380
-
if err := rows.Scan(&name, &department, &age); err != nil {
381
-
t.Fatalf("Failed to scan row: %v", err)
382
-
}
383
-
results = append(results, struct {
384
-
name, department string
385
-
age int
386
-
}{name, department, age})
387
-
}
388
-
389
-
if len(results) != 6 {
390
-
t.Fatalf("Expected 6 results, got %d", len(results))
391
-
}
392
-
393
-
if results[0].department != "Engineering" {
394
-
t.Errorf("Expected first result to be from Engineering, got %s", results[0].department)
395
-
}
396
-
if results[0].age != 35 {
397
-
t.Errorf("Expected first result age to be 35, got %d", results[0].age)
398
-
}
399
-
})
400
-
401
-
t.Run("GROUP BY with COUNT", func(t *testing.T) {
402
-
s := Select("department", "COUNT(*) as user_count").
403
-
From("users").
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
-
}
411
-
defer rows.Close()
412
-
413
-
var results []struct {
414
-
department string
415
-
count int
416
-
}
417
-
418
-
for rows.Next() {
419
-
var department string
420
-
var count int
421
-
if err := rows.Scan(&department, &count); err != nil {
422
-
t.Fatalf("Failed to scan row: %v", err)
423
-
}
424
-
results = append(results, struct {
425
-
department string
426
-
count int
427
-
}{department, count})
428
-
}
429
-
430
-
if len(results) != 3 {
431
-
t.Errorf("Expected 3 departments, got %d", len(results))
432
-
}
433
-
434
-
if results[0].department != "Engineering" || results[0].count != 3 {
435
-
t.Errorf("Expected Engineering with 3 users first, got %s with %d users", results[0].department, results[0].count)
436
-
}
437
-
})
438
-
439
-
t.Run("LIMIT with ORDER BY", func(t *testing.T) {
440
-
s := Select("name", "salary").
441
-
From("users").
442
-
Where(Eq("active", true)).
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
-
}
450
-
defer rows.Close()
451
-
452
-
var results []struct {
453
-
name string
454
-
salary float64
455
-
}
456
-
457
-
for rows.Next() {
458
-
var name string
459
-
var salary float64
460
-
if err := rows.Scan(&name, &salary); err != nil {
461
-
t.Fatalf("Failed to scan row: %v", err)
462
-
}
463
-
results = append(results, struct {
464
-
name string
465
-
salary float64
466
-
}{name, salary})
467
-
}
468
-
469
-
if len(results) != 2 {
470
-
t.Errorf("Expected 2 results, got %d", len(results))
471
-
}
472
-
473
-
if results[0].salary != 85000.0 {
474
-
t.Errorf("Expected highest salary to be 85000, got %f", results[0].salary)
475
-
}
476
-
if results[1].salary != 75000.0 {
477
-
t.Errorf("Expected second highest salary to be 75000, got %f", results[1].salary)
478
-
}
479
-
})
480
-
}
481
-
482
-
func TestSelectIntegration_DataTypes(t *testing.T) {
483
-
db := setupTestDB(t)
484
-
defer db.Close()
485
-
486
-
t.Run("String comparisons", func(t *testing.T) {
487
-
s := Select("name", "email").
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
-
}
495
-
defer rows.Close()
496
-
497
-
count := 0
498
-
for rows.Next() {
499
-
count++
500
-
}
501
-
502
-
if count != 1 {
503
-
t.Errorf("Expected 1 result, got %d", count)
504
-
}
505
-
})
506
-
507
-
t.Run("Boolean comparisons", func(t *testing.T) {
508
-
s := Select("name").
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
-
}
516
-
defer rows.Close()
517
-
518
-
count := 0
519
-
for rows.Next() {
520
-
count++
521
-
}
522
-
523
-
if count != 2 {
524
-
t.Errorf("Expected 2 inactive users, got %d", count)
525
-
}
526
-
})
527
-
528
-
t.Run("Float comparisons", func(t *testing.T) {
529
-
s := Select("name", "salary").
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
-
}
537
-
defer rows.Close()
538
-
539
-
count := 0
540
-
for rows.Next() {
541
-
count++
542
-
}
543
-
544
-
if count != 2 {
545
-
t.Errorf("Expected 2 users with salary >= 80000, got %d", count)
546
-
}
547
-
})
548
-
}
···
5
"testing"
6
)
7
8
+
func TestSelectCompileSuccess(t *testing.T) {
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
9
tests := []struct {
10
name string
11
stmt Compiler
···
74
75
for _, test := range tests {
76
t.Run(test.name, func(t *testing.T) {
77
+
sql, args := test.stmt.MustCompile()
0
0
0
0
78
79
if sql != test.expectedSql {
80
t.Errorf("Expected '%s', got '%s'", test.expectedSql, sql)
···
93
}
94
}
95
96
+
func TestSelectCompileFail(t *testing.T) {
97
tests := []struct {
98
name string
99
stmt Compiler
···
148
}
149
}
150
151
+
func TestSelectIntegration(t *testing.T) {
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
152
db := setupTestDB(t)
153
defer db.Close()
154
···
210
})
211
}
212
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
+71
-8
types.go
···
16
17
type Compiler interface {
18
Compile() (string, []any, error)
0
19
}
20
21
type Builder interface {
···
49
type Execer interface {
50
Exec(db Database) (sql.Result, error)
51
ExecContext(ctx context.Context, db Database) (sql.Result, error)
0
0
0
52
}
53
54
// anything that is a Builder can also be an Execer
···
65
return stmt.ExecContext(ctx, args...)
66
}
67
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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)
0
0
0
0
0
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
}
···
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
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
100
}
101
102
type Direction string
···
16
17
type Compiler interface {
18
Compile() (string, []any, error)
19
+
MustCompile() (string, []any)
20
}
21
22
type Builder interface {
···
50
type 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
···
69
return stmt.ExecContext(ctx, args...)
70
}
71
72
+
func MustExec(b Builder, p Database) sql.Result {
73
+
res, err := Exec(b, p)
74
+
if err != nil {
75
+
panic(err)
76
+
}
77
+
78
+
return res
79
+
}
80
+
81
+
func MustExecContext(ctx context.Context, b Builder, p Database) sql.Result {
82
+
res, err := ExecContext(ctx, b, p)
83
+
if err != nil {
84
+
panic(err)
85
+
}
86
+
87
+
return res
88
+
}
89
+
90
type Querier interface {
91
Query(db Database) (*sql.Rows, error)
92
QueryContext(ctx context.Context, db Database) (*sql.Rows, error)
93
QueryRow(db Database) (*sql.Row, error)
94
QueryRowContext(ctx context.Context, db Database) (*sql.Row, error)
95
+
96
+
MustQuery(db Database) *sql.Rows
97
+
MustQueryContext(ctx context.Context, db Database) *sql.Rows
98
+
MustQueryRow(db Database) *sql.Row
99
+
MustQueryRowContext(ctx context.Context, db Database) *sql.Row
100
}
101
102
// anything that is a Builder can also be an Querier
103
+
func Query(b Builder, db Database) (*sql.Rows, error) {
104
+
return QueryContext(context.Background(), b, db)
105
}
106
107
+
func QueryContext(ctx context.Context, b Builder, db Database) (*sql.Rows, error) {
108
+
stmt, args, err := b.Build(db)
109
if err != nil {
110
return nil, err
111
}
···
113
return stmt.QueryContext(ctx, args...)
114
}
115
116
+
func QueryRow(b Builder, db Database) (*sql.Row, error) {
117
+
return QueryRowContext(context.Background(), b, db)
118
}
119
120
+
func QueryRowContext(ctx context.Context, b Builder, db Database) (*sql.Row, error) {
121
+
stmt, args, err := b.Build(db)
122
if err != nil {
123
return nil, err
124
}
125
126
return stmt.QueryRowContext(ctx, args...), nil
127
+
}
128
+
129
+
func MustQuery(b Builder, db Database) *sql.Rows {
130
+
rows, err := Query(b, db)
131
+
if err != nil {
132
+
panic(err)
133
+
}
134
+
135
+
return rows
136
+
}
137
+
138
+
func MustQueryContext(ctx context.Context, b Builder, db Database) *sql.Rows {
139
+
rows, err := QueryContext(ctx, b, db)
140
+
if err != nil {
141
+
panic(err)
142
+
}
143
+
144
+
return rows
145
+
}
146
+
147
+
func MustQueryRow(b Builder, db Database) *sql.Row {
148
+
row, err := QueryRow(b, db)
149
+
if err != nil {
150
+
panic(err)
151
+
}
152
+
153
+
return row
154
+
}
155
+
156
+
func MustQueryRowContext(ctx context.Context, b Builder, db Database) *sql.Row {
157
+
row, err := QueryRowContext(ctx, b, db)
158
+
if err != nil {
159
+
panic(err)
160
+
}
161
+
162
+
return row
163
}
164
165
type Direction string