Turns a parsed request plan (Bier.QueryParser.parse_request/1) plus a target
relation into ONE parameterized SQL statement that returns the result set as
JSON text, then executes it through the per-instance Postgrex pool.
The shape mirrors PostgREST:
SELECT coalesce(json_agg(_postgrest_t), '[]')::text AS body,
count(*) OVER() AS full_count
FROM ( SELECT <select-list> FROM <schema>.<relation>
WHERE <filters> ORDER BY <order> LIMIT <l> OFFSET <o> ) _postgrest_t;The window count (count(*) OVER()) is emitted only for Prefer: count=exact|estimated,
avoiding a WindowAgg materialization when not needed (:none and :planned modes).
User-supplied values are always passed as bound parameters ($1, $2, …);
identifiers (columns) are validated by the parser and quoted here.
Summary
Functions
Wrap a column value expression in its read-representation cast function when
the column has one, so its JSON output uses the registered representation.
Returns expr unchanged when there is no read cast. The result is a json
value (which to_jsonb/json_build_object embed directly).
Build the row source a custom media handler aggregates over.
Build a representation query whose source is a mutation CTE.
Renders the call to computed field col over the row identified by row.
The Postgres base type of a request field — a column's declared type with
its DOMAIN chain resolved (cfBaseType), or a computed field's return type.
nil when the relation does not know the name.
The read-representation cast function (<domain> AS json) for a column, as a
{schema, function} tuple, or nil when the column has no such cast.
Build and run the read query.
Build and run a read query whose source is a set-returning function
(/rpc/<fn>), called with the parsed args (a list of {name, type, value}).
Apply Prefer: timezone to an open transaction. Public so the RPC path
(Bier.Rpc) applies it through the same statement and the same error shape
rather than duplicating it.
The query-string filter parser (text AS <domain>) for a column, as a
{schema, function} tuple, or nil. When present, a filter value is parsed
through this function before comparison (cases 1808/1810).
The body-value parser (json AS <domain>) for a column, as a
{schema, function} tuple, or nil. When present, a JSON body value is
parsed through this function on write (cases 1811-1813).
Functions
Wrap a column value expression in its read-representation cast function when
the column has one, so its JSON output uses the registered representation.
Returns expr unchanged when there is no read cast. The result is a json
value (which to_jsonb/json_build_object embed directly).
@spec build_media_source(Bier.Introspection.Relation.t(), map()) :: {:ok, String.t(), [term()]}
Build the row source a custom media handler aggregates over.
A relation-scoped handler is an aggregate over the relation's whole ROW type,
so its projection is always the default select list (an explicit ?select=
takes the handler out of negotiation entirely — see Bier.CustomMedia). Only
the request's filters, order and limit/offset window narrow the set.
The rows are projected as the whole-row reference __t, so the derived table
has a single column carrying the relation's composite type and the aggregate's
argument type still matches; a bare SELECT * would degrade it to record.
Build a representation query whose source is a mutation CTE.
source_sql is the INSERT/UPDATE/DELETE ... RETURNING * statement; its
bound params are source_params (in $1..$n order). The returned SQL wraps
the source in a CTE named pgrst_source and renders plan.select (with
embedding) over it.
opts accepts :format — :json (default) or :geojson (aggregate the
representation into a GeoJSON FeatureCollection via ST_AsGeoJSON), mirroring
run/5.
The result is a single row {body, count, meta} where:
body— the JSON-array representation shaped byplan.select,count— the number of mutated rows,meta— a JSON object{"pk": <first row's PK cols>}used to build theLocationheader.
@spec computed_field_call(Bier.Introspection.Relation.t(), String.t(), String.t()) :: String.t()
Renders the call to computed field col over the row identified by row.
Qualified with the schema the function lives in, which is not necessarily
rel.schema — PostgREST only requires the function to be in an exposed schema
or on the extra search path, so a computed field may extend a relation in a
different schema. Qualifying with the relation's schema instead calls the
wrong function, or none (#100).
This is the single renderer for all three call sites (filter targets here,
select list and ORDER BY in Bier.Embed) so they cannot drift apart.
PostgREST renders this unqualified — "alias"."fn", resolved by Postgres
functional notation (tbl.f is f(tbl)) plus search_path — and once
db_extra_search_path is actually applied (#105) matching it becomes
possible. Deliberately not done: search_path is order-dependent, so
same-named fields in different schemas would resolve by position and shift
with config. That is the context-inference #100 was about, and the explicit
form gives identical results. See #106 before changing this.
The Postgres base type of a request field — a column's declared type with
its DOMAIN chain resolved (cfBaseType), or a computed field's return type.
nil when the relation does not know the name.
The read-representation cast function (<domain> AS json) for a column, as a
{schema, function} tuple, or nil when the column has no such cast.
PostgREST applies this cast in the SELECT list so the column's JSON output is
produced by the user-defined representation rather than the base type's
default rendering. PostgreSQL strips a domain to its base type for a plain
CAST(col AS json), so the cast function is invoked by name instead.
@spec run(conn :: term(), Bier.Introspection.Relation.t(), map(), map(), keyword()) :: {:ok, %{body: String.t(), count: non_neg_integer()}} | {:error, term()}
Build and run the read query.
Options:
:count_mode—:none(default) /:exact/:planned/:estimated. Controls how the total row count (forContent-Range) is computed.:max_rows— serverdb-max-rows; used by:estimatedto decide when to fall back to an exact count.:format—:json(default) or:geojson(aggregate the rows into a GeoJSON FeatureCollection viaST_AsGeoJSON; requires postgis).
Returns {:ok, %{body: json_string, count: non_neg_integer}} or
{:error, %Postgrex.Error{}}.
@spec run_function( term(), map(), Bier.Introspection.Relation.t(), [tuple()], map(), keyword() ) :: {:ok, %{body: String.t(), count: non_neg_integer()}} | {:error, term()}
Build and run a read query whose source is a set-returning function
(/rpc/<fn>), called with the parsed args (a list of {name, type, value}).
Only the flat read shape is supported (select / filters / order / limit /
offset / count) — enough for the GET RPC pagination cases. The function's
returned relation supplies the column set for select and column filters.
Options accept :format — :json (default) or :geojson (aggregate the
rows into a GeoJSON FeatureCollection via ST_AsGeoJSON), mirroring run/5 —
and :auth, the {context, config} tuple that makes the call run inside the
per-request auth transaction (SET LOCAL ROLE, request.* GUCs,
db-pre-request) exactly like a relation read. Without it the statement would
execute as the pool's connecting role — issue #108.
Apply Prefer: timezone to an open transaction. Public so the RPC path
(Bier.Rpc) applies it through the same statement and the same error shape
rather than duplicating it.
The query-string filter parser (text AS <domain>) for a column, as a
{schema, function} tuple, or nil. When present, a filter value is parsed
through this function before comparison (cases 1808/1810).
The body-value parser (json AS <domain>) for a column, as a
{schema, function} tuple, or nil. When present, a JSON body value is
parsed through this function on write (cases 1811-1813).