init
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func New(ctx context.Context, driver, dsn string) (*sql.DB, error) {
|
||||
driverName, err := normalizeDriver(driver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if driverName == "sqlite" {
|
||||
if err := ensureSQLiteDirectory(dsn); err != nil {
|
||||
return nil, fmt.Errorf("prepare sqlite path: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
database, err := sql.Open(driverName, dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open %s database: %w", driverName, err)
|
||||
}
|
||||
|
||||
if driverName == "sqlite" {
|
||||
// In-memory databases are connection-local, so keep SQLite single-connection
|
||||
// and enable foreign keys for every operation through this handle.
|
||||
database.SetMaxOpenConns(1)
|
||||
database.SetMaxIdleConns(1)
|
||||
if _, err := database.ExecContext(ctx, "PRAGMA foreign_keys = ON"); err != nil {
|
||||
database.Close()
|
||||
return nil, fmt.Errorf("configure sqlite: %w", err)
|
||||
}
|
||||
} else {
|
||||
database.SetMaxOpenConns(10)
|
||||
database.SetMaxIdleConns(1)
|
||||
}
|
||||
|
||||
if err := database.PingContext(ctx); err != nil {
|
||||
database.Close()
|
||||
return nil, fmt.Errorf("ping %s database: %w", driverName, err)
|
||||
}
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
func normalizeDriver(driver string) (string, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(driver)) {
|
||||
case "postgres", "postgresql", "pgx":
|
||||
return "pgx", nil
|
||||
case "sqlite", "sqlite3":
|
||||
return "sqlite", nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported database driver %q (use postgres or sqlite)", driver)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureSQLiteDirectory(dsn string) error {
|
||||
if dsn == ":memory:" || strings.HasPrefix(dsn, "file::memory:") {
|
||||
return nil
|
||||
}
|
||||
|
||||
path := strings.SplitN(dsn, "?", 2)[0]
|
||||
path = strings.TrimPrefix(path, "file:")
|
||||
if path == "" || path == ":memory:" {
|
||||
return nil
|
||||
}
|
||||
|
||||
directory := filepath.Dir(path)
|
||||
if directory == "." || directory == "" {
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(directory, 0o755)
|
||||
}
|
||||
Reference in New Issue
Block a user