• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stephenafamo / bob / 16856507503

10 Aug 2025 02:54AM UTC coverage: 41.092% (+0.02%) from 41.074%
16856507503

push

github

stephenafamo
Disable enums output if there are no enums

19 of 40 new or added lines in 8 files covered. (47.5%)

3 existing lines in 1 file now uncovered.

9288 of 22603 relevant lines covered (41.09%)

278.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/gen/bobgen-sql/driver/sql.go
1
package driver
2

3
import (
4
        "context"
5
        "database/sql"
6
        "fmt"
7
        "io"
8
        "io/fs"
9
        "log"
10
        "os"
11

12
        "github.com/lib/pq"
13
        "github.com/stephenafamo/bob/gen"
14
        helpers "github.com/stephenafamo/bob/gen/bobgen-helpers"
15
        mysqlDriver "github.com/stephenafamo/bob/gen/bobgen-mysql/driver"
16
        psqlDriver "github.com/stephenafamo/bob/gen/bobgen-psql/driver"
17
        sqliteDriver "github.com/stephenafamo/bob/gen/bobgen-sqlite/driver"
18
        "github.com/stephenafamo/bob/gen/drivers"
19
        "github.com/stephenafamo/bob/gen/plugins"
20
        "github.com/testcontainers/testcontainers-go"
21
        mysqltest "github.com/testcontainers/testcontainers-go/modules/mysql"
22
        "github.com/testcontainers/testcontainers-go/modules/postgres"
23
)
24

25
type Config struct {
26
        helpers.Config `yaml:",squash"`
27

28
        // What dialect to generate with
29
        // psql | mysql | sqlite
30
        Dialect string
31
        // Glob pattern to match migration files
32
        Pattern string
33
        // The database schemas to generate models for
34
        Schemas []string
35
        // The name of this schema will not be included in the generated models
36
        // a context value can then be used to set the schema at runtime
37
        // useful for multi-tenant setups
38
        SharedSchema string `yaml:"shared_schema"`
39
        // How many tables to fetch in parallel
40
        Concurrency int
41
        // Which UUID package to use (gofrs or google)
42
        UUIDPkg string `yaml:"uuid_pkg"`
43
        fs      fs.FS
44
}
45

NEW
46
func RunPostgres(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
47
        d, err := getPsqlDriver(ctx, config)
×
48
        if err != nil {
×
49
                return fmt.Errorf("getting psql driver: %w", err)
×
50
        }
×
51

NEW
52
        templates := helpers.TemplatesFromWellKnownTree(gen.PSQLTemplates)
×
NEW
53
        plugins := helpers.OutputPlugins[any, any, psqlDriver.IndexExtra](pluginsConfig, templates)
×
NEW
54

×
UNCOV
55
        return gen.Run(ctx, state, d, plugins...)
×
56
}
57

58
func getPsqlDriver(ctx context.Context, config Config) (psqlDriver.Interface, error) {
×
59
        postgresContainer, err := postgres.Run(
×
60
                ctx, "postgres:16",
×
61
                postgres.BasicWaitStrategies(),
×
62
                testcontainers.WithLogger(log.New(io.Discard, "", log.LstdFlags)),
×
63
        )
×
64
        if err != nil {
×
65
                return nil, fmt.Errorf("failed to start container: %w", err)
×
66
        }
×
67
        defer func() {
×
68
                if err := testcontainers.TerminateContainer(postgresContainer); err != nil {
×
69
                        log.Printf("failed to terminate container: %s", err)
×
70
                }
×
71
        }()
72

73
        dsn, err := postgresContainer.ConnectionString(ctx, "sslmode=disable")
×
74
        if err != nil {
×
75
                return nil, fmt.Errorf("failed to get connection string: %w", err)
×
76
        }
×
77

78
        db, err := sql.Open("postgres", dsn)
×
79
        if err != nil {
×
80
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
81
        }
×
82
        defer db.Close()
×
83

×
84
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
85
                return nil, fmt.Errorf("migrating: %w", err)
×
86
        }
×
87
        db.Close() // close early
×
88

×
89
        config.Dsn = dsn
×
90
        d := wrapDriver(ctx, psqlDriver.New(psqlDriver.Config{
×
91
                Config:       config.Config,
×
92
                Schemas:      pq.StringArray(config.Schemas),
×
93
                SharedSchema: config.SharedSchema,
×
94
                Concurrency:  config.Concurrency,
×
95
                UUIDPkg:      config.UUIDPkg,
×
96
        }))
×
97

×
98
        return d, nil
×
99
}
100

NEW
101
func RunMySQL(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
102
        d, err := getMySQLDriver(ctx, config)
×
103
        if err != nil {
×
104
                return fmt.Errorf("getting mysql driver: %w", err)
×
105
        }
×
106

NEW
107
        templates := helpers.TemplatesFromWellKnownTree(gen.MySQLTemplates)
×
NEW
108
        plugins := helpers.OutputPlugins[any, any, any](pluginsConfig, templates)
×
NEW
109

×
UNCOV
110
        return gen.Run(ctx, state, d, plugins...)
×
111
}
112

