ergon_sql (ergon v0.5.0)
View SourceFilesystem-backed SQL loader and executor.
Convention
Statements live at priv/queries/<domain>/<operation>.sql and use PostgreSQL
positional parameters ($1..$n). Each file is keyed by {Domain, Operation}, so
priv/queries/jobs/insert.sql becomes {jobs, insert}:
ergon_sql:query({jobs, insert}, [Queue, Worker, Payload, MaxAttempts, Dedup])This gen_server walks the query roots once at boot and caches every statement in
a read-optimised ETS table, so executing a named statement is a lock-free
ets:lookup/2 plus a ergon_repo:query/3. It is supervised ahead of every
consumer that depends on it.
Host query directories
Host applications register additional priv/queries-style directories through
application environment, so one loader serves both Ergon's statements and
host-owned domains (assets, telemetry, spatial, ...). The default is Ergon's own
priv/queries only:
{ergon, [{ergon_sql, [{extra_roots, [
{priv, my_app, "queries"}, %% <my_app>/priv/queries
{app, my_app, "priv/queries"}, %% <my_app>/priv/queries, spelled the long way
"/absolute/path/to/queries" %% used verbatim
]}]}]}Keys from extra roots share the {Domain, Operation} namespace; a collision
across roots is rejected at load time exactly like one inside a single root.
Naming
find/1 returns {ok, SQL} | error; fetch/1 returns the statement or raises.
That is the maps:find/2 / maps:get/2 pairing, and the raising form carries an
EEP-54 error_info so a mistyped key prints the known keys and the path the file
was expected at, rather than a bare badkey.
Summary
Functions
Like find/1, but raises for an unknown Key.
The raw SQL registered for Key, or error if there is none.
Every registered {Domain, Operation} key, sorted.
Execute the statement registered for Key with positional Params.
Like query/2 with extra pgo options, forwarded untouched to
ergon_repo:query/3.
Re-read the query roots from disk. Returns the number of statements loaded.
Start the loader with options taken from application environment.
Start the loader, merging Opts over the application environment.
Types
-type repo_result() :: {ok, pgo:result()} | {error, term()}.
-type sql_option() :: {root, file:filename_all()} | {extra_roots, [sql_root()]}.
-type sql_root() :: file:filename_all() | {priv, atom(), file:filename_all()} | {app, atom(), file:filename_all()}.
Functions
Like find/1, but raises for an unknown Key.
The raw SQL registered for Key, or error if there is none.
-spec init([sql_option()]) -> {ok, [sql_option()]}.
-spec keys() -> [sql_key()].
Every registered {Domain, Operation} key, sorted.
-spec query(sql_key(), [term()]) -> repo_result().
Execute the statement registered for Key with positional Params.
-spec query(sql_key(), [term()], query_options()) -> repo_result().
Like query/2 with extra pgo options, forwarded untouched to
ergon_repo:query/3.
Keep this pass-through: it is the only way a caller can pin a statement to a specific connection, which both the RLS tenant path and the test fixtures need.
-spec reload() -> non_neg_integer().
Re-read the query roots from disk. Returns the number of statements loaded.
Useful in development after editing a statement; production never needs it.
Start the loader with options taken from application environment.
-spec start_link([sql_option()]) -> {ok, pid()} | {error, term()}.
Start the loader, merging Opts over the application environment.
The root option replaces Ergon's own priv/queries rather than adding to it,
which is how a test suite points the loader at a fixture directory.