// Package database owns the Bun connection and the migration registry. package database import ( "database/sql" "fmt" "strings" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/dialect/sqlitedialect" "github.com/uptrace/bun/driver/pgdriver" "github.com/uptrace/bun/driver/sqliteshim" "github.com/uptrace/bun/extra/bundebug" "github.com/uptrace/bun/migrate" "github.com/uptrace/bun/schema" "git.k6n.net/mats/fullstack-template/internal/config" migrations "git.k6n.net/mats/fullstack-template/internal/database/migrations" ) // Open connects to the database named by cfg.DatabaseURL: // // sqlite:FILE?params Bun + modernc sqlite (pure Go, no CGO) — dev default // postgres://… Bun + pgdriver against Postgres // // It does not ping — call PingContext during startup to fail fast. func Open(cfg config.Config) (*bun.DB, error) { var sqldb *sql.DB var dialect schema.Dialect switch { case strings.HasPrefix(cfg.DatabaseURL, "sqlite:"): dsn := strings.TrimPrefix(cfg.DatabaseURL, "sqlite:") // Sensible dev defaults when the DSN brings no pragmas of its own: // WAL keeps readers unblocked during writes, busy_timeout waits out // the remaining write locks instead of erroring, foreign_keys is // what everyone expects to be on. if !strings.Contains(dsn, "_pragma") { sep := "?" if strings.Contains(dsn, "?") { sep = "&" } dsn += sep + "_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)" } var err error sqldb, err = sql.Open(sqliteshim.ShimName, dsn) if err != nil { return nil, fmt.Errorf("open sqlite %q: %w", dsn, err) } dialect = sqlitedialect.New() default: sqldb = sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(cfg.DatabaseURL))) dialect = pgdialect.New() } db := bun.NewDB(sqldb, dialect) if cfg.DebugQueries { db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) } return db, nil } // NewMigrator returns a bun migrator over the migrations registered in // internal/database/migrations. See cmd/migrate for the CLI around it. func NewMigrator(db *bun.DB) *migrate.Migrator { return migrate.NewMigrator(db, migrations.Migrations) }