Subversion Repositories SE.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
50 7u83 1
# Pluggable commands
2
 
3
Starting from epgsql 4.0.0 it's possible to create custom epgsql commands. The term "command"
4
signifies a single `request -> response` sequence.
5
Under the hood it might contain many PostgreSQL protocol command requests and responses,
6
but from the point of view of an epgsql user, it's a single request that produces a single
7
response.
8
Examples of such commands are `connect`, `squery`, `equery`, `prepared_query`,
9
`parse`/`bind`/`execute` and so on. See [src/commands](src/commands) for a full list and
10
examples. Basically, almost all epgsql end-user APIs are implemented as a commands.
11
 
12
It is possible to send many commands without waiting for the results of previously sent ones
13
(pipelining) by using `epgsqla` or `epgsqli` interfaces.
14
 
15
## Possible usecases
16
 
17
Why you may want to implement a custom command? Some ideas:
18
 
19
* You are not satisfied by performance or functionality of epgsql's built-in commands
20
* To create a version of equery with built-in statement cache
21
* To create a single-roundtrip equery (currently equery works by combining `parse` and
22
  `equery` commands)
23
* To construct some tricky batching commands, eg, bulk-inserts
24
 
25
## This can be done by following steps
26
 
27
If you are not familiar with the PostgreSQL wire protocol, please, read at least the
28
[Message Flow](https://www.postgresql.org/docs/current/static/protocol-flow.html) and
29
[Message Formats](https://www.postgresql.org/docs/current/static/protocol-message-formats.html)
30
sections of the PostgreSQL documentation.
31
The entire [Frontend/Backend Protocol](https://www.postgresql.org/docs/current/static/protocol.html)
32
would be nice to know.
33
 
34
### Implement epgsql_command behaviour callback module
35
 
36
See [epgsql_command](src/epgsql_command.erl).
37
 
38
This module should have the following functions exported:
39
 
40
```erlang
41
init(any()) -> state().
42
```
43
 
44
Called only once when the command is received and is about to be executed by the epgsql connection
45
process. Command's arguments are passed as the callback's arguments, see `epgsql_sock:sync_command/3` and
46
`epgsql_sock:async_command/4`. Should initialize and return command's state that will be
47
passed to all subsequent callbacks. No PostgreSQL interactions should be done here.
48
 
49
```erlang
50
execute(pg_sock(), state()) ->
51
    {ok, pg_sock(), state()}
52
  | {stop, Reason :: any(), Response :: any(), pg_sock()}.
53
 
54
```
55
 
56
Client -> Server packets should be sent from this callback by `epgsql_sock:send_multi/2` or
57
`epgsql_sock:send/3`. `epgsql_wire` module is usually used to create wire protocol packets.
58
Please note that many packets might be sent at once. See `epgsql_cmd_equery` as an example.
59
 
60
This callback might be executed more than once for a single command execution if your command
61
requires a response for some of the packets to send next packet (more than one round-trip).
62
Since epgsql is asynchronous under the hood, you can't just do blocking `receive`.
63
See `handle_message/4 -> {requeue, ...}` and `epgsql_cmd_connect` as an example.
64
 
65
`pg_sock()` is an opaque state of a `epgsql_sock` process. There are some APIs to get or
66
set some fields on it in `epgsql_sock` module.
67
 
68
```erlang
69
handle_message(Type :: byte(), Payload :: binary() | query_error(),
70
               pg_sock(), state()) ->
71
    {noaction, pg_sock()}
72
  | {noaction, pg_sock(), state()}
73
  | {add_row, tuple(), pg_sock(), state()}
74
  | {add_result, Data :: any(), Notification :: any(), pg_sock(), state()}
75
  | {finish, Result :: any(), Notification :: any(), pg_sock()}
76
  | {requeue, pg_sock(), state()}
77
  | {stop, Reason :: any(), Response :: any(), pg_sock()}
78
  | {sync_required, Why :: any()}
79
  | unknown.
80
 
81
```
82
 
83
Server -> Client packet handling code. Packet `Type` byte is the integer ID of a
84
[protocol packet](https://www.postgresql.org/docs/current/static/protocol-message-formats.html), basically
85
the 1st byte of a packet. And `Payload` is the remaining bytes of a packet. `epgsql_wire` module
86
has some helpers that might help decode the packet payload.
87
 
88
In the case when the epgsql connection gets an error packet from the server, it will be decoded and `Payload`
89
will be `query_error()` instead of binary.
90
 
91
**NEVER** call `epgsql_sock:send/3`/`epgsql_sock:send_multi/2` from this callback! Use
92
`requeue` return instead: otherwise you will break pipelining!
93
 
94
This callback should return one of the following responses to control command's behaviour:
95
 
96
- `{noaction, pg_sock()}` - to do nothing (this usualy means that packet was ignored)
97
- `{noaction, pg_sock(), state()}` - do nothing, but update command's state
98
- `{add_row, tuple(), pg_sock(), state()}` - add a row to current resultset rows accumulator.
99
  You may get the current accumulated resultset by `epgsql_sock::get_rows(pg_sock())` (except
100
  when `epgsqli` interface is used).
101
- `{add_result, Result :: any(), Notification :: any(), pg_sock(), state()}` - add a
102
  new result to the list of results. Usualy all commands have only a single result, except `squery`, when
103
  multiple SQL queries were passed, separated by a semicolon and `execute_batch`.
104
  You will usually will just return something like `{ok, epgsql_sock:get_rows(PgSock)}` or an error as a result. `Notification` is used for `epgsqli` interface.
105
  You may get the current list of accumulated results with `epgsql_sock:get_results(pg_sock())`.
106
- `{finish, Results, Notification, pg_sock(), state()}` - returned when command was successfuly
107
  executed and no more actions needed. `Results` will be returned to a client as a result of command
108
  execution and the command will be descheduled from epgsql connection process.
109
  You usually use the result of `epgsql_sock:get_results/1` as a `Results`.
110
  `Notification` is used for `epgsqli` interface.
111
- `{requeue, pg_sock(), state()}` - asks the epgsql process to put this command in the execution queue
112
  once again (with a new state). This means that the `execute/2` callback will be executed again and
113
  new packets may be sent from client to server. This way you can implement chatty commands with
114
  multiple `request -> response` sequences. See `epgsql_cmd_connect` as an example.
115
- `{stop, Reason, Response, pg_sock()}` - returned when some unrecoverable error occured and
116
  you want to terminate epgsql connection process. `Response` will be returned as a command result
117
  and `Reason` will be process termination reason.
118
  Please, try to avoid use of this response if possible.
119
- `{sync_required, Why}` - returned to finish command execution, flush enqueued but not yet
120
  executed commands and to set epgsql process to `sync_required` state. In this state it
121
  will not accept any commands except `epgsql_cmd_sync`.
122
  This usualy means that multipacket protocol sequence was done out-of-order (eg, `bind` before `parse`),
123
  so, client and server states are out-of-sync and we need to reset them.
124
- `unknown` - command got unexpected packet. Connection process will be terminated with
125
  `{error, {unexpected_message, Type, Payload, state()}}`. Usualy returned from a
126
  catch-all last clause.
127
 
128
### Command now can be executed
129
 
130
By calling
131
 
132
- `epgsql_sock:sync_command(connection(), command(), Args :: any())` for a
133
  `gen_server:call`-style, synchronous behaviour (`epgsql`-like API)
134
- `epgsql_sock:async_command(connection(), cast, command(), Args :: any())` for asynchronous
135
  behaviour when whole resultset will be delivered as a single erlang message (`epgsqla`-like API)
136
- `epgsql_sock:async_command(connection(), incremental, command(), Args :: any())` for
137
  asynchronous behaviour when **each row** and some status info will be delivered as separate erlang
138
  messages (`epgsqli`-like API)
139
 
140
`command()` is the name of a module, implementing `epgsql_command` behaviour.
141
`Args` may be any (eg, SQL query / arguments / options), they will be passed to `init/1` callback as-is.
142
 
143
## Tips
144
 
145
* If you are implementing your command outside of a epgsql main tree, it might be handy to
146
  add `do(Conn, Arg1, Arg2...) -> epgsql_sock:sync_command(Conn, ?MODULE, Args).` to
147
  incapsulate `epgsql_sock` calls and provide end-user API.
148
* Don't be afraid of `requeue`. It might make your code more complex, but will make it possible to
149
  implement complex multistep logic inside of a single command
150
* `epgsql_sock` module has some APIs that might be used from within commands. Refer to that module's
151
  source code. `epgsql_wire` has some helpers to encode/decode wire protocol and data packets.
152
* Packet IDs are defined in `include/protocol.hrl`
153
* Again, never try to send packets from `handle_message/4` or `init/1` callbacks!
154
* Note that any error in callback functions will crash the epgsql connection process!