an ORM-free SQL experience

improve interfaces

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 805ffaaf 0b277d57

verified
+374 -418
+2
.gitignore
··· 1 + *.out 2 + *.html
+13 -24
delete.go
··· 32 32 var sql strings.Builder 33 33 var args []any 34 34 35 - sql.WriteString("DELETE ") 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 - func (s delete_) Build(p Database) (*sql.Stmt, []any, error) { 54 - return Build(s, p) 55 - } 53 + func (s delete_) MustCompile() (string, []any) { 54 + sql, args, err := s.Compile() 55 + if err != nil { 56 + panic(err) 57 + } 56 58 57 - func (s delete_) MustBuild(p Database) (*sql.Stmt, []any) { 58 - return MustBuild(s, p) 59 + return sql, args 59 60 } 60 61 61 - func (s delete_) Exec(p Database) (sql.Result, error) { 62 - return Exec(s, p) 63 - } 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) } 64 64 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 - 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) 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) 83 72 }
+95 -1
delete_test.go
··· 5 5 "testing" 6 6 ) 7 7 8 - func TestDeleteIntegration_BasicQueries(t *testing.T) { 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) { 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 + // 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 + 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 76 77 77 sql.WriteString("INSERT ") 78 78 79 - if s.or != None { 79 + orKw := s.or.String() 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 - func (s insert) Build(p Database) (*sql.Stmt, []any, error) { 114 - return Build(s, p) 115 - } 114 + func (s insert) MustCompile() (string, []any) { 115 + sql, args, err := s.Compile() 116 + if err != nil { 117 + panic(err) 118 + } 116 119 117 - func (s insert) MustBuild(p Database) (*sql.Stmt, []any) { 118 - return MustBuild(s, p) 120 + return sql, args 119 121 } 120 122 121 - func (s insert) Exec(p Database) (sql.Result, error) { 122 - return Exec(s, p) 123 - } 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) } 124 125 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 - 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) 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) 143 133 }
+134 -5
insert_test.go
··· 19 19 expectedArgs: []any{"John"}, 20 20 }, 21 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 + { 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 + 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 + { 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 - sql, args, err := test.stmt.Compile() 38 - 39 - if err != nil { 40 - t.Errorf("Expected no error, got %v", err) 41 - } 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 + 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 135 return sql.String(), args, nil 136 136 } 137 137 138 - func (s select_) Build(p Database) (*sql.Stmt, []any, error) { 139 - return Build(s, p) 140 - } 138 + func (s select_) MustCompile() (string, []any) { 139 + sql, args, err := s.Compile() 140 + if err != nil { 141 + panic(err) 142 + } 141 143 142 - func (s select_) MustBuild(p Database) (*sql.Stmt, []any) { 143 - return MustBuild(s, p) 144 + return sql, args 144 145 } 145 146 146 - func (s select_) Exec(p Database) (sql.Result, error) { 147 - return Exec(s, p) 148 - } 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) } 149 149 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 - 154 - func (s select_) Query(p Database) (*sql.Rows, error) { 155 - return Query(s, p) 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) 156 157 } 157 158 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 + 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 + } 161 167 162 - func (s select_) QueryRow(p Database) (*sql.Row, error) { 163 - return QueryRow(s, p) 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) 164 171 } 165 - 166 - func (s select_) QueryRowContext(ctx context.Context, p Database) (*sql.Row, error) { 167 - return QueryRowContext(ctx, s, p) 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) 168 175 }
+4 -340
select_test.go
··· 5 5 "testing" 6 6 ) 7 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) { 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 - sql, args, err := test.stmt.Compile() 131 - 132 - if err != nil { 133 - t.Errorf("Expected no error, got %v", err) 134 - } 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 - func TestSelectBuild_Errors(t *testing.T) { 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 - 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) { 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 - 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 - }
+71 -8
types.go
··· 16 16 17 17 type Compiler interface { 18 18 Compile() (string, []any, error) 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 + 54 + MustExec(db Database) sql.Result 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 + 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 + 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 + 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 73 100 } 74 101 75 102 // 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) 103 + func Query(b Builder, db Database) (*sql.Rows, error) { 104 + return QueryContext(context.Background(), b, db) 78 105 } 79 106 80 - func QueryContext(ctx context.Context, b Builder, p Database) (*sql.Rows, error) { 81 - stmt, args, err := b.Build(p) 107 + func QueryContext(ctx context.Context, b Builder, db Database) (*sql.Rows, error) { 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 - func QueryRow(b Builder, p Database) (*sql.Row, error) { 90 - return QueryRowContext(context.Background(), b, p) 116 + func QueryRow(b Builder, db Database) (*sql.Row, error) { 117 + return QueryRowContext(context.Background(), b, db) 91 118 } 92 119 93 - func QueryRowContext(ctx context.Context, b Builder, p Database) (*sql.Row, error) { 94 - stmt, args, err := b.Build(p) 120 + func QueryRowContext(ctx context.Context, b Builder, db Database) (*sql.Row, error) { 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 + } 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 100 163 } 101 164 102 165 type Direction string