Documentation
¶
Overview ¶
Package db opens the GoRun SQLite database and provides migration, transaction, ID and time helpers shared by every module.
Index ¶
- Constants
- Variables
- func Checksum(body string) string
- func Decrypt(key []byte, ciphertext string, aad string) ([]byte, error)
- func DestructiveStatements(body string) []string
- func Encrypt(key, plaintext []byte, aad string) (string, error)
- func FileURI(path string) (string, error)
- func FormatTime(t time.Time) string
- func InTx(ctx context.Context, d *sql.DB, fn func(tx *sql.Tx) error) (err error)
- func Migrate(ctx context.Context, d *sql.DB, module string, files fs.FS) error
- func NewID() string
- func Now() string
- func Open(path string) (*sql.DB, error)
- func ParseTime(s string) (time.Time, error)
- func StripSQL(s string) string
- type MigrateReport
- type SchemaInspection
Constants ¶
const ( RuleDropTable = "DROP TABLE" RuleDropColumn = "DROP COLUMN" RuleDropIndex = "DROP INDEX" RuleRename = "RENAME" )
Destructive statement rules reported by DestructiveStatements.
Variables ¶
var ErrDecrypt = errors.New("db: decrypt failed")
ErrDecrypt is returned when a ciphertext cannot be opened: wrong key, wrong AAD, tampering or truncation. Callers cannot tell these apart, by design.
Functions ¶
func Decrypt ¶
Decrypt opens a value produced by Encrypt with the same key and aad. Any mismatch or corruption yields ErrDecrypt (wrapped); a bad key length yields a key error.
func DestructiveStatements ¶
DestructiveStatements returns the destructive rules (DROP TABLE, DROP COLUMN, DROP INDEX, RENAME) the migration body matches, ignoring comments and string literals. Nil means the migration is additive. Matching is case-insensitive and whitespace-tolerant.
func Encrypt ¶
Encrypt seals plaintext with AES-256-GCM under a 32-byte key and a fresh random 12-byte nonce. aad binds the ciphertext to its context (e.g. "settings:mail.password") so it cannot be replayed into another field. The result is standard base64 (with padding) of nonce || ciphertext || tag.
func FileURI ¶
FileURI returns path as an SQLite "file:" URI with the path percent-encoded, so characters such as '?', '#', '%' and spaces are part of the file name instead of starting the query (which carries the pragmas) or a fragment. The path is made absolute first (file:///abs).
func InTx ¶
InTx runs fn inside a transaction (BEGIN IMMEDIATE, see Open). It commits when fn returns nil and rolls back on error or panic (re-panicking).
The transaction holds SQLite's write lock, so any write made through the pool (*sql.DB) from inside fn waits busy_timeout (5s) and fails. Inside fn write only through tx: use audit RecordTx, jobs EnqueueTx and settings SetTx; call Record, Enqueue, hooks Emit and Set only after InTx returns.
func Migrate ¶
Migrate applies the module's pending migrations from files. Files must live at "migrations/NNNN_name.sql" inside files; they are applied in lexical order, each in its own transaction, and recorded in schema_migrations(module, name, applied_at, checksum). A nil FS or one without migrations is a no-op.
func NewID ¶
func NewID() string
NewID returns a new ULID string (26 chars, lexically sortable by time). ulid.Make uses a process-wide, mutex-guarded monotonic entropy source, so IDs generated within the same millisecond still sort in creation order.
func Now ¶
func Now() string
Now returns the current time as an RFC3339 UTC string at second precision. Every stored timestamp uses this format so lexical comparison in SQL (e.g. run_at <= ?) matches chronological order.
func Open ¶
Open opens (creating if needed) the SQLite database at path with WAL, busy_timeout=5000, foreign_keys=ON, synchronous=NORMAL and immediate transactions. The parent directory is created when missing.
func StripSQL ¶
StripSQL removes "--" line comments and "/* */" block comments (each becomes a space, newlines kept) and empties single-quoted string literals (doubled-quote escapes included), in one pass so an apostrophe inside a comment or "--" inside a string cannot confuse the other. Double-quoted identifiers are kept.
Types ¶
type MigrateReport ¶
type MigrateReport struct {
Module string
Applied []string // applied by this run
Backfilled []string // applied earlier without a checksum; checksum recorded now
Mismatched []string // applied earlier, but the file in this binary has different content
}
MigrateReport is what one module's migrate run did. Names are file names ("0001_x.sql").
func MigrateWithReport ¶
func MigrateWithReport(ctx context.Context, d *sql.DB, module string, files fs.FS) (MigrateReport, error)
MigrateWithReport is Migrate, also reporting what it applied and which already-applied files no longer match their recorded sha256 checksum. A mismatch is not an error: the caller decides how loudly to warn.
type SchemaInspection ¶
type SchemaInspection struct {
// Unknown are applied migrations this binary has no file for: a newer
// binary migrated the database (the schema is ahead), or a module was
// removed.
Unknown []string `json:"unknown"`
// Mismatched are applied migrations whose file in this binary differs
// from the content recorded when they ran.
Mismatched []string `json:"mismatched"`
}
SchemaInspection compares schema_migrations with the migration files a binary ships. Entries are "module/name", sorted.
func InspectMigrations ¶
func InspectMigrations(ctx context.Context, d *sql.DB, known map[string]fs.FS) (SchemaInspection, error)
InspectMigrations reads schema_migrations and compares it with known, the migration files of every module this binary ships (module name → FS as given to Migrate; a nil FS means the module has no migrations). Rows of a module missing from known are all unknown. Rows without a checksum (applied before checksums, or by an older binary) are never mismatched.
func (SchemaInspection) Ahead ¶
func (s SchemaInspection) Ahead() bool
Ahead reports whether the database holds migrations this binary does not know.