Current section

18 Versions

Jump to

Compare versions

23 files changed
+509 additions
-195 deletions
  @@ -71,6 +71,7 @@ connect(Opts) -> {ok, Connection :: epgsql:connection()} | {error, Reason :: epg
71 71 port => inet:port_number(),
72 72 ssl => boolean() | required,
73 73 ssl_opts => [ssl:ssl_option()], % @see OTP ssl app, ssl_api.hrl
74 + tcp_opts => [gen_tcp:option()], % @see OTP gen_tcp module documentation
74 75 timeout => timeout(), % socket connect timeout, default: 5000 ms
75 76 async => pid() | atom(), % process to receive LISTEN/NOTIFY msgs
76 77 codecs => [{epgsql_codec:codec_mod(), any()}]}
  @@ -84,7 +85,10 @@ connect(Host, Username, Password, Opts) -> {ok, C} | {error, Reason}.
84 85 example:
85 86
86 87 ```erlang
87 - {ok, C} = epgsql:connect("localhost", "username", "psss", #{
88 + {ok, C} = epgsql:connect(#{
89 + host => "localhost",
90 + username => "username",
91 + password => "psss",
88 92 database => "test_db",
89 93 timeout => 4000
90 94 }),
  @@ -103,6 +107,10 @@ Only `host` and `username` are mandatory, but most likely you would need `databa
103 107 if encryption isn't supported by server. if set to `required` connection will fail if encryption
104 108 is not available.
105 109 - `ssl_opts` will be passed as is to `ssl:connect/3`
110 + - `tcp_opts` will be passed as is to `gen_tcp:connect/3`. Some options are forbidden, such as
111 + `mode`, `packet`, `header`, `active`. When `tcp_opts` is not provided, epgsql does some tuning
112 + (eg, sets TCP `keepalive` and auto-tunes `buffer`), but when `tcp_opts` is provided, no
113 + additional tweaks are added by epgsql itself, other than necessary ones (`active`, `packet` and `mode`).
106 114 - `async` see [Server notifications](#server-notifications)
107 115 - `codecs` see [Pluggable datatype codecs](#pluggable-datatype-codecs)
108 116 - `nulls` terms which will be used to represent SQL `NULL`. If any of those has been encountered in
  @@ -112,6 +120,9 @@ Only `host` and `username` are mandatory, but most likely you would need `databa
112 120 Default is `[null, undefined]`, i.e. encode `null` or `undefined` in parameters as `NULL`
113 121 and decode `NULL`s as atom `null`.
114 122 - `replication` see [Streaming replication protocol](#streaming-replication-protocol)
123 + - `application_name` is an optional string parameter. It is usually set by an application upon
124 + connection to the server. The name will be displayed in the `pg_stat_activity`
125 + view and included in CSV log entries.
115 126
116 127 Options may be passed as proplist or as map with the same key names.
117 128
  @@ -427,8 +438,11 @@ epgsql:execute_batch(C, "INSERT INTO account (name, age) VALUES ($1, $2) RETURNI
427 438 [ ["Joe", 35], ["Paul", 26], ["Mary", 24] ]).
428 439 ```
429 440
430 - In case one of the batch items causes an error, the result returned for this particular
431 - item will be `{error, #error{}}` and no more results will be produced.
441 + In case one of the batch items causes an error, all the remaining queries of
442 + that batch will be ignored. So, last element of the result list will be
443 + `{error, #error{}}` and the length of the result list might be shorter that
444 + the length of the batch. For a better illustration of such scenario please
445 + refer to `epgsql_SUITE:batch_error/1`
432 446
433 447 `epgsqla:execute_batch/{2,3}` sends `{C, Ref, Results}`
434 448
  @@ -448,7 +462,7 @@ epgsql:cancel(connection()) -> ok.
448 462
449 463 PostgreSQL protocol supports [cancellation](https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.9)
450 464 of currently executing command. `cancel/1` sends a cancellation request via the
451 - new temporary TCP connection asynchronously, it doesn't await for the command to
465 + new temporary TCP/TLS_over_TCP connection asynchronously, it doesn't await for the command to
452 466 be cancelled. Instead, client should expect to get
453 467 `{error, #error{code = <<"57014">>, codename = query_canceled}}` back from
454 468 the command that was cancelled. However, normal response can still be received as well.
  @@ -37,11 +37,11 @@
37 37 <<"src/epgsql_command.erl">>,<<"src/epgsql_errcodes.erl">>,
38 38 <<"src/epgsql_fdatetime.erl">>,<<"src/epgsql_idatetime.erl">>,
39 39 <<"src/epgsql_oid_db.erl">>,<<"src/epgsql_replication.hrl">>,
40 - <<"src/epgsql_scram.erl">>,<<"src/epgsql_sock.erl">>,
41 - <<"src/epgsql_wire.erl">>,<<"src/epgsqla.erl">>,<<"src/epgsqli.erl">>,
42 - <<"src/ewkb.erl">>]}.
40 + <<"src/epgsql_sasl_prep_profile.erl">>,<<"src/epgsql_scram.erl">>,
41 + <<"src/epgsql_sock.erl">>,<<"src/epgsql_wire.erl">>,<<"src/epgsqla.erl">>,
42 + <<"src/epgsqli.erl">>,<<"src/ewkb.erl">>]}.
43 43 {<<"licenses">>,[<<"BSD">>]}.
44 44 {<<"links">>,[{<<"Github">>,<<"https://github.com/epgsql/epgsql">>}]}.
45 45 {<<"name">>,<<"epgsql">>}.
46 46 {<<"requirements">>,[]}.
47 - {<<"version">>,<<"4.4.0">>}.
47 + {<<"version">>,<<"4.5.0">>}.
  @@ -36,7 +36,8 @@
36 36 -type response() :: [{ok, Count :: non_neg_integer(), Rows :: [tuple()]}
37 37 | {ok, Count :: non_neg_integer()}
38 38 | {ok, Rows :: [tuple()]}
39 - | {error, epgsql:query_error()}].
39 + | {error, epgsql:query_error()}
40 + ].
40 41 -type state() :: #batch{}.
41 42
42 43 -spec init(arguments()) -> state().
  @@ -57,10 +58,9 @@ execute(Sock, #batch{batch = Batch, statement = undefined} = State) ->
57 58 BinFormats = epgsql_wire:encode_formats(Columns),
58 59 add_command(StatementName, Types, Parameters, BinFormats, Codec, Acc)
59 60 end,
60 - [{?SYNC, []}],
61 + [epgsql_wire:encode_sync()],
61 62 Batch),
62 - epgsql_sock:send_multi(Sock, Commands),
63 - {ok, Sock, State};
63 + {send_multi, Commands, Sock, State};
64 64 execute(Sock, #batch{batch = Batch,
65 65 statement = #statement{name = StatementName,
66 66 columns = Columns,
  @@ -73,16 +73,15 @@ execute(Sock, #batch{batch = Batch,
73 73 fun(Parameters, Acc) ->
74 74 add_command(StatementName, Types, Parameters, BinFormats, Codec, Acc)
75 75 end,
76 - [{?SYNC, []}],
76 + [epgsql_wire:encode_sync()],
77 77 Batch),
78 - epgsql_sock:send_multi(Sock, Commands),
79 - {ok, Sock, State}.
78 + {send_multi, Commands, Sock, State}.
80 79
81 80 add_command(StmtName, Types, Params, BinFormats, Codec, Acc) ->
82 81 TypedParameters = lists:zip(Types, Params),
83 82 BinParams = epgsql_wire:encode_parameters(TypedParameters, Codec),
84 - [{?BIND, [0, StmtName, 0, BinParams, BinFormats]},
85 - {?EXECUTE, [0, <<0:?int32>>]} | Acc].
83 + [epgsql_wire:encode_bind("", StmtName, BinParams, BinFormats),
84 + epgsql_wire:encode_execute("", 0) | Acc].
86 85
87 86 handle_message(?BIND_COMPLETE, <<>>, Sock, State) ->
88 87 Columns = current_cols(State),
  @@ -110,8 +109,7 @@ handle_message(?COMMAND_COMPLETE, Bin, Sock,
110 109 {ok, Rows}
111 110 end,
112 111 {add_result, Result, {complete, Complete}, Sock, State#batch{batch = Batch}};
113 - handle_message(?READY_FOR_QUERY, _Status, Sock, #batch{batch = B} = _State) when
114 - length(B) =< 1 ->
112 + handle_message(?READY_FOR_QUERY, _Status, Sock, _State) ->
115 113 Results = epgsql_sock:get_results(Sock),
116 114 {finish, Results, done, Sock};
117 115 handle_message(?ERROR, Error, Sock, #batch{batch = [_ | Batch]} = State) ->
  @@ -1,4 +1,4 @@
1 - %% @doc Binds placeholder parameters to prepared statement
1 + %% @doc Binds placeholder parameters to prepared statement, creating a "portal"
2 2 %%
3 3 %% ```
4 4 %% > Bind
  @@ -30,13 +30,11 @@ execute(Sock, #bind{stmt = Stmt, portal = PortalName, params = Params} = St) ->
30 30 TypedParams = lists:zip(Types, Params),
31 31 Bin1 = epgsql_wire:encode_parameters(TypedParams, Codec),
32 32 Bin2 = epgsql_wire:encode_formats(Columns),
33 - epgsql_sock:send_multi(
34 - Sock,
35 - [
36 - {?BIND, [PortalName, 0, StatementName, 0, Bin1, Bin2]},
37 - {?FLUSH, []}
38 - ]),
39 - {ok, Sock, St}.
33 + Commands = [
34 + epgsql_wire:encode_bind(PortalName, StatementName, Bin1, Bin2),
35 + epgsql_wire:encode_flush()
36 + ],
37 + {send_multi, Commands, Sock, St}.
40 38
41 39 handle_message(?BIND_COMPLETE, <<>>, Sock, _State) ->
42 40 {finish, ok, ok, Sock};
  @@ -22,17 +22,11 @@ init({Type, Name}) ->
22 22 #close{type = Type, name = Name}.
23 23
24 24 execute(Sock, #close{type = Type, name = Name} = St) ->
25 - Type2 = case Type of
26 - statement -> ?PREPARED_STATEMENT;
27 - portal -> ?PORTAL
28 - end,
29 - epgsql_sock:send_multi(
30 - Sock,
31 - [
32 - {?CLOSE, [Type2, Name, 0]},
33 - {?FLUSH, []}
34 - ]),
35 - {ok, Sock, St}.
25 + Packets = [
26 + epgsql_wire:encode_close(Type, Name),
27 + epgsql_wire:encode_flush()
28 + ],
29 + {send_multi, Packets, Sock, St}.
36 30
37 31 handle_message(?CLOSE_COMPLETE, <<>>, Sock, _St) ->
38 32 {finish, ok, ok, Sock};
Loading more files…