Documentation
¶
Overview ¶
Package db holds shared low-level database abstractions used across the GoFastr framework subpackages. Splitting this out lets slow_query, eager loading, and the CRUD handler all share the same Executor interface without depending on each other.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func TxFromContext ¶
TxFromContext returns the active *sql.Tx from context when a CRUD handler has wrapped the operation in a transaction. Lifecycle hooks may use it to perform additional database work that is atomic with the parent operation, queries the hook runs through the tx see (and only commit with) the parent write.
func WithoutTx ¶ added in v0.78.0
WithoutTx masks any ambient transaction (and its commit queue) in ctx, keeping everything else — tenant, owner, request identity. Use it when handing a context to a goroutine that outlives or runs beside the transaction's owner: a *sql.Tx shares one connection, and a statement from a second goroutine interleaves with the owner's on the wire (#353). TxFromContext and CommitQueueFromContext report absent on the result.
Types ¶
type Beginner ¶
Beginner is satisfied by *sql.DB. *sql.Tx does not satisfy it, which lets inTx skip nested begin attempts.
type CommitQueue ¶ added in v0.78.0
type CommitQueue struct {
// contains filtered or unexported fields
}
CommitQueue collects work that must run only after the transaction that owns it commits. CRUD handlers queue live-bus emissions here instead of probing the live *sql.Tx from a goroutine: a probe statement races the owner's own statements on the transaction's single connection, and the interleaved wire protocol hands each side the other's results — observed as sql.ErrNoRows from an INSERT…RETURNING and as pq "syntax error at end of input" from a well-formed query (#353). Draining after Commit also answers commit-vs-rollback exactly, where the probe had to re-derive it from row state.
func CommitQueueFromContext ¶ added in v0.78.0
func CommitQueueFromContext(ctx context.Context) (*CommitQueue, bool)
CommitQueueFromContext returns the commit queue attached by the ambient transaction's owner. A context carrying a tx but no queue means the caller wrapped its own transaction with WithTx directly; emissions for those fall back to POLLING the live *sql.Tx (a "SELECT 1" every few milliseconds until it reports sql.ErrTxDone) and then re-checking the row on the base connection. That probe statement can interleave with the owner's own statements on the transaction's single connection — the race #353 removed from every framework-owned path. There is no way to passively learn a foreign transaction's fate, so callers who own their tx should prefer WithTxQueue and drain the queue themselves after a successful Commit.
func WithTxQueue ¶ added in v0.78.0
WithTxQueue derives a context carrying both tx and a fresh CommitQueue, returning the queue for the owner to drain. Framework-owned transactions (App.InTx and crud's inTx) use this instead of WithTx, so emissions staged inside the transaction fire only after it commits.
func (*CommitQueue) Add ¶ added in v0.78.0
func (q *CommitQueue) Add(fn func())
Add queues fn to run after the owning transaction commits. Safe for concurrent use. An Add that arrives after the queue has drained runs fn immediately: the drain only ever happens once Commit has succeeded, so the work's precondition already holds, and appending to a list nothing will read again would lose it silently.
func (*CommitQueue) RunAfterCommit ¶ added in v0.78.0
func (q *CommitQueue) RunAfterCommit()
RunAfterCommit runs and clears the queued work, in Add order. The tx owner calls it exactly once, after Commit returns nil; a rollback path simply drops the queue, and the queued work never runs. Calling it again is a no-op.
type Executor ¶
type Executor interface {
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}
Executor is the interface for database operations. Both *sql.DB and *sql.Tx satisfy it; wrappers (e.g. SlowQueryLogger) implement it by delegating to an inner Executor.