Bloccs.Effects.DB.Ecto (bloccs v0.9.0)

Copy Markdown View Source

Real DB backend that writes through an Ecto repo — with no compile-time dependency on Ecto. It performs a schemaless repo.insert_all(table, [row]) via a runtime call on the repo module you configure, so the published bloccs package never forces ecto/postgrex on consumers who don't use the DB axis.

Setup

Add ecto_sql + your driver (e.g. postgrex) to your app, define a repo, then point bloccs at it:

config :bloccs, :effect_backends, db: Bloccs.Effects.DB.Ecto
config :bloccs, Bloccs.Effects.DB.Ecto, repo: MyApp.Repo

Nodes keep calling Bloccs.Effects.DB.insert(ctx.effects.db, table, attrs) — no node changes when you switch from the mock.

Semantics

  • The declared "table:insert" / "table:read" scope is enforced; a violation raises Bloccs.Effects.Denied (like the mock).
  • On success insert/3 returns {:ok, row}. If you configure returning columns (below), database-generated values (e.g. an auto-increment id) are merged into the returned row; otherwise the row is just the attrs you inserted.
  • A repo/database error is caught and returned as {:error, exception}.

Reads

get/3 / all/3 / one/3 run a parameterized repo.query!/2 (the only path that needs no compile-time Ecto.Query macro) and return rows as string-keyed maps. The filter is ANDed equality only (%{column => value}). Placeholders are chosen from repo.__adapter__/0$1, $2, … for Ecto.Adapters.Postgres, ? otherwise (SQLite3 / MyXQL). Identifiers are double-quoted, so Postgres and SQLite3 are supported; back-tick dialects (MySQL) are not in this version — implement the Bloccs.Effects.DB behaviour directly for those.

Table and column names are interpolated into SQL (values are always parameterized), so every identifier is validated against [A-Za-z_][A-Za-z0-9_]* before any SQL is built — a name that doesn't match (e.g. one derived from a message payload) raises Bloccs.Effects.Denied instead of reaching the database.

Returning generated columns

A schemaless insert_all needs an explicit column list to echo generated values. Configure one list, applied to every insert (set it to your primary key — tables lacking those columns will error):

config :bloccs, Bloccs.Effects.DB.Ecto, repo: MyApp.Repo, returning: [:id]

For a fully custom datastore, implement the Bloccs.Effects.DB behaviour directly instead of using this adapter.

Summary

Types

t()

@type t() :: %Bloccs.Effects.DB.Ecto{
  allow: [String.t()],
  repo: module() | nil,
  returning: [atom()] | nil
}