Skip to content

Commit cb0fa84

Browse files
committed
ci: add database services for MSSQL testing
Add PostgreSQL, MySQL, and MSSQL database services to CI workflow. This enables proper testing of all database engines including the new MSSQL support. Revert the graceful skipping code since CI now provides the required databases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent e9fbc2e commit cb0fa84

File tree

9 files changed

+55
-93
lines changed

9 files changed

+55
-93
lines changed

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,39 @@ jobs:
2424
GOARCH: ${{ matrix.goarch }}
2525
test:
2626
runs-on: ubuntu-24.04
27+
services:
28+
postgres:
29+
image: postgres:16
30+
env:
31+
POSTGRES_USER: postgres
32+
POSTGRES_PASSWORD: mysecretpassword
33+
POSTGRES_DB: postgres
34+
ports:
35+
- 5432:5432
36+
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
37+
mysql:
38+
image: mysql:9
39+
env:
40+
MYSQL_DATABASE: dinotest
41+
MYSQL_ROOT_PASSWORD: mysecretpassword
42+
MYSQL_ROOT_HOST: '%'
43+
ports:
44+
- 3306:3306
45+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
46+
mssql:
47+
image: mcr.microsoft.com/mssql/server:2022-latest
48+
env:
49+
ACCEPT_EULA: Y
50+
MSSQL_SA_PASSWORD: MySecretPassword1!
51+
MSSQL_PID: Developer
52+
ports:
53+
- 1433:1433
54+
options: >-
55+
--health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'MySecretPassword1!' -Q 'SELECT 1' -C || exit 1"
56+
--health-interval 10s
57+
--health-timeout 5s
58+
--health-retries 10
59+
--health-start-period 30s
2760
steps:
2861
- uses: actions/checkout@v6
2962
- uses: actions/setup-go@v6
@@ -58,6 +91,9 @@ jobs:
5891
CI_SQLC_AUTH_TOKEN: ${{ secrets.CI_SQLC_AUTH_TOKEN }}
5992
SQLC_AUTH_TOKEN: ${{ secrets.CI_SQLC_AUTH_TOKEN }}
6093
CGO_ENABLED: "0"
94+
POSTGRESQL_SERVER_URI: "postgres://postgres:mysecretpassword@localhost:5432/postgres?sslmode=disable"
95+
MYSQL_SERVER_URI: "root:mysecretpassword@tcp(localhost:3306)/dinotest?parseTime=true"
96+
MSSQL_SERVER_URI: "sqlserver://sa:MySecretPassword1!@localhost:1433?database=master"
6197

6298
vuln_check:
6399
runs-on: ubuntu-24.04

internal/cmd/generate.go

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -293,16 +293,7 @@ func remoteGenerate(ctx context.Context, configPath string, conf *config.Config,
293293
return output, nil
294294
}
295295

296-
// parseResult indicates the outcome of parsing a SQL configuration
297-
type parseResult int
298-
299-
const (
300-
parseSuccess parseResult = iota
301-
parseFailed
302-
parseSkipped // Configuration was skipped (e.g., database unavailable)
303-
)
304-
305-
func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, parseResult) {
296+
func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) {
306297
defer trace.StartRegion(ctx, "parse").End()
307298
c, err := compiler.NewCompiler(sql, combo, parserOpts)
308299
defer func() {
@@ -311,15 +302,8 @@ func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.C
311302
}
312303
}()
313304
if err != nil {
314-
// Check if this is a database unavailability error (e.g., missing env var)
315-
// In this case, skip the configuration gracefully rather than failing
316-
if compiler.IsDatabaseUnavailable(err) {
317-
fmt.Fprintf(stderr, "# package %s\n", name)
318-
fmt.Fprintf(stderr, "skipping: %s\n", err)
319-
return nil, parseSkipped
320-
}
321305
fmt.Fprintf(stderr, "error creating compiler: %s\n", err)
322-
return nil, parseFailed
306+
return nil, true
323307
}
324308
if err := c.ParseCatalog(sql.Schema); err != nil {
325309
fmt.Fprintf(stderr, "# package %s\n", name)
@@ -330,7 +314,7 @@ func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.C
330314
} else {
331315
fmt.Fprintf(stderr, "error parsing schema: %s\n", err)
332316
}
333-
return nil, parseFailed
317+
return nil, true
334318
}
335319
if parserOpts.Debug.DumpCatalog {
336320
debug.Dump(c.Catalog())
@@ -344,9 +328,9 @@ func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.C
344328
} else {
345329
fmt.Fprintf(stderr, "error parsing queries: %s\n", err)
346330
}
347-
return nil, parseFailed
331+
return nil, true
348332
}
349-
return c.Result(), parseSuccess
333+
return c.Result(), false
350334
}
351335

