Documentation
¶
Overview ¶
Package sqlcore provides a database/sql-backed DBAdapter that works with both PostgreSQL ($N placeholders) and SQLite (? placeholders).
Index ¶
- func Quote(name string) string
- type Adapter
- func (a *Adapter) AutoMigrate(ctx context.Context, reg maniflex.RegistryAccessor) error
- func (a *Adapter) BeginTx(ctx context.Context, opts *maniflex.TxOptions) (maniflex.Tx, error)
- func (a *Adapter) Close() error
- func (a *Adapter) Create(ctx context.Context, model *maniflex.ModelMeta, record any) (any, error)
- func (a *Adapter) Delete(ctx context.Context, model *maniflex.ModelMeta, id string) error
- func (a *Adapter) DriverName() string
- func (a *Adapter) DriverType() maniflex.DriverType
- func (a *Adapter) ExistsInScope(ctx context.Context, model *maniflex.ModelMeta, id string, ...) (bool, error)
- func (a *Adapter) FindByID(ctx context.Context, model *maniflex.ModelMeta, id string, ...) (any, error)
- func (a *Adapter) FindByIDForUpdate(ctx context.Context, model *maniflex.ModelMeta, id string) (any, error)
- func (a *Adapter) FindMany(ctx context.Context, model *maniflex.ModelMeta, qp *maniflex.QueryParams) ([]any, int64, error)
- func (a *Adapter) Ping(ctx context.Context) error
- func (a *Adapter) Raw(ctx context.Context, query string, args ...any) maniflex.RawResult
- func (a *Adapter) Restore(ctx context.Context, model *maniflex.ModelMeta, id string, ...) (any, error)
- func (a *Adapter) SetErrorNormalizer(fn ErrorNormalizer)
- func (a *Adapter) SetLogger(l *slog.Logger)
- func (a *Adapter) Update(ctx context.Context, model *maniflex.ModelMeta, id string, record any, ...) (any, error)
- func (a *Adapter) WriteDB() *sql.DB
- type ErrorNormalizer
- type PlaceholderBuilder
- type RawSqlResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Quote ¶
Quote is the exported version of q for packages that need to build SQL fragments with consistent identifier quoting (e.g. validate.UniqueField composing parameterised SELECT COUNT(*) queries). The encoding is ANSI SQL §5.2: double-quote delimiters with embedded quotes doubled. Works with both Postgres and SQLite.
Types ¶
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter is a generic SQL DBAdapter backed by a *sql.DB.
func New ¶
func New(writeDb *sql.DB, readDb *sql.DB, driver maniflex.DriverType, reg maniflex.RegistryAccessor) *Adapter
New wraps an existing *sql.DB and returns a maniflex.DBAdapter. Pass the same RegistryAccessor you gave to maniflex.New — the adapter uses it to resolve related models for include population and migrations.
func (*Adapter) AutoMigrate ¶
AutoMigrate synchronises the database schema with the registered models.
For each model it:
- Creates the table if it does not exist (original behaviour, safe no-op on re-run).
- Introspects the live table's columns and compares them to the model's fields.
- Issues ALTER TABLE ADD COLUMN for every field present in the model but absent from the table — allowing structs to grow without manual DDL.
- Logs a WARNING (slog.LevelWarn) for every column present in the table but absent from the model, indicating a likely removed field. It never drops columns automatically.
The entire migrate-and-diff sequence for each table runs inside a transaction (BeginTx → CREATE / introspect / ALTER … / CREATE INDEX → Commit) so it is atomic against concurrent startup processes: two replicas starting simultaneously each see either the pre-migration or post-migration schema, never a half-applied state.
func (*Adapter) BeginTx ¶
BeginTx starts a database transaction and returns a maniflex.Tx that routes all CRUD operations through the same *sql.Tx connection.
func (*Adapter) DriverName ¶
DriverName returns "postgres" or "sqlite" so dialect-aware helpers (e.g. maniflex.Migrate) can choose the right placeholder syntax and column types without importing this package.
func (*Adapter) DriverType ¶
func (a *Adapter) DriverType() maniflex.DriverType
DriverType returns the SQL dialect used by this adapter. Implements the optional maniflex.adapterDriverTyper interface so that ServerContext.DriverType() can surface the dialect to packages (e.g. pkg/ledger) that need to build driver-specific parameterised queries.
func (*Adapter) ExistsInScope ¶ added in v0.2.4
func (a *Adapter) ExistsInScope(ctx context.Context, model *maniflex.ModelMeta, id string, filters []*maniflex.FilterExpr, ) (bool, error)
ExistsInScope implements maniflex.ScopeChecker.
func (*Adapter) FindByID ¶
func (a *Adapter) FindByID(ctx context.Context, model *maniflex.ModelMeta, id string, qp *maniflex.QueryParams) (any, error)
FindByID returns the matching record as a field-populated *T scanned directly from the row (typed-models Phase 4). Includes are populated onto the record's extra carrier. createMap/updateMap still use findByIDMap for their map tails.
func (*Adapter) FindByIDForUpdate ¶
func (a *Adapter) FindByIDForUpdate(ctx context.Context, model *maniflex.ModelMeta, id string) (any, error)
FindByIDForUpdate fetches the row and acquires a pessimistic write lock. Postgres appends FOR UPDATE and routes through the write pool so the lock participates in an enclosing transaction. SQLite does a plain SELECT — the lock is at the transaction level, taken at BEGIN (db/sqlite opens write connections with _txlock=immediate).
func (*Adapter) Ping ¶
Ping verifies that both the write and read database connections are alive. It is called by the health handler when Config.HealthCheckDB is true. Ping implements the maniflex.Pinger interface.
func (*Adapter) Restore ¶ added in v0.2.4
func (a *Adapter) Restore(ctx context.Context, model *maniflex.ModelMeta, id string, qp *maniflex.QueryParams, ) (any, error)
Restore implements maniflex.Restorer: it clears the soft-delete marker on one row and returns the restored record (5.19).
It has to be its own statement rather than an Update, because every read and update path applies softDeleteCond — the row it targets is invisible to all of them. It is the mirror image of softDelete, down to the guard: softDelete requires the row to be live, this requires it to be deleted, so restoring a row that was never deleted is a 404 rather than a silent success.
Only the marker is written. updated_at is deliberately left alone so a restore does not read as an edit to anything watching that column.
func (*Adapter) SetErrorNormalizer ¶
func (a *Adapter) SetErrorNormalizer(fn ErrorNormalizer)
SetErrorNormalizer registers the driver-specific constraint-error normalizer. Driver packages call this from their Open constructor. When no normalizer is registered, driver errors from Create/Update pass through unchanged.
func (*Adapter) SetLogger ¶
SetLogger sets the logger used for adapter-level output (AutoMigrate column-drift warnings, column additions). the Server framework calls this automatically before AutoMigrate when Config.Logger is set, so callers normally do not need to call it directly.
func (*Adapter) Update ¶
func (a *Adapter) Update(ctx context.Context, model *maniflex.ModelMeta, id string, record any, present map[string]struct{}) (any, error)
Update applies the present columns of the record (*T) and returns the updated representation (*T). Transition (Phase 3): the record is bridged to its present-column map, the existing map update runs, and the result is wrapped back to a *T.
func (*Adapter) WriteDB ¶
WriteDB exposes the underlying writer *sql.DB. It is used by maniflex.Migrate to run versioned migrations inside a transaction. Application code should prefer the DBAdapter CRUD methods or Raw; this accessor exists so that cross-cutting helpers (migrations, custom maintenance scripts) can drive the same connection that the adapter uses for writes.
type ErrorNormalizer ¶
ErrorNormalizer converts a raw driver error into a *maniflex.ErrConstraint when it represents a known constraint violation (unique or foreign-key), so the DB step can return 409 Conflict instead of an opaque 500. Errors that are not constraint violations must be returned unchanged.
Constraint errors are driver-specific, so sqlcore does not handle them itself: lib/pq surfaces a typed *pq.Error with SQLSTATE codes, while modernc.org/sqlite reports them only as message strings. Each driver package (db/postgres, db/sqlite) implements its own ErrorNormalizer and registers it via Adapter.SetErrorNormalizer, keeping this package free of any driver dependency.
table is the offending model's table name, used to populate ErrConstraint.Table.
type PlaceholderBuilder ¶
type PlaceholderBuilder struct {
// contains filtered or unexported fields
}
PlaceholderBuilder is the exported version of the placeholder helper, allowing other packages (e.g. jobs/sql) to reuse this logic instead of reimplementing it locally.
Usage:
pb := sqlcore.NewPlaceholderBuilder(driver)
query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", pb.Add(userID))
rows, err := db.QueryContext(ctx, query, pb.Args()...)
func NewPlaceholderBuilder ¶
func NewPlaceholderBuilder(driver maniflex.DriverType) *PlaceholderBuilder
NewPlaceholderBuilder creates a PlaceholderBuilder for the given driver.
func (*PlaceholderBuilder) Add ¶
func (pb *PlaceholderBuilder) Add(v any) string
Add appends a value to the argument list and returns the appropriate placeholder ($1, $2... for Postgres; ? for SQLite).
func (*PlaceholderBuilder) Args ¶
func (pb *PlaceholderBuilder) Args() []any
Args returns the collected arguments for the query.
type RawSqlResult ¶
type RawSqlResult struct {
// contains filtered or unexported fields
}
── Raw SQL ──────────────────────────────────────────────────────────────
func (RawSqlResult) LastInsertId ¶
func (r RawSqlResult) LastInsertId() (*int64, error)
func (RawSqlResult) RowsAffected ¶
func (r RawSqlResult) RowsAffected() (*int64, error)