all repos — postern @ fc287763b9ce6b6f9823e11c0b1173da0446ebc5

Modern mail management

internal/db/db.go (view raw)

 1package db
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"fmt"
 7	"strings"
 8
 9	_ "modernc.org/sqlite"
10)
11
12type Config struct {
13	DBPath string
14}
15type DB struct {
16	write *sql.DB
17	read  *sql.DB
18}
19
20func OpenDB(config *Config) (*DB, error) {
21	ctx := context.Background()
22	dsn := config.DBPath + "?_pragma=foreign_keys(1)"
23
24	writeDB, err := sql.Open("sqlite", dsn)
25	if err != nil {
26		return nil, fmt.Errorf("opening meta.db for writes: %w", err)
27	}
28	// SQLite only supports one writer at a time; enforce it at the pool level.
29	writeDB.SetMaxOpenConns(1)
30
31	readDB, err := sql.Open("sqlite", dsn)
32	if err != nil {
33		_ = writeDB.Close()
34		return nil, fmt.Errorf("opening meta.db for reads: %w", err)
35	}
36
37	db := &DB{write: writeDB, read: readDB}
38
39	if err := db.applyPragmas(ctx); err != nil {
40		return nil, fmt.Errorf("applying pragmas: %w", err)
41	}
42
43	if err := db.applySchema(ctx); err != nil {
44		return nil, fmt.Errorf("applying schema: %w", err)
45	}
46
47	if err := writeDB.Ping(); err != nil {
48		_ = writeDB.Close()
49		return nil, fmt.Errorf("pinging meta.db write pool: %w", err)
50	}
51
52	if err := readDB.Ping(); err != nil {
53		_ = writeDB.Close()
54		_ = readDB.Close()
55		return nil, fmt.Errorf("pinging meta.db read pool: %w", err)
56	}
57
58	return db, nil
59}
60
61func (db *DB) GetWriteTx(ctx context.Context) (*sql.Tx, error) {
62	return db.write.BeginTx(ctx, nil)
63}
64
65func escapeLike(s string) string {
66	s = strings.ReplaceAll(s, `\`, `\\`)
67	s = strings.ReplaceAll(s, `%`, `\%`)
68	s = strings.ReplaceAll(s, `_`, `\_`)
69	return s
70}