OpenDB opens a SQLite database with recommended pragmas for concurrency and foreign key support. It configures the connection pool for serialized writes (MaxOpenConns=1).
(ctx context.Context, path string)
| 16 | // OpenDB opens a SQLite database with recommended pragmas for concurrency and foreign key support. |
| 17 | // It configures the connection pool for serialized writes (MaxOpenConns=1). |
| 18 | func OpenDB(ctx context.Context, path string) (*sql.DB, error) { |
| 19 | dir := filepath.Dir(path) |
| 20 | if err := os.MkdirAll(dir, 0o700); err != nil { |
| 21 | return nil, fmt.Errorf("cannot create database directory %q: %w", dir, err) |
| 22 | } |
| 23 | |
| 24 | // Add query parameters for better concurrency handling and data integrity |
| 25 | // _pragma=busy_timeout(5000): Wait up to 5 seconds if database is locked |
| 26 | // _pragma=journal_mode(WAL): Enable Write-Ahead Logging for better concurrent access |
| 27 | // _pragma=foreign_keys(1): Enable foreign key constraints (critical for ON DELETE CASCADE) |
| 28 | dsn := path + "?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)" |
| 29 | |
| 30 | db, err := sql.Open("sqlite", dsn) |
| 31 | if err != nil { |
| 32 | if IsCantOpenError(err) { |
| 33 | return nil, DiagnoseDBOpenError(path, err) |
| 34 | } |
| 35 | return nil, err |
| 36 | } |
| 37 | |
| 38 | // Configure connection pool to serialize writes (SQLite limitation) |
| 39 | // This prevents "database is locked" errors from concurrent writes |
| 40 | db.SetMaxOpenConns(1) |
| 41 | db.SetMaxIdleConns(1) |
| 42 | db.SetConnMaxLifetime(0) |
| 43 | |
| 44 | // Verify connection works (this will trigger file creation/open) |
| 45 | if err := db.PingContext(ctx); err != nil { |
| 46 | db.Close() |
| 47 | if IsCantOpenError(err) { |
| 48 | return nil, DiagnoseDBOpenError(path, err) |
| 49 | } |
| 50 | return nil, err |
| 51 | } |
| 52 | |
| 53 | return db, nil |
| 54 | } |
| 55 | |
| 56 | // IsCantOpenError checks if the error is a SQLite CANTOPEN error (code 14). |
| 57 | func IsCantOpenError(err error) bool { |