db

package
v0.4.18 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package db provides multi-tenant PostgreSQL access with app-scoped credential isolation and row-level security.

Production: credentials injected per invocation via Lambda payload. Dev: DATABASE_URL env var with localhost fallback.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AcquireScopedConn

func AcquireScopedConn(ctx context.Context, pool *pgxpool.Pool) (*pgxpool.Conn, func(), error)

AcquireScopedConn is AcquireScoped but returns the concrete *pgxpool.Conn instead of the narrower Querier interface. Callers that need the raw driver connection underneath — e.g. COPY FROM STDIN via conn.Conn().PgConn(), which db.Querier does not expose — use this; everything else should keep using AcquireScoped.

func PrefixFrom

func PrefixFrom(ctx context.Context) string

PrefixFrom reads the module's resolved table prefix from the context. Returns the empty string when unset.

func SchemaFrom

func SchemaFrom(ctx context.Context) string

SchemaFrom reads the app schema from the context.

func Tx

func Tx(ctx context.Context, pool *pgxpool.Pool, fn func(q Querier) error) error

Tx runs fn inside a transaction. Commits on success, rolls back on error or panic.

Inside the transaction, search_path and ms.app_id are set transaction-local (SET LOCAL / set_config is_local=true) so they are automatically cleared on COMMIT or ROLLBACK. The pool's pre-borrow sanitizer is the defense-in-depth backstop before any later caller receives the connection.

err := db.Tx(ctx, pool, func(q db.Querier) error {
    queries := generated.New(q)
    item, err := queries.GetItem(ctx, id)
    if err != nil { return err }
    return queries.DeductBalance(ctx, params)
})

func TxReadOnly

func TxReadOnly(ctx context.Context, pool *pgxpool.Pool, fn func(tx pgx.Tx) error) error

TxReadOnly runs fn inside a READ ONLY transaction (pgx.TxOptions with AccessMode: pgx.ReadOnly). Postgres rejects any write attempted inside fn with SQLSTATE 25006 regardless of what the connecting role is granted — the doubled enforcement the deployed cross-module read runs under (decision 18 §2 invariant 2: consumer-role connection + READ ONLY tx). The read executes AS whatever role owns pool, so the install-time GRANT is the ceiling.

Unlike Tx, fn receives the raw pgx.Tx: the dynamic-SELECT executor needs tx.Query + pgx.CollectRows, which the Querier interface does not expose. The app schema (search_path + ms.app_id) is pinned transaction-local from ctx (WithSchema) via the shared applyScope so SET LOCAL auto-clears on COMMIT/ROLLBACK and RLS on the producer's exposed relation resolves to this tenant — SET LOCAL is legal in a read-only tx. The pool's pre-borrow sanitizer is the defense-in-depth backstop.

Acquire-a-conn (like Tx) rather than pool.BeginTx so the tenant-scoping SQL routes through the single applyScope seam (one batched round trip, one place the ms.app_id RLS GUC is composed) instead of a divergent second copy.

func WithCredential

func WithCredential(ctx context.Context, cred Credential) context.Context

WithCredential returns a context with the per-app DB credential set. Used by the platform's Lambda invoke shim before calling the module handler, so Module.DB / Module.Tx can scope queries to the app schema.

func WithCredentialProvider

func WithCredentialProvider(ctx context.Context, provider CredentialProvider) context.Context

WithCredentialProvider installs a renewable per-app database credential source.

func WithDependencies

func WithDependencies(ctx context.Context, manifest []DependencyGrant) context.Context

WithDependencies returns a context carrying the platform-resolved dependency manifest, parallel to WithSchema / WithCredential / WithPrefix. Set once by the Lambda invoke shim (runtime.InjectResources) from the trusted envelope; read by the deployed-plane DependencyDB branch. Never set from module input.

func WithModuleCredential

func WithModuleCredential(ctx context.Context, cred Credential) context.Context

WithModuleCredential returns a context with the per-module DB credential set. Used by the platform's Lambda invoke shim for handlers that touch the module's cross-app shared schema (mod_<id>). The use cases live on Module.ModuleDB; this function only handles credential injection.

