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