352336
func codegen(ctx context.Context, combo config.CombinedSettings, sql OutputPair, result *compiler.Result) (string, *plugin.GenerateResponse, error) {

internal/cmd/process.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,11 @@ func processQuerySets(ctx context.Context, rp ResultProcessor, conf *config.Conf
104104
packageRegion := trace.StartRegion(gctx, "package")
105105
trace.Logf(gctx, "", "name=%s dir=%s plugin=%s", name, dir, lang)
106106

107-
result, status := parse(gctx, name, dir, sql.SQL, combo, parseOpts, errout)
108-
switch status {
109-
case parseFailed:
107+
result, failed := parse(gctx, name, dir, sql.SQL, combo, parseOpts, errout)
108+
if failed {
110109
packageRegion.End()
111110
errored = true
112111
return nil
113-
case parseSkipped:
114-
// Configuration was skipped (e.g., database unavailable)
115-
// This is not an error - just skip this configuration
116-
packageRegion.End()
117-
return nil
118112
}
119113
if err := rp.ProcessResult(gctx, combo, sql, result); err != nil {
120114
fmt.Fprintf(errout, "# package %s\n", name)
@@ -128,15 +122,12 @@ func processQuerySets(ctx context.Context, rp ResultProcessor, conf *config.Conf
128122
if err := grp.Wait(); err != nil {
129123
return err
130124
}
131-
// Always print stderr buffers (includes skip messages and errors)
132-
for i := range stderrs {
133-
if stderrs[i].Len() > 0 {
125+
if errored {
126+
for i, _ := range stderrs {
134127
if _, err := io.Copy(stderr, &stderrs[i]); err != nil {
135128
return err
136129
}
137130
}
138-
}
139-
if errored {
140131
return fmt.Errorf("errored")
141132
}
142133
return nil

internal/cmd/vet.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,9 @@ func (c *checker) checkSQL(ctx context.Context, s config.SQL) error {
484484
Debug: debug.Debug,
485485
}
486486

487-
result, status := parse(ctx, name, c.Dir, s, combo, parseOpts, c.Stderr)
488-
switch status {
489-
case parseFailed:
487+
result, failed := parse(ctx, name, c.Dir, s, combo, parseOpts, c.Stderr)
488+
if failed {
490489
return ErrFailedChecks
491-
case parseSkipped:
492-
// Configuration was skipped (e.g., database unavailable)
493-
// This is not an error for vet - just skip this configuration
494-
return nil
495490
}
496491

497492
var prep preparer

internal/compiler/engine.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
1616
sqliteanalyze "github.com/sqlc-dev/sqlc/internal/engine/sqlite/analyzer"
1717
"github.com/sqlc-dev/sqlc/internal/opts"
18-
"github.com/sqlc-dev/sqlc/internal/shfmt"
1918
"github.com/sqlc-dev/sqlc/internal/sql/catalog"
2019
"github.com/sqlc-dev/sqlc/internal/x/expander"
2120
)
@@ -120,20 +119,12 @@ func NewCompiler(conf config.SQL, combo config.CombinedSettings, parserOpts opts
120119
c.catalog = mssql.NewCatalog()
121120
c.selector = newDefaultSelector()
122121

123-
// MSSQL requires database-only mode because the teesql parser
124-
// doesn't provide AST node positions needed for query analysis
122+
// MSSQL only supports database-only mode
125123
if conf.Database == nil {
126-
return nil, fmt.Errorf("%w: mssql engine requires database configuration", ErrDatabaseUnavailable)
124+
return nil, fmt.Errorf("mssql engine requires database configuration")
127125
}
128-
hasURI := conf.Database.URI != ""
129-
if hasURI && !conf.Database.Managed {
130-
// Check if the URI is empty after environment variable substitution
131-
replacer := shfmt.NewReplacer(nil)
132-
expandedURI := replacer.Replace(conf.Database.URI)
133-
hasURI = expandedURI != ""
134-
}
135-
if !hasURI && !conf.Database.Managed {
136-
return nil, fmt.Errorf("%w: mssql engine requires database.uri to be set (ensure the environment variable is defined)", ErrDatabaseUnavailable)
126+
if conf.Database.URI == "" && !conf.Database.Managed {
127+
return nil, fmt.Errorf("mssql engine requires database.uri or database.managed")
137128
}
138129
c.databaseOnlyMode = true
139130
// Create the MSSQL analyzer (implements Analyzer interface)

internal/compiler/errors.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

internal/endtoend/case_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ type Exec struct {
2929
OS []string `json:"os"`
3030
Env map[string]string `json:"env"`
3131
Meta ExecMeta `json:"meta"`
32-
Requires []string `json:"requires"` // Required databases: "postgres", "mysql", "mssql"
3332
}
3433

3534
func parseStderr(t *testing.T, dir, testctx string) []byte {

internal/endtoend/endtoend_test.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,8 @@ func BenchmarkExamples(b *testing.B) {
101101
}
102102

103103
type textContext struct {
104-
Mutate func(*testing.T, string) func(*config.Config)
105-
Enabled func() bool
106-
AvailableDatabases map[string]bool // "postgres", "mysql", "mssql"
104+
Mutate func(*testing.T, string) func(*config.Config)
105+
Enabled func() bool
107106
}
108107

109108
func TestReplay(t *testing.T) {
@@ -192,17 +191,10 @@ func TestReplay(t *testing.T) {
192191
t.Logf("MySQL available: %v (URI: %s)", mysqlURI != "", mysqlURI)
193192
t.Logf("MSSQL available: %v (URI: %s)", mssqlURI != "", mssqlURI)
194193

195-
availableDatabases := map[string]bool{
196-
"postgres": postgresURI != "",
197-
"mysql": mysqlURI != "",
198-
"mssql": mssqlURI != "",
199-
}
200-
201194
contexts := map[string]textContext{
202195
"base": {
203-
Mutate: func(t *testing.T, path string) func(*config.Config) { return func(c *config.Config) {} },
204-
Enabled: func() bool { return true },
205-
AvailableDatabases: availableDatabases,
196+
Mutate: func(t *testing.T, path string) func(*config.Config) { return func(c *config.Config) {} },
197+
Enabled: func() bool { return true },
206198
},
207199
"managed-db": {
208200
Mutate: func(t *testing.T, path string) func(*config.Config) {
@@ -254,7 +246,6 @@ func TestReplay(t *testing.T) {
254246
// Enabled if at least one database URI is available
255247
return postgresURI != "" || mysqlURI != "" || mssqlURI != ""
256248
},
257-
AvailableDatabases: availableDatabases,
258249
},
259250
}
260251

@@ -299,13 +290,6 @@ func TestReplay(t *testing.T) {
299290
}
300291
}
301292

302-
// Skip tests that require databases that are not available
303-
for _, required := range args.Requires {
304-
if !testctx.AvailableDatabases[required] {
305-
t.Skipf("required database not available: %s", required)
306-
}
307-
}
308-
309293
opts := cmd.Options{
310294
Env: cmd.Env{
311295
Debug: opts.DebugFromString(args.Env["SQLCDEBUG"]),

internal/endtoend/testdata/column_as/mssql/exec.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)