func WithModuleCredentialProvider

func WithModuleCredentialProvider(ctx context.Context, provider CredentialProvider) context.Context

WithModuleCredentialProvider installs a renewable module-database source.

func WithPrefix

func WithPrefix(ctx context.Context, prefix string) context.Context

WithPrefix returns a context with the module's resolved table prefix set. The platform's Lambda invoke shim populates this from app_<app_id>.module_install.prefix per request, so the SDK can resolve a module's per-app table names without a fresh catalog round-trip on each call. Empty prefix means "no resolution available" — callers fall back to the dev/legacy mod_<id> form.

Distinct from WithSchema: schema is the Postgres search_path (app_<app_id>); prefix is the leading segment baked into per-app table names inside that schema (<username>_<slug>_<table>).

func WithSchema

func WithSchema(ctx context.Context, schema string) context.Context

WithSchema returns a context with the app schema set.

Types

type Credential

type Credential struct {
	Host      string    `json:"host"`
	Port      int       `json:"port"`
	Database  string    `json:"database"`
	Username  string    `json:"username"`
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expiresAt,omitzero"`
}

Credential holds per-invocation database credentials injected by the platform. The same struct serves both per-app and per-module credentials, distinguished by the context key they live under (credentialKey vs moduleCredentialKey). Different DB usernames separate pool cache keys naturally — no flag on the struct is needed.

func CredentialFrom

func CredentialFrom(ctx context.Context) *Credential

CredentialFrom reads the per-app DB credential from the context.

func EnvBaseCredential

func EnvBaseCredential() (Credential, error)

EnvBaseCredential returns the Host/Port/Database derived from the SDK's standard dev env vars (resolved via devEnvURL — same ladder db.Open consults). Username and Token are left empty for the caller to fill from per-invocation credentials — e.g. the install request body carries a per-(app, module) username + token but the DB location it should connect to comes from this helper.

Production (deployed runtime) will replace this with a managed-secret fetch when that infra lands; the contract (returns a partial Credential the caller completes with per-invocation fields) stays identical so the install handler doesn't change.

func ModuleCredentialFrom

func ModuleCredentialFrom(ctx context.Context) *Credential

ModuleCredentialFrom reads the per-module DB credential from the context.

type CredentialProvider

type CredentialProvider interface {
	Credential(context.Context) (Credential, error)
}

CredentialProvider supplies renewable database credentials. Implementations must keep Host, Port, Database and Username stable for their lifetime.

func CredentialProviderFrom

func CredentialProviderFrom(ctx context.Context) CredentialProvider

CredentialProviderFrom reads the renewable per-app credential source.

func ModuleCredentialProviderFrom

func ModuleCredentialProviderFrom(ctx context.Context) CredentialProvider

ModuleCredentialProviderFrom reads the renewable module-database source.

type CredentialProviderKey

type CredentialProviderKey interface {
	CredentialProviderKey() string
}

CredentialProviderKey may be implemented by a job-scoped provider to allow safe pool reuse within that provider's lifetime. Providers without a key are never shared: endpoint/user equality alone is not enough because it could attach a later task attempt to an earlier attempt's renewal capability.

type DB

type DB struct {
	// contains filtered or unexported fields
}

DB is the dev-mode database client (single pool, no credential injection).

func New

func New(ctx context.Context, connStr string) (*DB, error)

New creates a DB with the given connection string. The pool gets the same pre-borrow scope sanitizer as production pools so dev mode has identical session-state guarantees.

func Open

func Open(ctx context.Context) (*DB, error)

Open creates a DB for local development. Precedence:

MS_LOCAL_DB_URL    — set by `mirrorstack dev` to point at the bundled compose Postgres
DATABASE_URL       — generic 12-factor name, honored for non-CLI dev flows
defaultDevURL      — falls back to the compose-shape default

Use this for dev mode only. Production uses PoolCache with injected credentials. Returns an error if called inside AWS Lambda (credentials should be injected per-invocation).

func (*DB) Close

func (d *DB) Close()

Close closes the connection pool.

func (*DB) Conn

