Current section
18 Versions
Jump to
Current section
18 Versions
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…