pgc_client (pgc v0.2.0)

Copy Markdown View Source

A client for sending queries to the PostgreSQL server over one connection.

{ok, Options} = pgc_client_options:from_uri(~"postgresql://postgres:postgres@localhost/postgres"),
{ok, Client} = pgc_client:start_link(Options),
{ok, #{command := ~"select", rows := 1}, [#{~"answer" := 42}]} =
    pgc_client:execute(Client, ~"select $1::int4 as answer", [42]).

Rows are maps by default. The row option selects another shape:

{ok, _, [{42, ~"pgc"}]} =
    pgc_client:execute(Client, ~"select 42 as answer, 'pgc' as name", [], #{row => tuple}).

execute_fold/6 folds over the rows as they arrive, without collecting them:

{ok, #{rows := 10}, 55} = pgc_client:execute_fold(Client, ~"select generate_series(1, 10)", [],
    fun (_Columns, [N], Sum) -> {continue, Sum + N} end, 0, #{}).

transaction/3 runs a fun in a transaction, committed or rolled back as the fun returns:

42 = pgc_client:transaction(Client, fun () ->
    {ok, _, [#{~"answer" := Answer}]} = pgc_client:execute(Client, ~"select 42 as answer", []),
    {commit, Answer}
end, #{}).

stop/1 closes the connection.

Summary

Types

Runs a parameterized statement through parse/bind/execute, folding Fun over each row as it arrives. A halted fold cancels the query on the server; see execute/4 for Options' timeout semantics.

Runs a parameterized statement through parse/bind/execute, collecting every row into a list shaped per Options' row (map by default).

Encoding and decoding are deliberately done here, in the caller's own process, rather than in handle_row_data/5/handle_prepare_result/4 (which run in the connection process): the connection is a shared, serializing bottleneck, while Types (pgc_client_types:t()) wraps a protected ets table specifically so any number of callers can read it -- and therefore encode and decode -- concurrently, off the connection's own execution stack. The connection only ever mediates the wire and owns the (write side of the) type cache; the caller builds its own pgc_client_codec:t() from Types and this call's codec option (see collect/7).

Functions

Runs a parameterized statement through parse/bind/execute, collecting every row into a list of maps.

Brings ClientRef back to a clean session state -- rolls back any transaction Action left open, then clears any session-level set it left behind. Meant to be called whenever a connection is handed back to a pool, between one caller and the next.

Start a new postgresql client connection.

Start a new postgresql client connection registered using the provided ClientName.

Orders the client to exit and waits for it to terminate.

Types

client_name()

-type client_name() :: gen_statem:server_name().

client_ref()

-type client_ref() :: gen_statem:server_ref().

execute_cache_options()

-type execute_cache_options() :: false | {true, Key :: unicode:chardata() | atom()}.

execute_fold_fun(Acc)

-type execute_fold_fun(Acc) ::
          fun(([#{name := unicode:unicode_binary()}], [term() | null], Acc) ->
                  {continue, Acc} | {halt, Acc}).

Runs a parameterized statement through parse/bind/execute, folding Fun over each row as it arrives. A halted fold cancels the query on the server; see execute/4 for Options' timeout semantics.

Fun receives the result's columns, the row's values in column order and the accumulator.

execute_fold_options()

-type execute_fold_options() ::
          #{timeout => timeout() | {abs, integer()},
            cache => execute_cache_options(),
            codec => #{modules => [module()], atom() => term()}}.

execute_options()

-type execute_options() ::
          #{row => map | list | tuple | proplist,
            timeout => timeout() | {abs, integer()},
            cache => execute_cache_options(),
            codec => #{modules => [module()], atom() => term()}}.

Runs a parameterized statement through parse/bind/execute, collecting every row into a list shaped per Options' row (map by default).

Options' timeout, if given, on expiry, the query is cancelled on the server and exits with exit({timeout, _}).

Options' cache key names the prepared statement: at most 63 bytes once converted to UTF-8, as Postgres keeps no more of a name; a longer one raises {pgc, {statement_name_too_long, Name}}.

request_error()

-type request_error() ::
          #{severity := panic | fatal | error,
            code := binary(),
            message := unicode:unicode_binary(),
            detail => unicode:unicode_binary(),
            hint => unicode:unicode_binary(),
            position => pos_integer(),
            internal_position => pos_integer(),
            internal_query => unicode:unicode_binary(),
            where => [unicode:unicode_binary()],
            schema => unicode:unicode_binary(),
            table => unicode:unicode_binary(),
            column => unicode:unicode_binary(),
            data_type => unicode:unicode_binary(),
            constraint => unicode:unicode_binary(),
            file => unicode:unicode_binary(),
            line => non_neg_integer(),
            routine => unicode:unicode_binary()}.

Encoding and decoding are deliberately done here, in the caller's own process, rather than in handle_row_data/5/handle_prepare_result/4 (which run in the connection process): the connection is a shared, serializing bottleneck, while Types (pgc_client_types:t()) wraps a protected ets table specifically so any number of callers can read it -- and therefore encode and decode -- concurrently, off the connection's own execution stack. The connection only ever mediates the wire and owns the (write side of the) type cache; the caller builds its own pgc_client_codec:t() from Types and this call's codec option (see collect/7).

result_metadata()

-type result_metadata() :: #{command => binary(), rows => non_neg_integer()}.

statement_parameters()

-type statement_parameters() :: [term() | null].

statement_text()

-type statement_text() :: unicode:chardata().

transaction_options()

-type transaction_options() ::
          #{isolation => serializable | repeatable_read | read_committed | read_uncommitted | default,
            access => read_write | read_only | default,
            deferrable => boolean() | default}.

Functions

execute(ClientRef, Statement, Parameters)

-spec execute(ClientRef, Statement, Parameters) -> {ok, Metadata, Rows} | {error, Error}
                 when
                     ClientRef :: client_ref(),
                     Statement :: statement_text(),
                     Parameters :: statement_parameters(),
                     Metadata :: result_metadata(),
                     Rows :: [map()],
                     Error :: request_error().

Runs a parameterized statement through parse/bind/execute, collecting every row into a list of maps.

execute(ClientRef, Statement, Parameters, Options)

-spec execute(ClientRef, Statement, Parameters, Options) -> {ok, Metadata, Rows} | {error, Error}
                 when
                     ClientRef :: client_ref(),
                     Statement :: statement_text(),
                     Parameters :: statement_parameters(),
                     Options :: execute_options(),
                     Metadata :: result_metadata(),
                     Rows :: [dynamic()],
                     Error :: request_error().

execute_fold(ClientRef, Statement, Parameters, Fun, Acc, Options)

-spec execute_fold(ClientRef, Statement, Parameters, Fun, Acc, Options) ->
                      {ok, Metadata, Acc} | {error, Error}
                      when
                          ClientRef :: client_ref(),
                          Statement :: statement_text(),
                          Parameters :: statement_parameters(),
                          Fun :: execute_fold_fun(Acc),
                          Options :: execute_fold_options(),
                          Metadata :: result_metadata(),
                          Error :: request_error().

reset(ClientRef)

-spec reset(ClientRef) -> ok when ClientRef :: client_ref().

Brings ClientRef back to a clean session state -- rolls back any transaction Action left open, then clears any session-level set it left behind. Meant to be called whenever a connection is handed back to a pool, between one caller and the next.

start_link(Options)

-spec start_link(pgc_client_options:t()) -> gen_statem:start_ret().

Start a new postgresql client connection.

start_link(ClientName, Options)

Start a new postgresql client connection registered using the provided ClientName.

ClientName specifies the client_name/0 to register for the client process.

stop(ClientRef)

-spec stop(client_ref()) -> ok.

Orders the client to exit and waits for it to terminate.

transaction(ClientRef, Fun, Options)

-spec transaction(ClientRef, Fun, Options) -> Result
                     when
                         ClientRef :: client_ref(),
                         Fun :: fun(() -> {commit | rollback, Result}),
                         Options :: transaction_options().