113
func getMySQLDriver(ctx context.Context, config Config) (mysqlDriver.Interface, error) {
×
114
        mysqlContainer, err := mysqltest.Run(ctx,
×
115
                "mysql:8.0.35",
×
116
                mysqltest.WithDatabase("bobgen"),
×
117
                mysqltest.WithUsername("root"),
×
118
                mysqltest.WithPassword("password"),
×
119
        )
×
120
        defer func() {
×
121
                if err := testcontainers.TerminateContainer(mysqlContainer); err != nil {
×
122
                        fmt.Printf("failed to terminate MySQL container: %v\n", err)
×
123
                }
×
124
        }()
125
        if err != nil {
×
126
                return nil, fmt.Errorf("failed to start container: %w", err)
×
127
        }
×
128

129
        dsn, err := mysqlContainer.ConnectionString(ctx, "tls=skip-verify", "multiStatements=true", "parseTime=true")
×
130
        if err != nil {
×
131
                return nil, fmt.Errorf("failed to get connection string: %w", err)
×
132
        }
×
133

134
        db, err := sql.Open("mysql", dsn)
×
135
        if err != nil {
×
136
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
137
        }
×
138
        defer db.Close()
×
139

×
140
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
141
                return nil, fmt.Errorf("migrating: %w", err)
×
142
        }
×
143
        db.Close() // close early
×
144

×
145
        config.Dsn = dsn
×
146
        d := wrapDriver(ctx, mysqlDriver.New(mysqlDriver.Config{
×
147
                Config:      config.Config,
×
148
                Concurrency: config.Concurrency,
×
149
        }))
×
150

×
151
        return d, nil
×
152
}
153

NEW
154
func RunSQLite(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
155
        d, err := getSQLiteDriver(ctx, config)
×
156
        if err != nil {
×
157
                return fmt.Errorf("getting sqlite driver: %w", err)
×
158
        }
×
159

NEW
160
        templates := helpers.TemplatesFromWellKnownTree(gen.SQLiteTemplates)
×
NEW
161
        plugins := helpers.OutputPlugins[any, any, sqliteDriver.IndexExtra](pluginsConfig, templates)
×
NEW
162

×
UNCOV
163
        return gen.Run(ctx, state, d, plugins...)
×
164
}
165

166
func getSQLiteDriver(ctx context.Context, config Config) (sqliteDriver.Interface, error) {
×
167
        tmp, err := os.CreateTemp("", "bobgen_sqlite")
×
168
        if err != nil {
×
169
                return nil, fmt.Errorf("creating temp file: %w", err)
×
170
        }
×
171
        defer tmp.Close()
×
172

×
173
        db, err := sql.Open("sqlite", tmp.Name())
×
174
        if err != nil {
×
175
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
176
        }
×
177
        defer db.Close()
×
178

×
179
        attach := make(map[string]string)
×
180
        for _, schema := range config.Schemas {
×
181
                tmp, err := os.CreateTemp("", "bobgen_sqlite_"+schema)
×
182
                if err != nil {
×
183
                        return nil, fmt.Errorf("creating temp file: %w", err)
×
184
                }
×
185
                defer tmp.Close()
×
186

×
187
                attach[schema] = tmp.Name()
×
188
                _, err = db.ExecContext(ctx, fmt.Sprintf(
×
189
                        "attach database '%s' as %s", tmp.Name(), schema,
×
190
                ))
×
191
                if err != nil {
×
192
                        return nil, fmt.Errorf("could not attach %q: %w", schema, err)
×
193
                }
×
194
        }
195

196
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
197
                return nil, fmt.Errorf("migrating: %w", err)
×
198
        }
×
199
        db.Close() // close early
×
200

×
201
        config.Dsn = "file:" + tmp.Name()
×
202
        d := sqliteDriver.New(sqliteDriver.Config{
×
203
                Config:       config.Config,
×
204
                Attach:       attach,
×
205
                SharedSchema: config.SharedSchema,
×
206
        })
×
207

×
208
        return d, nil
×
209
}
210

211
func wrapDriver[T, C, I any](ctx context.Context, d drivers.Interface[T, C, I]) driver[T, C, I] {
×
212
        info, err := d.Assemble(ctx)
×
213
        return driver[T, C, I]{d, info, err}
×
214
}
×
215

216
type driver[T, C, I any] struct {
217
        drivers.Interface[T, C, I]
218
        info *drivers.DBInfo[T, C, I]
219
        err  error
220
}
221

222
func (d driver[T, C, I]) Assemble(context.Context) (*drivers.DBInfo[T, C, I], error) {
×
223
        return d.info, d.err
×
224
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc