Browse Source

Rewrite overview.edoc as internal, not end-user documentation

Sergey Prokhorov 5 years ago
parent
commit
5edd3c96e0
4 changed files with 74 additions and 67 deletions
  1. 4 0
      Makefile
  2. 38 52
      doc/overview.edoc
  3. 11 6
      src/epgsql.erl
  4. 21 9
      src/epgsql_sock.erl

+ 4 - 0
Makefile

@@ -12,6 +12,10 @@ compile: src/epgsql_errcodes.erl $(REBAR)
 
 clean: $(REBAR)
 	@$(REBAR) clean
+	@rm -f doc/*.html
+	@rm -f doc/erlang.png
+	@rm -f doc/stylesheet.css
+	@rm -f doc/edoc-info
 
 src/epgsql_errcodes.erl:
 	./generate_errcodes_src.sh > src/epgsql_errcodes.erl

+ 38 - 52
doc/overview.edoc

@@ -1,8 +1,13 @@
-
-@title epgsql - PostgreSQL driver for Erlang
+@title epgsql - PostgreSQL driver for Erlang, internal documentation
 @doc
+This document is made mostly as internal documentation. It can be useful
+if you plan to contribute some patches to epgsql, want to implement
+custom datatypes or commands or to better understand epgsql internals.
+
+End-user interface is described in <a href="https://github.com/epgsql/epgsql#readme">README.md</a>.
+
 == Interfaces ==
-Epgsql has 3 API interfaces:
+Epgsql has 3 end-user API interfaces:
 
 <ul>
   <li>{@link epgsql} - synchronous</li>
@@ -10,62 +15,43 @@ Epgsql has 3 API interfaces:
   <li>{@link epgsqli} - incremental</li>
 </ul>
 
-They have the same set of functions, but differ by the way they deliver results.
+== Internals ==
 
-`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.
+All 3 interfaces communicate with {@link epgsql_sock} gen_server, which holds all
+the connection state. While `epgsql_sock' holds all the state, it doesn't know
+much about Client-Server communication protocol.
+All the communication logic between epgsql and PostgreSQL server is implemented
+as a {@section Commands} and `epgsql_sock' acts as an executor for those commands.
 
-It's usualy not a problem to use different interfaces with the same connection.
-
-== Example session ==
+PostgreSQL binary communication protocol is represented by 2 modules:
+<ul>
+  <li>{@link epgsql_wire} - codecs for on-wire communication protocol messages</li>
+  <li>{@link epgsql_binary} - interface to PostgreSQL binary data encoding protocol(see {@section Datatypes})</li>
+</ul>
 
-```
-{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]),
+`epgsql_sock' holds an internal state of `epgsql_binary' codecs as well. The
+main contents of this state is the mapping between PostgreSQL unique numeric
+datatype IDs (OIDs) and callbacks which will be used to decode this datatype.
+This mapping is handled by {@link epgsql_oid_db} module and is populated at
+connection set-up time by {@link epgsql_cmd_connect}.
 
-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).
-'''
+Most of the connection initialization (network connection, authentication, codecs)
+is performed by {@link epgsql_cmd_connect} command, wich is just a regualr command
+(but quite complex one) and can be replaced by own implementation if needed.
 
 == Commands ==
 
-Client can execute a number of built-in commands as well as define
-their own. See {@link epgsql_command}.
+Client can execute a number of built-in commands as well as define their own.
+See {@link epgsql_command} and all the `epgsql_cmd_*' pages.
+There exists a <a href="pluggable_commands.md">manual</a> that explains how to
+implement your own 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}.
+Epgsql supports both PostgreSQL <a href="https://www.postgresql.org/docs/current/protocol-overview.html#PROTOCOL-FORMAT-CODES">text and binary</a>
+data encodings to transfer the data (query placeholder parameters and result rows).
+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} and all the `epgsql_codec_*' pages for more details.
+There exists a <a href="pluggable_types.md">manual</a> that explains how to
+implement your own datatype codec.

+ 11 - 6
src/epgsql.erl

@@ -387,15 +387,20 @@ with_transaction(C, F) ->
 %% @doc Execute callback function with connection in a transaction.
 %% Transaction will be rolled back in case of exception.
 %% Options (proplist or map):
-%% - reraise (true): when set to true, exception will be re-thrown, otherwise
-%%   {rollback, ErrorReason} will be returned
-%% - ensure_comitted (false): even when callback returns without exception,
+%% <dl>
+%%  <dt>reraise</dt>
+%%  <dd>when set to true, exception will be re-thrown, otherwise
+%%   `{rollback, ErrorReason}' will be returned. Default: `true'</dd>
+%%  <dt>ensure_comitted</dt>
+%%  <dd>even when callback returns without exception,
 %%   check that transaction was comitted by checking CommandComplete status
 %%   of "COMMIT" command. In case when transaction was rolled back, status will be
-%%   "rollback" instead of "commit".
-%% - begin_opts (""): append extra options to "BEGIN" command (see
+%%   "rollback" instead of "commit". Default: `false'</dd>
+%%  <dt>begin_opts</dt>
+%%  <dd>append extra options to "BEGIN" command (see
 %%   https://www.postgresql.org/docs/current/static/sql-begin.html)
-%%   Beware of SQL injections! No escaping is made on begin_opts!
+%%   Beware of SQL injections! No escaping is made on begin_opts! Default: `""'</dd>
+%% </dl>
 -spec with_transaction(
         connection(), fun((connection()) -> Reply), Opts) -> Reply | {rollback, any()} | no_return() when
       Reply :: any(),

+ 21 - 9
src/epgsql_sock.erl

@@ -1,12 +1,9 @@
-%%% Copyright (C) 2009 - Will Glozer.  All rights reserved.
-%%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
-
-%%% @doc GenServer holding all connection state (including socket).
+%%% @doc GenServer holding all the connection state (including socket).
 %%%
 %%% 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 in PostgreSQL protocol are pipelined: you don't have 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:
@@ -14,14 +11,29 @@
 %%% > SQuery #1
 %%% > SQuery #2
 %%% < RowDescription #1
-%%% < DataRow #1
+%%% < DataRow #1.1
+%%% < ...
+%%% < DataRow #1.N
 %%% < CommandComplete #1
 %%% < RowDescription #2
-%%% < DataRow #2
+%%% < DataRow #2.1
+%%% < ...
+%%% < DataRow #2.N
 %%% < CommandComplete #2
 %%% '''
+%%% `epgsql_sock' is capable of utilizing the pipelining feature - as soon as
+%%% it receives a new command, it sends it to the server immediately and then
+%%% it puts command's callbacks and state into internal queue of all the commands
+%%% which were sent to the server and waiting for response. So it knows in which
+%%% order it should call each pipelined command's `handle_message' callback.
+%%% But it can be easily broken if high-level command is poorly implemented or
+%%% some conflicting low-level commands (such as `parse', `bind', `execute') are
+%%% executed in a wrong order. In this case server and epgsql states become out of
+%%% sync and {@link epgsql_cmd_sync} have to be executed in order to recover.
 %%% @see epgsql_cmd_connect. epgsql_cmd_connect for network connection and authentication setup
-
+%%% @end
+%%% Copyright (C) 2009 - Will Glozer.  All rights reserved.
+%%% Copyright (C) 2011 - Anton Lebedevich.  All rights reserved.
 
 -module(epgsql_sock).