|
@@ -31,7 +31,7 @@
|
|
|
|
|
|
-export_type([connection/0, connect_option/0, connect_opts/0,
|
|
|
connect_error/0, query_error/0, sql_query/0, column/0,
|
|
|
- type_name/0]).
|
|
|
+ type_name/0, epgsql_type/0]).
|
|
|
|
|
|
%% Deprecated types
|
|
|
-export_type([bind_param/0, typed_param/0,
|
|
@@ -72,48 +72,25 @@
|
|
|
-type connect_opts() :: [connect_option()].
|
|
|
-endif.
|
|
|
|
|
|
--type connect_error() ::
|
|
|
- #error{}
|
|
|
- | {unsupported_auth_method, atom()}
|
|
|
- | invalid_authorization_specification
|
|
|
- | invalid_password.
|
|
|
+-type connect_error() :: epgsql_cmd_connect:connect_error().
|
|
|
-type query_error() :: #error{}.
|
|
|
|
|
|
|
|
|
-type type_name() :: atom().
|
|
|
+-type epgsql_type() :: type_name()
|
|
|
+ | {array, type_name()}
|
|
|
+ | {unknown_oid, integer()}.
|
|
|
|
|
|
+%% Deprecated
|
|
|
+-type pg_date() :: epgsql_codec_datetime:pg_date().
|
|
|
+-type pg_time() :: epgsql_codec_datetime:pg_time().
|
|
|
+-type pg_datetime() :: epgsql_codec_datetime:pg_datetime().
|
|
|
+-type pg_interval() :: epgsql_codec_datetime:pg_interval().
|
|
|
|
|
|
+%% Deprecated
|
|
|
+-type bind_param() :: any().
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-%% Ranges are from https://www.postgresql.org/docs/current/static/datatype-datetime.html
|
|
|
--type pg_date() ::
|
|
|
- {Year :: -4712..294276,
|
|
|
- Month :: 1..12,
|
|
|
- Day :: 1..31}.
|
|
|
--type pg_time() ::
|
|
|
- {Hour :: 0..24, % Max value is 24:00:00
|
|
|
- Minute :: 0..59,
|
|
|
- Second :: 0..59 | float()}.
|
|
|
--type pg_datetime() :: {pg_date(), pg_time()}.
|
|
|
--type pg_interval() :: {pg_time(), Days :: integer(), Months :: integer()}.
|
|
|
-
|
|
|
--type bind_param() ::
|
|
|
- null
|
|
|
- | boolean()
|
|
|
- | string()
|
|
|
- | binary()
|
|
|
- | integer()
|
|
|
- | float()
|
|
|
- | pg_date()
|
|
|
- | pg_time()
|
|
|
- | pg_datetime()
|
|
|
- | pg_interval()
|
|
|
- | {list({binary(), binary() | null})} % hstore
|
|
|
- | [bind_param()]. %array (maybe nested)
|
|
|
-
|
|
|
--type typed_param() ::
|
|
|
- {epgsql_type(), bind_param()}.
|
|
|
+-type typed_param() :: {epgsql_type(), bind_param()}.
|
|
|
|
|
|
-type column() :: #column{}.
|
|
|
-type squery_row() :: tuple(). % tuple of binary().
|
|
@@ -121,7 +98,7 @@
|
|
|
-type ok_reply(RowType) ::
|
|
|
{ok, ColumnsDescription :: [column()], RowsValues :: [RowType]} | % select
|
|
|
{ok, Count :: non_neg_integer()} | % update/insert/delete
|
|
|
- {ok, Count :: non_neg_integer(), ColumnsDescription :: [#column{}], RowsValues :: [RowType]}. % update/insert/delete + returning
|
|
|
+ {ok, Count :: non_neg_integer(), ColumnsDescription :: [column()], RowsValues :: [RowType]}. % update/insert/delete + returning
|
|
|
-type error_reply() :: {error, query_error()}.
|
|
|
-type reply(RowType) :: ok_reply(RowType) | error_reply().
|
|
|
-type lsn() :: integer().
|
|
@@ -181,23 +158,22 @@ connect(C, Host, Username, Password, Opts0) ->
|
|
|
Error
|
|
|
end.
|
|
|
|
|
|
--spec update_type_cache(connection()) -> ok.
|
|
|
update_type_cache(C) ->
|
|
|
update_type_cache(C, [{epgsql_codec_hstore, []},
|
|
|
{epgsql_codec_postgis, []}]).
|
|
|
|
|
|
-spec update_type_cache(connection(), [{epgsql_codec:codec_mod(), Opts :: any()}]) ->
|
|
|
- {ok, [type_name()]} | {error, empty} | {error, query_error()}.
|
|
|
+ epgsql_cmd_update_type_cache:response() | {error, empty}.
|
|
|
update_type_cache(_C, []) ->
|
|
|
{error, empty};
|
|
|
update_type_cache(C, Codecs) ->
|
|
|
%% {error, #error{severity = error,
|
|
|
- %% message = <<"column \"typarray\" does not exist in pg_type">>, _}}
|
|
|
+ %% message = <<"column \"typarray\" does not exist in pg_type">>}}
|
|
|
%% Do not fail connect if pg_type table in not in the expected
|
|
|
%% format. Known to happen for Redshift which is based on PG v8.0.2
|
|
|
epgsql_sock:sync_command(C, epgsql_cmd_update_type_cache, Codecs).
|
|
|
|
|
|
-
|
|
|
+%% @doc close connection
|
|
|
-spec close(connection()) -> ok.
|
|
|
close(C) ->
|
|
|
epgsql_sock:close(C).
|
|
@@ -221,7 +197,7 @@ set_notice_receiver(C, PidOrName) ->
|
|
|
get_cmd_status(C) ->
|
|
|
epgsql_sock:get_cmd_status(C).
|
|
|
|
|
|
--spec squery(connection(), sql_query()) -> reply(squery_row()) | [reply(squery_row())].
|
|
|
+-spec squery(connection(), sql_query()) -> epgsql_cmd_squery:response().
|
|
|
%% @doc runs simple `SqlQuery' via given `Connection'
|
|
|
squery(Connection, SqlQuery) ->
|
|
|
epgsql_sock:sync_command(Connection, epgsql_cmd_squery, SqlQuery).
|
|
@@ -239,17 +215,19 @@ equery(C, Sql, Parameters) ->
|
|
|
Error
|
|
|
end.
|
|
|
|
|
|
--spec equery(connection(), string(), sql_query(), [bind_param()]) -> reply(equery_row()).
|
|
|
+-spec equery(connection(), string(), sql_query(), [bind_param()]) ->
|
|
|
+ epgsql_cmd_equery:response().
|
|
|
equery(C, Name, Sql, Parameters) ->
|
|
|
case parse(C, Name, Sql, []) of
|
|
|
{ok, #statement{types = Types} = S} ->
|
|
|
- Typed_Parameters = lists:zip(Types, Parameters),
|
|
|
- epgsql_sock:sync_command(C, epgsql_cmd_equery, {S, Typed_Parameters});
|
|
|
+ TypedParameters = lists:zip(Types, Parameters),
|
|
|
+ epgsql_sock:sync_command(C, epgsql_cmd_equery, {S, TypedParameters});
|
|
|
Error ->
|
|
|
Error
|
|
|
end.
|
|
|
|
|
|
--spec prepared_query(C::connection(), Name::string(), Parameters::[bind_param()]) -> reply(equery_row()).
|
|
|
+-spec prepared_query(C::connection(), Name::string(), Parameters::[bind_param()]) ->
|
|
|
+ epgsql_cmd_prepared_query:response().
|
|
|
prepared_query(C, Name, Parameters) ->
|
|
|
case describe(C, statement, Name) of
|
|
|
{ok, #statement{types = Types} = S} ->
|
|
@@ -269,7 +247,7 @@ parse(C, Sql, Types) ->
|
|
|
parse(C, "", Sql, Types).
|
|
|
|
|
|
-spec parse(connection(), iolist(), sql_query(), [epgsql_type()]) ->
|
|
|
- {ok, #statement{}} | {error, query_error()}.
|
|
|
+ epgsql_cmd_parse:response().
|
|
|
parse(C, Name, Sql, Types) ->
|
|
|
sync_on_error(
|
|
|
C, epgsql_sock:sync_command(
|
|
@@ -281,7 +259,7 @@ bind(C, Statement, Parameters) ->
|
|
|
bind(C, Statement, "", Parameters).
|
|
|
|
|
|
-spec bind(connection(), #statement{}, string(), [bind_param()]) ->
|
|
|
- ok | {error, query_error()}.
|
|
|
+ epgsql_cmd_bind:response().
|
|
|
bind(C, Statement, PortalName, Parameters) ->
|
|
|
sync_on_error(
|
|
|
C,
|
|
@@ -296,41 +274,43 @@ execute(C, S) ->
|
|
|
execute(C, S, N) ->
|
|
|
execute(C, S, "", N).
|
|
|
|
|
|
--spec execute(connection(), #statement{}, string(), non_neg_integer()) -> Reply
|
|
|
- when
|
|
|
- Reply :: {ok | partial, [equery_row()]}
|
|
|
- | {ok, non_neg_integer()}
|
|
|
- | {ok, non_neg_integer(), [equery_row()]}
|
|
|
- | {error, query_error()}.
|
|
|
+-spec execute(connection(), #statement{}, string(), non_neg_integer()) -> Reply when
|
|
|
+ Reply :: epgsql_cmd_execute:response().
|
|
|
execute(C, S, PortalName, N) ->
|
|
|
epgsql_sock:sync_command(C, epgsql_cmd_execute, {S, PortalName, N}).
|
|
|
|
|
|
--spec execute_batch(connection(), [{#statement{}, [bind_param()]}]) -> [reply(equery_row())].
|
|
|
+-spec execute_batch(connection(), [{#statement{}, [bind_param()]}]) ->
|
|
|
+ epgsql_cmd_batch:response().
|
|
|
execute_batch(C, Batch) ->
|
|
|
epgsql_sock:sync_command(C, epgsql_cmd_batch, Batch).
|
|
|
|
|
|
%% statement/portal functions
|
|
|
-
|
|
|
+-spec describe(connection(), #statement{}) -> epgsql_cmd_describe_statement:response().
|
|
|
describe(C, #statement{name = Name}) ->
|
|
|
describe(C, statement, Name).
|
|
|
|
|
|
+-spec describe(connection(), portal, iodata()) -> epgsql_cmd_describe_portal:response();
|
|
|
+ (connection(), statement, iodata()) -> epgsql_cmd_describe_statement:response().
|
|
|
describe(C, statement, Name) ->
|
|
|
sync_on_error(
|
|
|
C, epgsql_sock:sync_command(
|
|
|
C, epgsql_cmd_describe_statement, Name));
|
|
|
|
|
|
-%% TODO unknown result format of Describe portal
|
|
|
describe(C, portal, Name) ->
|
|
|
sync_on_error(
|
|
|
C, epgsql_sock:sync_command(
|
|
|
C, epgsql_cmd_describe_portal, Name)).
|
|
|
|
|
|
+%% @doc close statement
|
|
|
+-spec close(connection(), #statement{}) -> epgsql_cmd_close:response().
|
|
|
close(C, #statement{name = Name}) ->
|
|
|
close(C, statement, Name).
|
|
|
|
|
|
+-spec close(connection(), statement | portal, iodata()) -> epgsql_cmd_close:response().
|
|
|
close(C, Type, Name) ->
|
|
|
epgsql_sock:sync_command(C, epgsql_cmd_close, {Type, Name}).
|
|
|
|
|
|
+-spec sync(connection()) -> epgsql_cmd_sync:response().
|
|
|
sync(C) ->
|
|
|
epgsql_sock:sync_command(C, epgsql_cmd_sync, []).
|
|
|
|
|
@@ -400,13 +380,14 @@ sync_on_error(C, Error = {error, _}) ->
|
|
|
sync_on_error(_C, R) ->
|
|
|
R.
|
|
|
|
|
|
--spec standby_status_update(connection(), lsn(), lsn()) -> ok | error_reply().
|
|
|
+-spec standby_status_update(connection(), lsn(), lsn()) -> ok.
|
|
|
%% @doc sends last flushed and applied WAL positions to the server in a standby status update message via given `Connection'
|
|
|
standby_status_update(Connection, FlushedLSN, AppliedLSN) ->
|
|
|
gen_server:call(Connection, {standby_status_update, FlushedLSN, AppliedLSN}).
|
|
|
|
|
|
--spec start_replication(connection(), string(), Callback, cb_state(), string(), string()) -> ok | error_reply() when
|
|
|
- Callback :: module() | pid().
|
|
|
+-spec start_replication(connection(), string(), Callback, cb_state(), string(), string()) -> Response when
|
|
|
+ Response :: epgsql_cmd_start_replication:response(),
|
|
|
+ Callback :: module() | pid().
|
|
|
%% @doc instructs Postgres server to start streaming WAL for logical replication
|
|
|
%% where
|
|
|
%% `Connection' - connection in replication mode
|