Browse Source

Cleanup edocs. Fixes #213

- Make rebar3 edoc work
- Add some module-level edocs
- Add basic doc/overview.edoc
- Add edoc to travis
Sergey Prokhorov 5 years ago
parent
commit
a27772a60a
45 changed files with 376 additions and 102 deletions
  1. 2 0
      .gitignore
  2. 1 0
      .travis.yml
  3. 3 0
      Makefile
  4. 71 0
      doc/overview.edoc
  5. 2 1
      rebar.config
  6. 11 0
      src/commands/epgsql_cmd_batch.erl
  7. 6 0
      src/commands/epgsql_cmd_bind.erl
  8. 4 0
      src/commands/epgsql_cmd_close.erl
  9. 4 2
      src/commands/epgsql_cmd_connect.erl
  10. 5 1
      src/commands/epgsql_cmd_describe_portal.erl
  11. 7 2
      src/commands/epgsql_cmd_describe_statement.erl
  12. 9 0
      src/commands/epgsql_cmd_equery.erl
  13. 8 0
      src/commands/epgsql_cmd_execute.erl
  14. 4 0
      src/commands/epgsql_cmd_parse.erl
  15. 5 1
      src/commands/epgsql_cmd_prepared_query.erl
  16. 7 0
      src/commands/epgsql_cmd_squery.erl
  17. 5 0
      src/commands/epgsql_cmd_start_replication.erl
  18. 5 0
      src/commands/epgsql_cmd_sync.erl
  19. 1 1
      src/commands/epgsql_cmd_update_type_cache.erl
  20. 5 2
      src/datatypes/epgsql_codec_boolean.erl
  21. 12 5
      src/datatypes/epgsql_codec_bpchar.erl
  22. 11 4
      src/datatypes/epgsql_codec_datetime.erl
  23. 5 2
      src/datatypes/epgsql_codec_float.erl
  24. 9 3
      src/datatypes/epgsql_codec_geometric.erl
  25. 7 5
      src/datatypes/epgsql_codec_hstore.erl
  26. 6 3
      src/datatypes/epgsql_codec_integer.erl
  27. 7 2
      src/datatypes/epgsql_codec_intrange.erl
  28. 8 3
      src/datatypes/epgsql_codec_json.erl
  29. 6 3
      src/datatypes/epgsql_codec_net.erl
  30. 1 0
      src/datatypes/epgsql_codec_noop.erl
  31. 9 2
      src/datatypes/epgsql_codec_postgis.erl
  32. 13 4
      src/datatypes/epgsql_codec_text.erl
  33. 7 2
      src/datatypes/epgsql_codec_timerange.erl
  34. 6 3
      src/datatypes/epgsql_codec_uuid.erl
  35. 45 34
      src/epgsql.erl
  36. 7 1
      src/epgsql_binary.erl
  37. 3 0
      src/epgsql_codec.erl
  38. 4 2
      src/epgsql_command.erl
  39. 3 3
      src/epgsql_oid_db.erl
  40. 7 5
      src/epgsql_scram.erl
  41. 5 3
      src/epgsql_sock.erl
  42. 9 1
      src/epgsql_wire.erl
  43. 7 0
      src/epgsqla.erl
  44. 7 0
      src/epgsqli.erl
  45. 7 2
      src/ewkb.erl

+ 2 - 0
.gitignore

@@ -1,3 +1,5 @@
 _build
 rebar3
 datadir/
+doc/*
+!doc/overview.edoc

+ 1 - 0
.travis.yml

@@ -20,4 +20,5 @@ matrix:
 script:
   - '[ "$TRAVIS_OTP_RELEASE" = "18.3" ] || make elvis' # TODO: remove the guard when OTP18 support is dropped
   - make test
+  - make edoc
   - make dialyzer

+ 3 - 0
Makefile

@@ -34,4 +34,7 @@ dialyzer: compile
 elvis: $(REBAR)
 	@$(REBAR) as lint lint
 
+edoc: $(REBAR)
+	@$(REBAR) edoc
+
 .PHONY: all compile clean common-test eunit cover test dialyzer elvis

+ 71 - 0
doc/overview.edoc

@@ -0,0 +1,71 @@
+
+@title epgsql - PostgreSQL driver for Erlang
+@doc
+== Interfaces ==
+Epgsql has 3 API interfaces:
+
+<ul>
+  <li>{@link epgsql} - synchronous</li>
+  <li>{@link epgsqla} - asynchronous</li>
+  <li>{@link epgsqli} - incremental</li>
+</ul>
+
+They have the same set of functions, but differ by the way they deliver results.
+
+`epgsql' returns results as a function's return value;
+`epgsqla' delivers whole result as a single message;
+`epgsqli' delivers results and metadata in individual messages row-by-row.
+
+It's usualy not a problem to use different interfaces with the same connection.
+
+== Example session ==
+
+```
+{ok, C} = epgsql:connect(#{host => "localhost",
+                           username => "test-user", password => "test",
+                           database => "test-db"}),
+{ok, _Columns, Rows} = epgsql:equery(C, "SELECT * FROM users WHERE id=$1", [42]),
+io:format("Users: ~p~n", [Rows]),
+
+Ref = epgsqla:squery(C, "SELECT count(*) FROM users"),
+receive
+  {C, Ref, {ok, [#column{}], [{Count}]}} ->
+    io:format("count: ~p~n", [binary_to_integer(Count)]);
+  {C, Ref, #error{} = E} ->
+    io:format("error: ~p~n", [E])
+after 10000 ->
+    io:format("timeout"),
+    ok = epgsql:cancel(C),
+    % we still can receive normal result because `cancel' is always asynchronous
+    % otherwise we will receive
+    % `#error{code = <<"57014">>, codename = query_canceled}'
+    receive {C, Ref, OkOrCancel} ->
+      io:format("cancelation result: ~p~n", [OkOrCancel])
+    end
+end,
+ok =
+ epgsql:with_transaction(
+   C,
+   fun() ->
+     [{ok, _}, {ok, _}] =
+       epgsql:execute_batch(
+         C, "INSERT INTO users (name, age) VALUES ($1, $2)",
+         [
+          [<<"Joe">>, 35],
+          [<<"Mary">>, 24]
+         ]),
+     ok
+   end, #{}),
+ok = epgsql:close(C).
+'''
+
+== Commands ==
+
+Client can execute a number of built-in commands as well as define
+their own. See {@link epgsql_command}.
+
+== Datatypes ==
+
+Epgsql supports both text and binary data encodings. There are a bunch
+of built-in codecs and it's possible to implement custom ones
+as well as fine-tune some of built-ins. See {@link epgsql_codec}.

+ 2 - 1
rebar.config

@@ -3,7 +3,8 @@
 {eunit_opts, [verbose]}.
 
 {cover_enabled, true}.
-{cover_print_enabled, true}.
+
+{edoc_opts, [{preprocess, true}]}.
 
 {profiles, [
     {test, [

+ 11 - 0
src/commands/epgsql_cmd_batch.erl

@@ -1,3 +1,12 @@
+%% @doc Execute multiple extended queries in a single network round-trip
+%%
+%% There are 2 kinds of interface:
+%% <ol>
+%%  <li>To execute multiple queries, each with it's own `statement()'</li>
+%%  <li>To execute multiple queries, but by binding different parameters to the
+%%  same `statement()'</li>
+%% </ol>
+%% ```
 %% > {Bind
 %% <  BindComplete
 %% >  Execute
@@ -5,6 +14,7 @@
 %% <  CommandComplete}*
 %% > Sync
 %% < ReadyForQuery
+%% '''
 -module(epgsql_cmd_batch).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).
@@ -57,6 +67,7 @@ execute(Sock, #batch{batch = Batch,
                                             types = Types}} = State) ->
     Codec = epgsql_sock:get_codec(Sock),
     BinFormats = epgsql_wire:encode_formats(Columns),
+    %% TODO: build some kind of encoder and reuse it for each batch item
     Commands =
         lists:foldr(
           fun(Parameters, Acc) ->

+ 6 - 0
src/commands/epgsql_cmd_bind.erl

@@ -1,5 +1,11 @@
+%% @doc Binds placeholder parameters to prepared statement
+%%
+%% ```
 %% > Bind
 %% < BindComplete
+%% '''
+%% @see epgsql_cmd_parse
+%% @see epgsql_cmd_execute
 -module(epgsql_cmd_bind).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 4 - 0
src/commands/epgsql_cmd_close.erl

@@ -1,5 +1,9 @@
+%% @doc Closes statement / portal
+%%
+%% ```
 %% > Close
 %% < CloseComplete
+%% '''
 -module(epgsql_cmd_close).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 4 - 2
src/commands/epgsql_cmd_connect.erl

@@ -1,6 +1,8 @@
+%%% @doc Connects to the server and performs all the necessary handshakes.
+%%%
 %%% Special kind of command - it's exclusive: no other commands can run until
 %%% this one finishes.
-%%% It also uses some 'private' epgsql_sock's APIs
+%%% It also uses some "private" epgsql_sock's APIs
 %%%
 -module(epgsql_cmd_connect).
 -behaviour(epgsql_command).
@@ -105,7 +107,7 @@ opts_hide_password(Opts) -> Opts.
 
 
 %% @doc this function wraps plaintext password to a lambda function, so, if
-%% epgsql_sock process crashes when executing `connect` command, password will
+%% epgsql_sock process crashes when executing `connect' command, password will
 %% not appear in a crash log
 -spec hide_password(iodata()) -> fun( () -> iodata() ).
 hide_password(Password) when is_list(Password);

+ 5 - 1
src/commands/epgsql_cmd_describe_portal.erl

@@ -1,5 +1,9 @@
-%% > Describe
+%% @doc Asks the server to provide description of portal's results columns
+%%
+%% ```
+%% > Describe(PORTAL)
 %% < RowDescription | NoData
+%% '''
 -module(epgsql_cmd_describe_portal).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 7 - 2
src/commands/epgsql_cmd_describe_statement.erl

@@ -1,7 +1,12 @@
-%% Almost the same as "parse"
-%% > Describe
+%% @doc Asks server to provide input parameter and result rows information.
+%%
+%% Almost the same as {@link epgsql_cmd_parse}.
+%%
+%% ```
+%% > Describe(STATEMENT)
 %% < ParameterDescription
 %% < RowDescription | NoData
+%% '''
 -module(epgsql_cmd_describe_statement).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 9 - 0
src/commands/epgsql_cmd_equery.erl

@@ -1,3 +1,10 @@
+%% @doc Performs 2nd stage of
+%% <a href="https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY">
+%% extended query protocol.</a>
+%%
+%% Takes prepared `statement()' and bind-parameters for placeholders and produces
+%% query results.
+%% ```
 %% > Bind
 %% < BindComplete
 %% > Execute
@@ -7,6 +14,8 @@
 %% < CloseComplete
 %% > Sync
 %% < ReadyForQuery
+%% '''
+%% @see epgsql_cmd_parse
 -module(epgsql_cmd_equery).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 8 - 0
src/commands/epgsql_cmd_execute.erl

@@ -1,6 +1,14 @@
+%% @doc Executes a portal.
+%%
+%% It's possible to tell the server to only return limited number of rows by
+%% providing non-zero `MaxRows' parameter.
+%% ```
 %% > Execute
 %% < DataRow*
 %% < CommandComplete | PortalSuspended
+%% '''
+%% @see epgsql_cmd_parse
+%% @see epgsql_cmd_bind
 -module(epgsql_cmd_execute).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 4 - 0
src/commands/epgsql_cmd_parse.erl

@@ -1,8 +1,12 @@
+%% @doc Asks server to parse SQL query and send information aboud bind-parameters and result columns.
+%%
+%% ```
 %% > Parse
 %% < ParseComplete
 %% > Describe
 %% < ParameterDescription
 %% < RowDescription | NoData
+%% '''
 -module(epgsql_cmd_parse).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 5 - 1
src/commands/epgsql_cmd_prepared_query.erl

@@ -1,4 +1,7 @@
-%% Almost the same as equery, but don't execute 'CLOSE'
+%% @doc Almost the same as equery, but don't execute 'CLOSE'
+%%
+%% So, statement can be reused multiple times.
+%% ```
 %% > Bind
 %% < BindComplete
 %% > Execute
@@ -6,6 +9,7 @@
 %% < CommandComplete
 %% > Sync
 %% < ReadyForQuery
+%% '''
 -module(epgsql_cmd_prepared_query).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 7 - 0
src/commands/epgsql_cmd_squery.erl

@@ -1,4 +1,10 @@
+%% @doc Executes SQL query(es) using
+%% <a href="https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.4">
+%% simple query protocol</a>
+%%
+%% Squery can not have placeholders.
 %% Squery may contain many semicolon-separated queries
+%% ```
 %% > Query
 %% < (RowDescription?
 %% <  DataRow*
@@ -8,6 +14,7 @@
 %% > Query when len(strip(Query)) == 0
 %% < EmptyQueryResponse
 %% < ReadyForQuery
+%% '''
 -module(epgsql_cmd_squery).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 5 - 0
src/commands/epgsql_cmd_start_replication.erl

@@ -1,5 +1,10 @@
+%% @doc Requests server to start sending replication packets
+%%
+%% See {@link epgsql:connect/1} `replication' parameter.
+%% ```
 %% > SimpleQuery "START_REPLICATION ..."
 %% < CopyBothResponse | Error
+%% '''
 -module(epgsql_cmd_start_replication).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 5 - 0
src/commands/epgsql_cmd_sync.erl

@@ -1,5 +1,10 @@
+%% @doc Synchronize client and server states for multi-command combinations
+%%
+%% Should be executed if APIs start to return `{error, sync_required}'.
+%% ```
 %% > Sync
 %% < ReadyForQuery
+%% '''
 -module(epgsql_cmd_sync).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 1 - 1
src/commands/epgsql_cmd_update_type_cache.erl

@@ -1,4 +1,4 @@
-%% Special command. Executes Squery over pg_type table and updates codecs.
+%% @doc Special command. Executes Squery over pg_type table and updates codecs.
 -module(epgsql_cmd_update_type_cache).
 -behaviour(epgsql_command).
 -export([init/1, execute/2, handle_message/4]).

+ 5 - 2
src/datatypes/epgsql_codec_boolean.erl

@@ -1,8 +1,11 @@
 %%% @doc
 %%% Codec for `bool'.
+%%%
 %%% `unknown' is represented by `null'.
-%%% https://www.postgresql.org/docs/current/static/datatype-boolean.html
-%%% $PG$/src/backend/utils/adt/bool.c
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-boolean.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/bool.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 12 - 5
src/datatypes/epgsql_codec_bpchar.erl

@@ -1,9 +1,16 @@
 %%% @doc
-%%% Codec for `bpchar', `char' (CHAR(N), char).
-%%% ```SELECT 1::char''' ```SELECT 'abc'::char(10)'''
-%%% For 'text', 'varchar' see epgsql_codec_text.erl.
-%%% https://www.postgresql.org/docs/10/static/datatype-character.html
-%%% $PG$/src/backend/utils/adt/varchar.c
+%%% Codec for blank-padded fixed-size character type
+%%%
+%%% `CHAR' (single-byte) is represented as `byte()';
+%%% `CHARACTER(N) / CHAR(N)' as binary string
+%%%
+%%% <code>SELECT 1::char;</code> <code>SELECT 'abc'::char(10)</code>
+%%%
+%%% For 'text', 'varchar' see {@link epgsql_codec_text}.
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/10/static/datatype-character.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/varchar.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 11 - 4
src/datatypes/epgsql_codec_datetime.erl

@@ -1,9 +1,16 @@
 %%% @doc
 %%% Codec for `time', `timetz', `date', `timestamp', `timestamptz', `interval'
-%%% https://www.postgresql.org/docs/current/static/datatype-datetime.html
-%%% $PG$/src/backend/utils/adt/timestamp.c // `timestamp', `timestamptz', `interval'
-%%% $PG$/src/backend/utils/adt/datetime.c // helpers
-%%% $PG$/src/backend/utils/adt/date.c // `time', `timetz', `date'
+%%%
+%%% It supports both integer and float datetime representations (see
+%%% [https://www.postgresql.org/docs/current/runtime-config-preset.html#GUC-INTEGER-DATETIMES]).
+%%% But float representation support might be eventually removed.
+%%%
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-datetime.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/timestamp.c // `timestamp', `timestamptz', `interval'</li>
+%%%  <li>$PG$/src/backend/utils/adt/datetime.c // helpers</li>
+%%%  <li>$PG$/src/backend/utils/adt/date.c // `time', `timetz', `date'</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 5 - 2
src/datatypes/epgsql_codec_float.erl

@@ -1,7 +1,10 @@
 %%% @doc
 %%% Codec for `float4', `float8' (real, double precision).
-%%% https://www.postgresql.org/docs/current/static/datatype-numeric.html#datatype-float
-%%% $PG$/src/backend/utils/adt/float.c
+%%%
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-numeric.html#datatype-float]</li>
+%%%  <li>$PG$/src/backend/utils/adt/float.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 9 - 3
src/datatypes/epgsql_codec_geometric.erl

@@ -1,8 +1,14 @@
 %%% @doc
 %%% Codec for `point'.
-%%% https://www.postgresql.org/docs/current/static/datatype-geometric.html
-%%% $PG$/src/backend/utils/adt/geo_ops.c
-%%% XXX: it's not PostGIS!
+%%%
+%%% Codecs for other geometric datatypes (line, box, path, polygon, circle) can
+%%% be added later.
+%%%
+%%% XXX: it's not PostGIS! For PostGIS see {@link epgsql_codec_postgis}.
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-geometric.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/geo_ops.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 %%% TODO: line, lseg, box, path, polygon, circle

+ 7 - 5
src/datatypes/epgsql_codec_hstore.erl

@@ -1,10 +1,12 @@
 %%% @doc
 %%% Codec for `hstore' type.
-%%% https://www.postgresql.org/docs/current/static/hstore.html
-%%% XXX: hstore not a part of postgresql builtin datatypes, it's in contrib.
-%%% It should be enabled in postgresql by command
-%%% `CREATE EXTENSION hstore`
-%%% $PG$/contrib/hstore/
+%%%
+%%% XXX: hstore is not a part of postgresql builtin datatypes, it's in contrib.
+%%% It should be enabled in postgresql by command `CREATE EXTENSION hstore'.
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/hstore.html]</li>
+%%%  <li>$PG$/contrib/hstore/</li>
+%%% </ul>
 %%% @end
 %%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 6 - 3
src/datatypes/epgsql_codec_integer.erl

@@ -1,8 +1,11 @@
 %%% @doc
 %%% Codec for `int2', `int4', `int8' (smallint, integer, bigint).
-%%% https://www.postgresql.org/docs/current/static/datatype-numeric.html#datatype-int
-%%% $PG$/src/backend/utils/adt/int.c
-%%% $PG$/src/backend/utils/adt/int8.c
+%%%
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-numeric.html#datatype-int]</li>
+%%%  <li>$PG$/src/backend/utils/adt/int.c</li>
+%%%  <li>$PG$/src/backend/utils/adt/int8.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 7 - 2
src/datatypes/epgsql_codec_intrange.erl

@@ -1,7 +1,12 @@
 %%% @doc
 %%% Codec for `int4range', `int8range' types.
-%%% https://www.postgresql.org/docs/current/static/rangetypes.html#rangetypes-builtin
-%%% $PG$/src/backend/utils/adt/rangetypes.c
+%%%
+%%% <ul>
+%%%   <li>[https://www.postgresql.org/docs/current/static/rangetypes.html#rangetypes-builtin]</li>
+%%%   <li>$PG$/src/backend/utils/adt/rangetypes.c</li>
+%%% </ul>
+%%% @end
+%%% @see epgsql_codec_integer
 %%% @end
 %%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 %%% TODO: universal range, based on pg_range table

+ 8 - 3
src/datatypes/epgsql_codec_json.erl

@@ -1,8 +1,13 @@
 %%% @doc
 %%% Codec for `json', `jsonb'
-%%% https://www.postgresql.org/docs/current/static/datatype-json.html
-%%% $PG$/src/backend/utils/adt/json.c // `json'
-%%% $PG$/src/backend/utils/adt/jsonb.c // `jsonb'
+%%%
+%%% It is possible to instruct the codec to do JSON encoding/decoding to Erlang
+%%% terms by providing callback module name, see {@link json_mod()}.
+%%% <ul>
+%%%   <li>[https://www.postgresql.org/docs/current/static/datatype-json.html]</li>
+%%%   <li>$PG$/src/backend/utils/adt/json.c // `json'</li>
+%%%   <li>$PG$/src/backend/utils/adt/jsonb.c // `jsonb'</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 6 - 3
src/datatypes/epgsql_codec_net.erl

@@ -1,9 +1,12 @@
 %%% @doc
 %%% Codec for `inet', `cidr'
-%%% https://www.postgresql.org/docs/10/static/datatype-net-types.html
-%%% $PG$/src/backend/utils/adt/network.c
 %%%
-%%% TIP: use `inet:ntoa/1' to convert `ip()' to string.
+%%% TIP: use {@link inet:ntoa/1} and {@link inet:parse_address/1} to convert
+%%% between {@link ip()} and `string()'.
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/10/static/datatype-net-types.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/network.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 -module(epgsql_codec_net).

+ 1 - 0
src/datatypes/epgsql_codec_noop.erl

@@ -1,3 +1,4 @@
+%%% @private
 %%% @doc
 %%% Dummy codec. Used internally
 %%% @end

+ 9 - 2
src/datatypes/epgsql_codec_postgis.erl

@@ -1,7 +1,14 @@
 %%% @doc
 %%% Codec for `geometry' PostGIS umbrella datatype.
-%%% http://postgis.net/docs/manual-2.4/geometry.html
-%%% $POSTGIS$/postgis/lwgeom_inout.c
+%%%
+%%% XXX: PostGIS is not a Postgres's built-in datatype! It should be instaled
+%%% separately and enabled via `CREATE EXTENSION postgis'.
+%%% <ul>
+%%%  <li>[http://postgis.net/docs/manual-2.4/geometry.html]</li>
+%%%  <li>$POSTGIS$/postgis/lwgeom_inout.c</li>
+%%% </ul>
+%%% @end
+%%% @see ewkb
 %%% @end
 %%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 13 - 4
src/datatypes/epgsql_codec_text.erl

@@ -1,9 +1,18 @@
 %%% @doc
 %%% Codec for `text', `varchar', `bytea'.
-%%% For 'char' see epgsql_codec_bpchar.erl.
-%%% https://www.postgresql.org/docs/10/static/datatype-character.html
-%%% $PG$/src/backend/utils/adt/varchar.c
-%%% $PG$/src/backend/utils/adt/varlena.c
+%%%
+%%% If input for `text' or `varchar' is provided as a list, not binary, and it
+%%% contains not just `byte()', an attempt to perform unicode conversion will be made.
+%%%
+%%% Also, `integer()', `float()' and `atom()' are automatically converted to
+%%% strings, but this kind of conversion might be eventualy removed.
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/10/static/datatype-character.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/varchar.c</li>
+%%%  <li>$PG$/src/backend/utils/adt/varlena.c</li>
+%%% </ul>
+%%% @end
+%%% @see epgsql_codec_bpchar. epgsql_codec_bpchar - for 'char' and 'char(N)'
 %%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 7 - 2
src/datatypes/epgsql_codec_timerange.erl

@@ -1,7 +1,12 @@
 %%% @doc
 %%% Codec for `tsrange', `tstzrange', `daterange' types.
-%%% https://www.postgresql.org/docs/current/static/rangetypes.html#rangetypes-builtin
-%%% $PG$/src/backend/utils/adt/rangetypes.c
+%%%
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/rangetypes.html#rangetypes-builtin]</li>
+%%%  <li>$PG$/src/backend/utils/adt/rangetypes.c</li>
+%%% </ul>
+%%% @end
+%%% @see epgsql_codec_datetime
 %%% @end
 %%% Created : 16 Jul 2018 by Vladimir Sekissov <eryx67@gmail.com>
 %%% TODO: universal range, based on pg_range table

+ 6 - 3
src/datatypes/epgsql_codec_uuid.erl

@@ -1,9 +1,12 @@
 %%% @doc
 %%% Codec for `uuid' type.
-%%% Input expected to be in hex string, eg
+%%%
+%%% Input is expected to be in hex `string()' / `binary()', eg
 %%% `<<"550e8400-e29b-41d4-a716-446655440000">>'.
-%%% https://www.postgresql.org/docs/current/static/datatype-uuid.html
-%%% $PG$/src/backend/utils/adt/uuid.c
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/datatype-uuid.html]</li>
+%%%  <li>$PG$/src/backend/utils/adt/uuid.c</li>
+%%% </ul>
 %%% @end
 %%% Created : 14 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 

+ 45 - 34
src/epgsql.erl

@@ -1,3 +1,7 @@
+%%% @doc Synchronous interface.
+%%%
+%%% All functions block (with infinite timeout) until full result is available.
+%%% @end
 %%% Copyright (C) 2008 - Will Glozer.  All rights reserved.
 %%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
 
@@ -42,7 +46,7 @@
 
 -include("epgsql.hrl").
 
--type sql_query() :: iodata().
+-type sql_query() :: iodata(). % SQL query text
 -type host() :: inet:ip_address() | inet:hostname().
 -type password() :: string() | iodata() | fun( () -> iodata() ).
 -type connection() :: pid().
@@ -76,7 +80,7 @@
           replication => string()}.
 
 -type connect_error() :: epgsql_cmd_connect:connect_error().
--type query_error() :: #error{}.
+-type query_error() :: #error{}.              % Error report generated by server
 
 
 -type type_name() :: atom().
@@ -90,15 +94,15 @@
 -type pg_datetime() :: epgsql_codec_datetime:pg_datetime().
 -type pg_interval() :: epgsql_codec_datetime:pg_interval().
 
-%% Deprecated
 -type bind_param() :: any().
+%% Value to be bound to placeholder (`$1', `$2' etc)
 
 -type typed_param() :: {epgsql_type(), bind_param()}.
 
 -type column() :: #column{}.
 -type statement() :: #statement{}.
 -type squery_row() :: tuple(). % tuple of binary().
--type equery_row() :: tuple(). % tuple of bind_param().
+-type equery_row() :: tuple(). % tuple of any().
 -type ok_reply(RowType) ::
         %% select
     {ok, ColumnsDescription :: [column()], RowsValues :: [RowType]} |
@@ -126,6 +130,7 @@
 %% -------------
 
 %% -- client interface --
+%% @doc connects to the server and performs all the necessary handshakes
 -spec connect(connect_opts())
         -> {ok, Connection :: connection()} | {error, Reason :: connect_error()}.
 connect(Opts) ->
@@ -140,13 +145,13 @@ connect(Host, Username, Opts) ->
 
 -spec connect(host(), string(), password(), connect_opts())
         -> {ok, Connection :: connection()} | {error, Reason :: connect_error()}.
-%% @doc connects to Postgres
-%% where
-%% `Host'     - host to connect to
-%% `Username' - username to connect as, defaults to `$USER'
-%% `Password' - optional password to authenticate with
-%% `Opts'     - proplist or map of extra options
-%% returns `{ok, Connection}' otherwise `{error, Reason}'
+%% @doc connects to the server and performs all the necessary handshakes (legacy interface)
+%% @param Host     host to connect to
+%% @param Username username to connect as, defaults to `$USER'
+%% @param Password optional password to authenticate with
+%% @param Opts     proplist or map of extra options
+%% @returns `{ok, Connection}' otherwise `{error, Reason}'
+%% @see connect/1
 connect(Host, Username, Password, Opts) ->
     {ok, C} = epgsql_sock:start_link(),
     connect(C, Host, Username, Password, Opts).
@@ -216,10 +221,10 @@ get_parameter(C, Name) ->
 set_notice_receiver(C, PidOrName) ->
     epgsql_sock:set_notice_receiver(C, PidOrName).
 
-%% @doc Returns last command status message
-%% If multiple queries were executed using `squery/2', separated by semicolon,
+%% @doc Returns last command status message.
+%% If multiple queries were executed using {@link squery/2}, separated by semicolon,
 %% only the last query's status will be available.
-%% See https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQCMDSTATUS
+%% See [https://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQCMDSTATUS]
 -spec get_cmd_status(connection()) -> {ok, Status}
                                           when
       Status :: undefined | atom() | {atom(), integer()}.
@@ -228,24 +233,23 @@ get_cmd_status(C) ->
 
 -spec squery(connection(), sql_query()) -> epgsql_cmd_squery:response().
 %% @doc runs simple `SqlQuery' via given `Connection'
+%% @see epgsql_cmd_squery
 squery(Connection, SqlQuery) ->
     epgsql_sock:sync_command(Connection, epgsql_cmd_squery, SqlQuery).
 
 equery(C, Sql) ->
     equery(C, Sql, []).
 
-%% TODO add fast_equery command that doesn't need parsed statement
 -spec equery(connection(), sql_query(), [bind_param()]) ->
                     epgsql_cmd_equery:response().
 equery(C, Sql, Parameters) ->
-    case parse(C, "", Sql, []) of
-        {ok, #statement{types = Types} = S} ->
-            TypedParameters = lists:zip(Types, Parameters),
-            epgsql_sock:sync_command(C, epgsql_cmd_equery, {S, TypedParameters});
-        Error ->
-            Error
-    end.
+    equery(C, "", Sql, Parameters).
 
+%% @doc Executes extended query
+%% @end
+%% @see epgsql_cmd_equery
+%% @end
+%% TODO add fast_equery command that doesn't need parsed statement
 -spec equery(connection(), string(), sql_query(), [bind_param()]) ->
                     epgsql_cmd_equery:response().
 equery(C, Name, Sql, Parameters) ->
@@ -257,6 +261,8 @@ equery(C, Name, Sql, Parameters) ->
             Error
     end.
 
+%% @doc Similar to {@link equery/3}, but uses prepared statement that can be reused multiple times.
+%% @see epgsql_cmd_prepared_query
 -spec prepared_query(C::connection(), string() | statement(), Parameters::[bind_param()]) ->
                             epgsql_cmd_prepared_query:response().
 prepared_query(C, #statement{types = Types} = S, Parameters) ->
@@ -312,11 +318,15 @@ execute(C, S, N) ->
 execute(C, S, PortalName, N) ->
     epgsql_sock:sync_command(C, epgsql_cmd_execute, {S, PortalName, N}).
 
+%% @doc Executes batch of `{statement(), [bind_param()]}' extended queries
+%% @see epgsql_cmd_batch
 -spec execute_batch(connection(), [{statement(), [bind_param()]}]) ->
                            epgsql_cmd_batch:response().
 execute_batch(C, Batch) ->
     epgsql_sock:sync_command(C, epgsql_cmd_batch, Batch).
 
+%% @doc Executes same statement() extended query with each parameter list of a `Batch'
+%% @see epgsql_cmd_batch
 -spec execute_batch(connection(), statement() | sql_query(), [ [bind_param()] ]) ->
                            {[column()], epgsql_cmd_batch:response()}.
 execute_batch(C, #statement{columns = Cols} = Statement, Batch) ->
@@ -352,6 +362,7 @@ describe(C, portal, Name) ->
 close(C, #statement{name = Name}) ->
     close(C, statement, Name).
 
+%% @doc close statement or portal
 -spec close(connection(), statement | portal, iodata()) -> epgsql_cmd_close:response().
 close(C, Type, Name) ->
     epgsql_sock:sync_command(C, epgsql_cmd_close, {Type, Name}).
@@ -360,6 +371,7 @@ close(C, Type, Name) ->
 sync(C) ->
     epgsql_sock:sync_command(C, epgsql_cmd_sync, []).
 
+%% @doc cancel currently executing command
 -spec cancel(connection()) -> ok.
 cancel(C) ->
     epgsql_sock:cancel(C).
@@ -448,18 +460,17 @@ handle_x_log_data(Mod, StartLSN, EndLSN, WALRecord, Repl) ->
     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
-%% `ReplicationSlot' - the name of the replication slot to stream changes from
-%% `Callback'        - Callback module which should have the callback functions implemented for message processing.
-%%                      or a process which should be able to receive replication messages.
-%% `CbInitState'     - Callback Module's initial state
-%% `WALPosition'     - the WAL position XXX/XXX to begin streaming at.
-%%                      "0/0" to let the server determine the start point.
-%% `PluginOpts'      - optional options passed to the slot's logical decoding plugin.
-%%                      For example: "option_name1 'value1', option_name2 'value2'"
-%% `Opts'            - options of logical replication
-%% returns `ok' otherwise `{error, Reason}'
+%% @param Connection      connection in replication mode
+%% @param ReplicationSlot the name of the replication slot to stream changes from
+%% @param Callback        Callback module which should have the callback functions implemented for message processing.
+%%                        or a process which should be able to receive replication messages.
+%% @param CbInitState     Callback Module's initial state
+%% @param WALPosition     the WAL position XXX/XXX to begin streaming at.
+%%                        "0/0" to let the server determine the start point.
+%% @param PluginOpts      optional options passed to the slot's logical decoding plugin.
+%%                        For example: "option_name1 'value1', option_name2 'value2'"
+%% @param Opts            options of logical replication
+%% @returns `ok' otherwise `{error, Reason}'
 start_replication(Connection, ReplicationSlot, Callback, CbInitState, WALPosition, PluginOpts, Opts) ->
     Command = {ReplicationSlot, Callback, CbInitState, WALPosition, PluginOpts, to_map(Opts)},
     epgsql_sock:sync_command(Connection, epgsql_cmd_start_replication, Command).

+ 7 - 1
src/epgsql_binary.erl

@@ -1,5 +1,11 @@
+%%% @doc
+%%% Interface to encoder/decoder for binary postgres data representation
+%%% @end
+%%% @see epgsql_codec
+%%% @see epgsql_wire
+%%% @end
 %%% Copyright (C) 2008 - Will Glozer.  All rights reserved.
-%% XXX: maybe merge this module into epgsql_codec?
+%%% XXX: maybe merge this module into epgsql_codec?
 -module(epgsql_binary).
 
 -export([new_codec/2,

+ 3 - 0
src/epgsql_codec.erl

@@ -1,9 +1,12 @@
 %%% @doc
 %%% Behaviour for postgresql datatype codecs.
+%%%
 %%% XXX: this module and callbacks "know nothing" about OIDs.
 %%% XXX: state of codec shouldn't leave epgsql_sock process. If you need to
 %%% return "pointer" to data type/codec, it's better to return OID or type name.
 %%% @end
+%%% @see epgsql_binary
+%%% @end
 %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
 
 -module(epgsql_codec).

+ 4 - 2
src/epgsql_command.erl

@@ -1,5 +1,5 @@
-%%% Behaviour module for epgsql_sock commands.
-%%%
+%%% @doc Behaviour module for epgsql_sock commands.
+%%% @end
 %%% Copyright (C) 2017 - Sergey Prokhorov.  All rights reserved.
 
 -module(epgsql_command).
@@ -16,6 +16,7 @@
 -type execute_return() ::
         {ok, epgsql_sock:pg_sock(), state()}
       | {stop, Reason :: any(), Response :: any(), epgsql_sock:pg_sock()}.
+
 %% Execute command. It should send commands to socket.
 %% May be called many times if 'handle_message' will return 'requeue'.
 -callback execute(epgsql_sock:pg_sock(), state()) -> execute_return().
@@ -48,6 +49,7 @@
         %% Unknown packet. Terminate `epgsql_sock' process
       | unknown.
 %% Handle incoming packet
+
 -callback handle_message(Type :: byte(), Payload :: binary() | epgsql:query_error(),
                          epgsql_sock:pg_sock(), state()) -> handle_message_return().
 

+ 3 - 3
src/epgsql_oid_db.erl

@@ -1,7 +1,7 @@
 %%% @author Sergey Prokhorov <me@seriyps.ru>
 %%% @doc
-%%% Holds Oid <-> Type mappings (forward and reverse).
-%%% See https://www.postgresql.org/docs/current/static/catalog-pg-type.html
+%%% Holds Oid to Type mappings (forward and reverse).
+%%% See [https://www.postgresql.org/docs/current/static/catalog-pg-type.html].
 %%% @end
 
 -module(epgsql_oid_db).
@@ -36,7 +36,7 @@
 %% pg_type Data preparation
 %%
 
-%% @doc build query to fetch OID<->type_name information from PG server
+%% @doc build query to fetch OID to type_name information from PG server
 -spec build_query([epgsql:type_name() | binary()]) -> iolist().
 build_query(TypeNames) ->
     %% TODO: lists:join/2, ERL 19+

+ 7 - 5
src/epgsql_scram.erl

@@ -1,11 +1,13 @@
 %%% coding: utf-8
 %%% @doc
 %%% SCRAM--SHA-256 helper functions
-%%% See
-%%% https://www.postgresql.org/docs/current/static/sasl-authentication.html
-%%% https://en.wikipedia.org/wiki/Salted_Challenge_Response_Authentication_Mechanism
-%%% https://tools.ietf.org/html/rfc7677
-%%% https://tools.ietf.org/html/rfc5802
+%%%
+%%% <ul>
+%%%  <li>[https://www.postgresql.org/docs/current/static/sasl-authentication.html]</li>
+%%%  <li>[https://en.wikipedia.org/wiki/Salted_Challenge_Response_Authentication_Mechanism]</li>
+%%%  <li>[https://tools.ietf.org/html/rfc7677]</li>
+%%%  <li>[https://tools.ietf.org/html/rfc5802]</li>
+%%% </ul>
 %%% @end
 
 -module(epgsql_scram).

+ 5 - 3
src/epgsql_sock.erl

@@ -3,12 +3,14 @@
 
 %%% @doc GenServer holding all connection state (including socket).
 %%%
-%%% See https://www.postgresql.org/docs/current/static/protocol-flow.html
+%%% See [https://www.postgresql.org/docs/current/static/protocol-flow.html]
+%%%
 %%% Commands in PostgreSQL are pipelined: you don't need to wait for reply to
 %%% be able to send next command.
 %%% Commands are processed (and responses to them are generated) in FIFO order.
 %%% eg, if you execute 2 SimpleQuery: #1 and #2, first you get all response
 %%% packets for #1 and then all for #2:
+%%% ```
 %%% > SQuery #1
 %%% > SQuery #2
 %%% < RowDescription #1
@@ -17,8 +19,8 @@
 %%% < RowDescription #2
 %%% < DataRow #2
 %%% < CommandComplete #2
-%%%
-%%% See epgsql_cmd_connect for network connection and authentication setup
+%%% '''
+%%% @see epgsql_cmd_connect. epgsql_cmd_connect for network connection and authentication setup
 
 
 -module(epgsql_sock).

+ 9 - 1
src/epgsql_wire.erl

@@ -1,3 +1,9 @@
+%%% @doc
+%%% Interface to encoder/decoder for postgresql
+%%% <a href="https://www.postgresql.org/docs/current/protocol-message-formats.html">wire-protocol</a>
+%%%
+%%% See also `include/protocol.hrl'.
+%%% @end
 %%% Copyright (C) 2009 - Will Glozer.  All rights reserved.
 %%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
 
@@ -25,6 +31,7 @@
 
 -opaque row_decoder() :: {[epgsql_binary:decoder()], [epgsql:column()], epgsql_binary:codec()}.
 
+%% @doc tries to extract single postgresql packet from TCP stream
 -spec decode_message(binary()) -> {byte(), binary(), binary()} | binary().
 decode_message(<<Type:8, Len:?int32, Rest/binary>> = Bin) ->
     Len2 = Len - 4,
@@ -65,7 +72,7 @@ decode_fields(<<Type:8, Rest/binary>>, Acc) ->
     decode_fields(Rest2, [{Type, Str} | Acc]).
 
 %% @doc decode ErrorResponse
-%% See http://www.postgresql.org/docs/current/interactive/protocol-error-fields.html
+%% See [http://www.postgresql.org/docs/current/interactive/protocol-error-fields.html]
 -spec decode_error(binary()) -> epgsql:query_error().
 decode_error(Bin) ->
     Fields = decode_fields(Bin),
@@ -219,6 +226,7 @@ encode_formats([], Count, Acc) ->
 encode_formats([#column{format = Format} | T], Count, Acc) ->
     encode_formats(T, Count + 1, <<Acc/binary, Format:?int16>>).
 
+%% @doc Returns 1 if Codec knows how to decode binary format of the type provided and 0 otherwise
 format({unknown_oid, _}, _) -> 0;
 format(#column{oid = Oid}, Codec) ->
     case epgsql_binary:supports(Oid, Codec) of

+ 7 - 0
src/epgsqla.erl

@@ -1,3 +1,10 @@
+%%% @doc
+%%% Asynchronous interface.
+%%%
+%%% All the functions return `reference()' immediately. Results are delivered
+%%% asynchronously in a form of `{connection(), reference(), Result}', where
+%%% `Result' is what synchronous version of this function normally returns.
+%%% @end
 %%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
 
 -module(epgsqla).

+ 7 - 0
src/epgsqli.erl

@@ -1,3 +1,10 @@
+%%% @doc Incremental interface
+%%%
+%%% All the functions return `reference()' immediately. Each data row as well
+%%% as metadata are delivered as separate messages in a form of
+%%% `{connection(), reference(), Payload}' where `Payload' depends on command
+%%% being executed.
+%%% @end
 %%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
 
 -module(epgsqli).

+ 7 - 2
src/ewkb.erl

@@ -1,5 +1,10 @@
-%% https://en.wikipedia.org/wiki/Well-known_text
-%% http://postgis.net/docs/manual-2.4/using_postgis_dbmanagement.html#EWKB_EWKT
+%% @doc
+%% Encoder/decoder for PostGIS binary data representation.
+%%
+%% <ul>
+%%  <li>[https://en.wikipedia.org/wiki/Well-known_text]</li>
+%%  <li>[http://postgis.net/docs/manual-2.4/using_postgis_dbmanagement.html#EWKB_EWKT]</li>
+%% </ul>
 -module(ewkb).
 -export([decode_geometry/1, encode_geometry/1]).
 -export_type([point_type/0, point/1, multi_point/1, line_string/1,