asobi_repo (asobi v0.75.1)

View Source

Summary

Functions

aggregate(Q, Agg)

-spec aggregate(#kura_query{from :: atom() | module() | undefined,
                            select :: [atom() | term()] | {exprs, [term()]},
                            wheres :: [term()],
                            joins :: [term()],
                            order_bys :: [term()],
                            group_bys :: [atom()],
                            havings :: [term()],
                            limit :: non_neg_integer() | undefined,
                            offset :: non_neg_integer() | undefined,
                            distinct :: boolean() | [atom()],
                            lock :: binary() | undefined,
                            prefix :: binary() | undefined,
                            preloads :: [atom() | {atom(), list()}],
                            ctes :: [{binary(), #kura_query{}}],
                            combinations :: [{union | union_all | intersect | except, #kura_query{}}],
                            include_deleted :: boolean()},
                count | {count | sum | avg | min | max, atom()}) ->
                   {ok, term()} | {error, term()}.

all(Q)

-spec all(#kura_query{from :: atom() | module() | undefined,
                      select :: [atom() | term()] | {exprs, [term()]},
                      wheres :: [term()],
                      joins :: [term()],
                      order_bys :: [term()],
                      group_bys :: [atom()],
                      havings :: [term()],
                      limit :: non_neg_integer() | undefined,
                      offset :: non_neg_integer() | undefined,
                      distinct :: boolean() | [atom()],
                      lock :: binary() | undefined,
                      prefix :: binary() | undefined,
                      preloads :: [atom() | {atom(), list()}],
                      ctes :: [{binary(), #kura_query{}}],
                      combinations :: [{union | union_all | intersect | except, #kura_query{}}],
                      include_deleted :: boolean()}) ->
             {ok, [map()]} | {error, term()}.

delete(CS)

-spec delete(#kura_changeset{valid :: boolean(),
                             schema :: module() | undefined,
                             data :: map(),
                             params :: map(),
                             changes :: map(),
                             errors :: [{atom(), binary()}],
                             types :: #{atom() => kura_types:kura_type()},
                             required :: [atom()],
                             action :: atom() | undefined,
                             constraints ::
                                 [#kura_constraint{type :: unique | foreign_key | check | exclusion,
                                                   constraint :: binary(),
                                                   field :: atom(),
                                                   message :: binary()}],
                             assoc_changes :: #{atom() => #kura_changeset{} | [#kura_changeset{}]},
                             prepare :: [fun((#kura_changeset{}) -> #kura_changeset{})],
                             optimistic_lock :: atom() | undefined}) ->
                {ok, map()} | {error, term()}.

delete(Schema, Record)

-spec delete(module(), map()) -> {ok, map()} | {error, term()}.

delete_all(Q)

-spec delete_all(#kura_query{from :: atom() | module() | undefined,
                             select :: [atom() | term()] | {exprs, [term()]},
                             wheres :: [term()],
                             joins :: [term()],
                             order_bys :: [term()],
                             group_bys :: [atom()],
                             havings :: [term()],
                             limit :: non_neg_integer() | undefined,
                             offset :: non_neg_integer() | undefined,
                             distinct :: boolean() | [atom()],
                             lock :: binary() | undefined,
                             prefix :: binary() | undefined,
                             preloads :: [atom() | {atom(), list()}],
                             ctes :: [{binary(), #kura_query{}}],
                             combinations :: [{union | union_all | intersect | except, #kura_query{}}],
                             include_deleted :: boolean()}) ->
                    {ok, non_neg_integer()} | {error, term()}.

exists(Q)

-spec exists(#kura_query{from :: atom() | module() | undefined,
                         select :: [atom() | term()] | {exprs, [term()]},
                         wheres :: [term()],
                         joins :: [term()],
                         order_bys :: [term()],
                         group_bys :: [atom()],
                         havings :: [term()],
                         limit :: non_neg_integer() | undefined,
                         offset :: non_neg_integer() | undefined,
                         distinct :: boolean() | [atom()],
                         lock :: binary() | undefined,
                         prefix :: binary() | undefined,
                         preloads :: [atom() | {atom(), list()}],
                         ctes :: [{binary(), #kura_query{}}],
                         combinations :: [{union | union_all | intersect | except, #kura_query{}}],
                         include_deleted :: boolean()}) ->
                {ok, boolean()} | {error, term()}.

get(Schema, Id)

-spec get(module(), term()) -> {ok, map()} | {error, term()}.

increment/3

-spec increment(module(), #{atom() => term()}, #{atom() => integer()}) -> {ok, map()} | {error, term()}.

Add to integer counters on one row atomically, creating the row if absent.

Key is the conflict target; Deltas the counters to accumulate. One statement:

INSERT INTO quest_progress (player_id, quest_id, counter, inserted_at, updated_at)
VALUES ($1, $2, $3, now(), now())
ON CONFLICT (player_id, quest_id) DO UPDATE
   SET counter = quest_progress.counter + EXCLUDED.counter, updated_at = now()
RETURNING *

Neither half is expressible otherwise: update_all/2 SETs literals, and kura's on_conflict overwrites with the excluded value rather than adding to the stored one. A read-modify-write instead needs the wallet's advisory-lock dance to stay correct under concurrency.

This is the whole of asobi's raw-SQL surface, and the shape is the point. Every identifier in the statement is a field of Schema, matched against Schema:fields() and rejected unless it is a plain SQL name; every caller value is a bound parameter. So no caller-supplied string can reach the statement, which is a promise a general query/2 could not make and the reason there is not one.

Key must be a primary key or covered by a unique index, or Postgres rejects the conflict target. The returned map is the database row, decoded but not cast through the schema; use get/2 when the cast matters.

Timestamps are set when the schema declares them: inserted_at on the insert, updated_at on both paths.

insert(CS)

-spec insert(#kura_changeset{valid :: boolean(),
                             schema :: module() | undefined,
                             data :: map(),
                             params :: map(),
                             changes :: map(),
                             errors :: [{atom(), binary()}],
                             types :: #{atom() => kura_types:kura_type()},
                             required :: [atom()],
                             action :: atom() | undefined,
                             constraints ::
                                 [#kura_constraint{type :: unique | foreign_key | check | exclusion,
                                                   constraint :: binary(),
                                                   field :: atom(),
                                                   message :: binary()}],
                             assoc_changes :: #{atom() => #kura_changeset{} | [#kura_changeset{}]},
                             prepare :: [fun((#kura_changeset{}) -> #kura_changeset{})],
                             optimistic_lock :: atom() | undefined}) ->
                {ok, map()} | {error, term()}.

insert(CS, Opts)

-spec insert(#kura_changeset{valid :: boolean(),
                             schema :: module() | undefined,
                             data :: map(),
                             params :: map(),
                             changes :: map(),
                             errors :: [{atom(), binary()}],
                             types :: #{atom() => kura_types:kura_type()},
                             required :: [atom()],
                             action :: atom() | undefined,
                             constraints ::
                                 [#kura_constraint{type :: unique | foreign_key | check | exclusion,
                                                   constraint :: binary(),
                                                   field :: atom(),
                                                   message :: binary()}],
                             assoc_changes :: #{atom() => #kura_changeset{} | [#kura_changeset{}]},
                             prepare :: [fun((#kura_changeset{}) -> #kura_changeset{})],
                             optimistic_lock :: atom() | undefined},
             map()) ->
                {ok, map()} | {error, term()}.

insert_all(Schema, Entries)

-spec insert_all(module(), [map()]) -> {ok, non_neg_integer()} | {error, term()}.

migration_apps()

-spec migration_apps() -> [atom()].

The applications kura runs migrations from, beyond asobi itself: every installed extension.

kura calls this optional kura_repo callback for multi-application migration discovery, so extension migrations run inside core's transaction, under one advisory lock.

kura adds the repo's own application and topologically sorts the result by each application's OTP applications key, so this returns the extensions only and does not restate an ordering kura already derives.

multi(M)

-spec multi(term()) -> {ok, map()} | {error, atom(), term(), map()}.

otp_app()

-spec otp_app() -> asobi.

preload(Schema, Records, Assocs)

-spec preload(module(), map() | [map()], [atom()]) -> map() | [map()].

reload(Schema, Record)

-spec reload(module(), map()) -> {ok, map()} | {error, term()}.

transaction(Fun)

-spec transaction(fun()) -> term().

update(CS)

-spec update(#kura_changeset{valid :: boolean(),
                             schema :: module() | undefined,
                             data :: map(),
                             params :: map(),
                             changes :: map(),
                             errors :: [{atom(), binary()}],
                             types :: #{atom() => kura_types:kura_type()},
                             required :: [atom()],
                             action :: atom() | undefined,
                             constraints ::
                                 [#kura_constraint{type :: unique | foreign_key | check | exclusion,
                                                   constraint :: binary(),
                                                   field :: atom(),
                                                   message :: binary()}],
                             assoc_changes :: #{atom() => #kura_changeset{} | [#kura_changeset{}]},
                             prepare :: [fun((#kura_changeset{}) -> #kura_changeset{})],
                             optimistic_lock :: atom() | undefined}) ->
                {ok, map()} | {error, term()}.

update_all(Q, Updates)

-spec update_all(#kura_query{from :: atom() | module() | undefined,
                             select :: [atom() | term()] | {exprs, [term()]},
                             wheres :: [term()],
                             joins :: [term()],
                             order_bys :: [term()],
                             group_bys :: [atom()],
                             havings :: [term()],
                             limit :: non_neg_integer() | undefined,
                             offset :: non_neg_integer() | undefined,
                             distinct :: boolean() | [atom()],
                             lock :: binary() | undefined,
                             prefix :: binary() | undefined,
                             preloads :: [atom() | {atom(), list()}],
                             ctes :: [{binary(), #kura_query{}}],
                             combinations :: [{union | union_all | intersect | except, #kura_query{}}],
                             include_deleted :: boolean()},
                 map()) ->
                    {ok, non_neg_integer()} | {error, term()}.