func (d *DB) Conn(ctx context.Context) (Querier, func(), error)

Conn acquires a scoped connection. It sanitizes prior session scope before applying search_path and ms.app_id from context.

func (*DB) Exec

func (d *DB) Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)

Exec runs a query without returning rows. Sets search_path if schema is in context.

func (*DB) Pool

func (d *DB) Pool() *pgxpool.Pool

Pool returns the underlying pgxpool.Pool.

type DependencyGrant

type DependencyGrant struct {
	Ref    string            `json:"ref"`
	Tables map[string]string `json:"tables"`
}

DependencyGrant is one authorized cross-module read target the platform resolves at INVOKE time and ships down the trusted Lambda envelope (decision 18 §3). It is advisory ROUTING only — never authority: the install-time GRANT SELECT on the consumer's r_<app8>_<consumer> role is the single DB-level authorizer, and Postgres enforces it on the vended pool no matter what this manifest says.

That last sentence is a DEPLOYED-plane statement. A co-located `mirrorstack dev` session runs one superuser role and has no r_<app8>_<mod> roles at all, so there is no DB-level authorizer beneath it and the SDK's Go-side walk is the only gate — see the KNOWN LIMITS block in internal/core/dependency_local.go for exactly what that does and does not buy.

  • Ref is the producer keyed by the SAME normalized form the SDK's parseProducerRef yields (the bare "slug" the platform reconstructs from the producer's owner/slug — see decision 18 §3 step 6).
  • Tables maps each exposed LOGICAL table name ("users") to the physical relation the platform computed via ids.PhysicalTableName ("m<hex>_users"). The SDK never derives the physical name; it reads it here — except in a co-located dev session, where there is no platform to compute it; see localPhysicalName. Only tables exposed + consented on the producer's running version appear.

The JSON tags are the cross-repo wire contract with api-platform's local mirror (moduleinvoke.DependencyGrant — api-platform never imports the SDK). A rename on either side surfaces as a decode mismatch, not a silent drop.

func DependenciesFrom

func DependenciesFrom(ctx context.Context) []DependencyGrant

DependenciesFrom reads the dependency manifest from the context. Returns nil when unset — which the deployed DependencyDB branch treats as "the platform does not inject a manifest yet" and fails closed to today's dev-plane-only error (the rollout gate: decision 18 §3 read step 1).

type PoolCache

type PoolCache struct {
	// contains filtered or unexported fields
}

PoolCache manages per-(host,port,db,user) connection pools. It is a thin wrapper around refcache.Cache that adds credential validation, key derivation, and pool construction. The refcount + LRU + double-checked-locking lifecycle is implemented in refcache.

func NewPoolCache

func NewPoolCache() *PoolCache

NewPoolCache creates a PoolCache with default settings.

func (*PoolCache) Close

func (c *PoolCache) Close()

Close closes all pools.

func (*PoolCache) Get

func (c *PoolCache) Get(ctx context.Context, cred Credential) (*pgxpool.Pool, func(), error)

Get returns a pool for the given credential and a release closure. The pool is refcount-pinned until release runs, so concurrent eviction cannot close it. Pair every Get with a deferred release call.

func (*PoolCache) GetProvider

func (c *PoolCache) GetProvider(ctx context.Context, provider CredentialProvider) (*pgxpool.Pool, func(), error)

GetProvider returns a pool whose BeforeConnect hook asks provider for a credential on every new physical connection. The connection scope is pinned by the first credential; a provider may rotate only the token/expiry.

type Querier

type Querier interface {
	Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
	Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}

Querier is the interface for database operations. Compatible with pgxpool.Conn and sqlc's DBTX interface.

func AcquireScoped

func AcquireScoped(ctx context.Context, pool *pgxpool.Pool) (Querier, func(), error)

AcquireScoped acquires a connection from the pool, sets search_path and ms.app_id from context via a single batch round trip. The pool's PrepareConn hook cleared the previous borrower's scope before Acquire returned.

This entry point is for non-transactional access (no surrounding BEGIN), so it uses session-scoped SET / set_config(_, _, false). The Tx() function uses transaction-local SET LOCAL inside its BEGIN block.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL