will / epgsql (http://glozer.net/code.html#epgsql)

Erlang PostgreSQL client library.

Clone this repository (size: 53.2 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/will/epgsql/
commit 15: 09c9e8eb2bf1
parent 14: 6b24797fff01
branch: default
add support for timing out long-running operations
Will Glozer / will
11 months ago

Changed (Δ4.8 KB):

raw changeset »

README (5 lines added, 1 lines removed)

src/pgsql.erl (15 lines added, 16 lines removed)

src/pgsql_connection.erl (121 lines added, 33 lines removed)

src/pgsql_sock.erl (13 lines added, 1 lines removed)

test_src/pgsql_tests.erl (28 lines added, 0 lines removed)

Up to file-list README:

@@ -2,7 +2,7 @@ Erlang PostgreSQL Database Client
2
2
3
3
* Connect
4
4
5
  {ok, C} = pgsql:connect(Host, [Username], [Password], [Opts]).
5
  {ok, C} = pgsql:connect(Host, [Username], [Password], Opts).
6
6
7
7
  Host      - host to connect to.
8
8
  Username  - username to connect as, defaults to $USER.
@@ -13,10 +13,14 @@ Erlang PostgreSQL Database Client
13
13
    + port
14
14
    + ssl (true | false | required)
15
15
    + ssl_opts (see ssl docs in OTP)
16
    + timeout (milliseconds, defaults to 5000)
16
17
17
18
  {ok, C} = pgsql:connect("localhost", "username", [{database, "test_db"}]).
18
19
  ok = pgsql:close(C).
19
20
21
  The timeout parameter is applied to all operations. In the case of equery
22
  this means that total execution time may exceed the timeout value.
23
20
24
* Simple Query
21
25
22
26
  {ok, Columns, Rows} = pgsql:squery(C, Sql).

Up to file-list src/pgsql.erl:

1
1
%%% Copyright (C) 2008 - Will Glozer.  All rights reserved.
2
3
2
-module(pgsql).
4
3
5
4
-export([connect/2, connect/3, connect/4, close/1]).
11
10
12
11
-include("pgsql.hrl").
13
12
14
-define(timeout, 5000).
15
16
13
%% -- client interface --
17
14
18
15
connect(Host, Opts) ->
@@ -47,7 +44,7 @@ equery(C, Sql, Parameters) ->
47
44
        {ok, #statement{types = Types} = S} ->
48
45
            Typed_Parameters = lists:zip(Types, Parameters),
49
46
            ok = pgsql_connection:equery(C, S, Typed_Parameters),
50
            receive_result(C);
47
            receive_result(C, undefined);
51
48
        Error ->
52
49
            Error
53
50
    end.
@@ -114,16 +111,18 @@ with_transaction(C, F) ->
114
111
115
112
%% -- internal functions --
116
113
117
receive_result(C) ->
118
    R = receive_result(C, [], []),
119
    receive
120
        {pgsql, C, done} -> R
114
receive_result(C, Result) ->
115
    case receive_result(C, [], []) of
116
        done    -> Result;
117
        timeout -> {error, timeout};
118
        R       -> receive_result(C, R)
121
119
    end.
122
120
123
121
receive_results(C, Results) ->
124
122
    case receive_result(C, [], []) of
125
        done -> lists:reverse(Results);
126
        R    -> receive_results(C, [R | Results])
123
        done    -> lists:reverse(Results);
124
        timeout -> lists:reverse([{error, timeout} | Results]);
125
        R       -> receive_results(C, [R | Results])
127
126
    end.
128
127
129
128
receive_result(C, Cols, Rows) ->
@@ -144,9 +143,9 @@ receive_result(C, Cols, Rows) ->
144
143
        {pgsql, C, {notice, _N}} ->
145
144
            receive_result(C, Cols, Rows);
146
145
        {pgsql, C, done} ->
147
            done
148
    after
149
        ?timeout -> {error, timeout}
146
            done;
147
        {pgsql, C, timeout} ->
148
            timeout
150
149
    end.
151
150
152
151
receive_extended_result(C)->
@@ -168,7 +167,7 @@ receive_extended_result(C, Rows) ->
168
167
        {pgsql, C, {complete, _Type}} ->
169
168
            {ok, lists:reverse(Rows)};
170
169
        {pgsql, C, {notice, _N}} ->
171
            receive_extended_result(C, Rows)
172
    after
173
        ?timeout -> {error, timeout}
170
            receive_extended_result(C, Rows);
171
        {pgsql, C, timeout} ->
172
            {error, timeout}
174
173
    end.

Up to file-list src/pgsql_connection.erl:

14
14
15
15
-export([startup/3, auth/2, initializing/2, ready/2, ready/3]).
16
16
-export([querying/2, parsing/2, binding/2, describing/2]).
17
-export([executing/2, closing/2, synchronizing/2]).
17
-export([executing/2, closing/2, synchronizing/2, timeout/2]).
18
18
19
19
-include("pgsql.hrl").
20
20
21
21
-record(state, {
22
22
          reader,
23
23
          sock,
24
          timeout,
24
25
          parameters = [],
25
26
          reply,
26
27
          reply_to,
@@ -111,31 +112,35 @@ code_change(_Old_Vsn, State_Name, State,
111
112
%% -- states --
112
113
113
114
startup({connect, Host, Username, Password, Opts}, From, State) ->
115
    Timeout = proplists:get_value(timeout, Opts, 5000),
114
116
    case pgsql_sock:start_link(self(), Host, Username, Opts) of
115
117
        {ok, Sock} ->
116
118
            put(username, Username),
117
119
            put(password, Password),
118
            State2 = State#state{sock = Sock, reply_to = From},
119
            {next_state, auth, State2};
120
            State2 = State#state{sock = Sock, timeout = Timeout, reply_to = From},
121
            {next_state, auth, State2, Timeout};
120
122
        Error ->
121
123
            {stop, normal, Error, State}
122
124
    end.
123
125
124
126
%% AuthenticationOk
125
127
auth({$R, <<0:?int32>>}, State) ->
126
    {next_state, initializing, State};
128
    #state{timeout = Timeout} = State,
129
    {next_state, initializing, State, Timeout};
127
130
128
131
%% AuthenticationCleartextPassword
129
132
auth({$R, <<3:?int32>>}, State) ->
133
    #state{timeout = Timeout} = State,
130
134
    send(State, $p, [get(password), 0]),
131
    {next_state, auth, State};
135
    {next_state, auth, State, Timeout};
132
136
133
137
%% AuthenticationMD5Password
134
138
auth({$R, <<5:?int32, Salt:4/binary>>}, State) ->
139
    #state{timeout = Timeout} = State,
135
140
    Digest1 = hex(erlang:md5([get(password), get(username)])),
136
141
    Str = ["md5", hex(erlang:md5([Digest1, Salt])), 0],
137
142
    send(State, $p, Str),
138
    {next_state, auth, State};
143
    {next_state, auth, State, Timeout};
139
144
140
145
auth({$R, <<M:?int32, _/binary>>}, State) ->
141
146
    case M of
@@ -157,12 +162,17 @@ auth({error, E}, State) ->
157
162
        Any         -> Why = Any
158
163
    end,
159
164
    gen_fsm:reply(State#state.reply_to, {error, Why}),
165
    {stop, normal, State};
166
167
auth(timeout, State) ->
168
    gen_fsm:reply(State#state.reply_to, {error, timeout}),
160
169
    {stop, normal, State}.
161
170
162
171
%% BackendKeyData
163
172
initializing({$K, <<Pid:?int32, Key:?int32>>}, State) ->
173
    #state{timeout = Timeout} = State,
164
174
    State2 = State#state{backend = {Pid, Key}},
165
    {next_state, initializing, State2};
175
    {next_state, initializing, State2, Timeout};
166
176
167
177
%% ErrorResponse
168
178
initializing({error, E}, State) ->
@@ -173,6 +183,10 @@ initializing({error, E}, State) ->
173
183
    gen_fsm:reply(State#state.reply_to, {error, Why}),
174
184
    {stop, normal, State};
175
185
186
initializing(timeout, State) ->
187
    gen_fsm:reply(State#state.reply_to, {error, timeout}),
188
    {stop, normal, State};
189
176
190
%% ReadyForQuery
177
191
initializing({$Z, <<Status:8>>}, State) ->
178
192
    #state{parameters = Parameters, reply_to = Reply_To} = State,
@@ -190,12 +204,14 @@ ready(_Msg, State) ->
190
204
191
205
%% execute simple query
192
206
ready({squery, Sql}, From, State) ->
207
    #state{timeout = Timeout} = State,
193
208
    send(State, $Q, [Sql, 0]),
194
209
    State2 = State#state{statement = #statement{}, reply_to = From},
195
    {reply, ok, querying, State2};
210
    {reply, ok, querying, State2, Timeout};
196
211
197
212
%% execute extended query
198
213
ready({equery, Statement, Parameters}, From, State) ->
214
    #state{timeout = Timeout} = State,
199
215
    #statement{name = StatementName, columns = Columns} = Statement,
200
216
    Bin1 = encode_parameters(Parameters),
201
217
    Bin2 = encode_formats(Columns),
@@ -204,7 +220,7 @@ ready({equery, Statement, Parameters}, F
204
220
    send(State, $C, [$S, "", 0]),
205
221
    send(State, $S, []),
206
222
    State2 = State#state{statement = Statement, reply_to = From},
207
    {reply, ok, querying, State2};
223
    {reply, ok, querying, State2, Timeout};
208
224
209
225
ready({get_parameter, Name}, _From, State) ->
210
226
    case lists:keysearch(Name, 1, State#state.parameters) of
@@ -214,88 +230,108 @@ ready({get_parameter, Name}, _From, Stat
214
230
    {reply, {ok, Value}, ready, State};
215
231
216
232
ready({parse, Name, Sql, Types}, From, State) ->
233
    #state{timeout = Timeout} = State,
217
234
    Bin = encode_types(Types),
218
235
    send(State, $P, [Name, 0, Sql, 0, Bin]),
219
236
    send(State, $D, [$S, Name, 0]),
220
237
    send(State, $H, []),
221
238
    S = #statement{name = Name},
222
    {next_state, parsing, State#state{statement = S, reply_to = From}};
239
    State2 = State#state{statement = S, reply_to = From},
240
    {next_state, parsing, State2, Timeout};
223
241
224
242
ready({bind, Statement, PortalName, Parameters}, From, State) ->
243
    #state{timeout = Timeout} = State,
225
244
    #statement{name = StatementName, columns = Columns, types = Types} = Statement,
226
245
    Typed_Parameters = lists:zip(Types, Parameters),
227
246
    Bin1 = encode_parameters(Typed_Parameters),
228
247
    Bin2 = encode_formats(Columns),
229
248
    send(State, $B, [PortalName, 0, StatementName, 0, Bin1, Bin2]),
230
249
    send(State, $H, []),
231
    {next_state, binding, State#state{statement = Statement, reply_to = From}};
250
    State2 = State#state{statement = Statement, reply_to = From},
251
    {next_state, binding, State2, Timeout};
232
252
233
253
ready({execute, Statement, PortalName, MaxRows}, From, State) ->
254
    #state{timeout = Timeout} = State,
234
255
    send(State, $E, [PortalName, 0, <<MaxRows:?int32>>]),
235
256
    send(State, $H, []),
236
    {reply, ok, executing, State#state{statement = Statement, reply_to = From}};
257
    State2 = State#state{statement = Statement, reply_to = From},
258
    {reply, ok, executing, State2, Timeout};
237
259
238
260
ready({describe, Type, Name}, From, State) ->
261
    #state{timeout = Timeout} = State,
239
262
    case Type of
240
263
        statement -> Type2 = $S;
241
264
        portal    -> Type2 = $P
242
265
    end,
243
266
    send(State, $D, [Type2, Name, 0]),
244
267
    send(State, $H, []),
245
    {next_state, describing, State#state{reply_to = From}};
268
    {next_state, describing, State#state{reply_to = From}, Timeout};
246
269
247
270
ready({close, Type, Name}, From, State) ->
271
    #state{timeout = Timeout} = State,
248
272
    case Type of
249
273
        statement -> Type2 = $S;
250
274
        portal    -> Type2 = $P
251
275
    end,
252
276
    send(State, $C, [Type2, Name, 0]),
253
277
    send(State, $H, []),
254
    {next_state, closing, State#state{reply_to = From}};
278
    {next_state, closing, State#state{reply_to = From}, Timeout};
255
279
256
280
ready(sync, From, State) ->
281
    #state{timeout = Timeout} = State,
257
282
    send(State, $S, []),
258
    {next_state, synchronizing, State#state{reply = ok, reply_to = From}}.
283
    State2 = State#state{reply = ok, reply_to = From},
284
    {next_state, synchronizing, State2, Timeout}.
259
285
260
286
%% BindComplete
261
287
querying({$2, <<>>}, State) ->
262
    #state{statement = #statement{columns = Columns}} = State,
288
    #state{timeout = Timeout, statement = #statement{columns = Columns}} = State,
263
289
    notify(State, {columns, Columns}),
264
    {next_state, querying, State};
290
    {next_state, querying, State, Timeout};
265
291
266
292
%% CloseComplete
267
293
querying({$3, <<>>}, State) ->
268
    {next_state, querying, State};
294
    #state{timeout = Timeout} = State,
295
    {next_state, querying, State, Timeout};
269
296
270
297
%% RowDescription
271
298
querying({$T, <<Count:?int16, Bin/binary>>}, State) ->
299
    #state{timeout = Timeout} = State,
272
300
    Columns = decode_columns(Count, Bin),
273
301
    S2 = (State#state.statement)#statement{columns = Columns},
274
302
    notify(State, {columns, Columns}),
275
    {next_state, querying, State#state{statement = S2}};
303
    {next_state, querying, State#state{statement = S2}, Timeout};
276
304
277
305
%% DataRow
278
306
querying({$D, <<_Count:?int16, Bin/binary>>}, State) ->
279
    #state{statement = #statement{columns = Columns}} = State,
307
    #state{timeout = Timeout, statement = #statement{columns = Columns}} = State,
280
308
    Data = decode_data(Columns, Bin),
281
309
    notify(State, {data, Data}),
282
    {next_state, querying, State};
310
    {next_state, querying, State, Timeout};
283
311
284
312
%% CommandComplete
285
313
querying({$C, Bin}, State) ->
314
    #state{timeout = Timeout} = State,
286
315
    Complete = decode_complete(Bin),
287
316
    notify(State, {complete, Complete}),
288
    {next_state, querying, State};
317
    {next_state, querying, State, Timeout};
289
318
290
319
%% EmptyQueryResponse
291
320
querying({$I, _Bin}, State) ->
321
    #state{timeout = Timeout} = State,
292
322
    notify(State, {complete, empty}),
293
    {next_state, querying, State};
323
    {next_state, querying, State, Timeout};
324
325
querying(timeout, State) ->
326
    #state{sock = Sock, timeout = Timeout, backend = {Pid, Key}} = State,
327
    pgsql_sock:cancel(Sock, Pid, Key),
328
    {next_state, timeout, State, Timeout};
294
329
295
330
%% ErrorResponse
296
331
querying({error, E}, State) ->
332
    #state{timeout = Timeout} = State,
297
333
    notify(State, {error, E}),
298
    {next_state, querying, State};
334
    {next_state, querying, State, Timeout};
299
335
300
336
%% ReadyForQuery
301
337
querying({$Z, <<_Status:8>>}, State) ->
@@ -304,13 +340,21 @@ querying({$Z, <<_Status:8>>}, State) ->
304
340
305
341
%% ParseComplete
306
342
parsing({$1, <<>>}, State) ->
307
    {next_state, describing, State};
343
    #state{timeout = Timeout} = State,
344
    {next_state, describing, State, Timeout};
345
346
parsing(timeout, State) ->
347
    #state{timeout = Timeout} = State,
348
    Reply = {error, timeout},
349
    send(State, $S, []),
350
    {next_state, parsing, State#state{reply = Reply}, Timeout};
308
351
309
352
%% ErrorResponse
310
353
parsing({error, E}, State) ->
354
    #state{timeout = Timeout} = State,
311
355
    Reply = {error, E},
312
356
    send(State, $S, []),
313
    {next_state, parsing, State#state{reply = Reply}};
357
    {next_state, parsing, State#state{reply = Reply}, Timeout};
314
358
315
359
%% ReadyForQuery
316
360
parsing({$Z, <<Status:8>>}, State) ->
@@ -323,11 +367,18 @@ binding({$2, <<>>}, State) ->
323
367
    gen_fsm:reply(State#state.reply_to, ok),
324
368
    {next_state, ready, State};
325
369
370
binding(timeout, State) ->
371
    #state{timeout = Timeout} = State,
372
    Reply = {error, timeout},
373
    send(State, $S, []),
374
    {next_state, binding, State#state{reply = Reply}, Timeout};
375
326
376
%% ErrorResponse
327
377
binding({error, E}, State) ->
378
    #state{timeout = Timeout} = State,
328
379
    Reply = {error, E},
329
380
    send(State, $S, []),
330
    {next_state, binding, State#state{reply = Reply}};
381
    {next_state, binding, State#state{reply = Reply}, Timeout};
331
382
332
383
%% ReadyForQuery
333
384
binding({$Z, <<Status:8>>}, State) ->
@@ -337,9 +388,10 @@ binding({$Z, <<Status:8>>}, State) ->
337
388
338
389
%% ParameterDescription
339
390
describing({$t, <<_Count:?int16, Bin/binary>>}, State) ->
391
    #state{timeout = Timeout} = State,
340
392
    Types = [pgsql_types:oid2type(Oid) || <<Oid:?int32>> <= Bin],
341
393
    S2 = (State#state.statement)#statement{types = Types},
342
    {next_state, describing, State#state{statement = S2}};
394
    {next_state, describing, State#state{statement = S2}, Timeout};
343
395
344
396
%% RowDescription
345
397
describing({$T, <<Count:?int16, Bin/binary>>}, State) ->
@@ -355,11 +407,18 @@ describing({$n, <<>>}, State) ->
355
407
    gen_fsm:reply(State#state.reply_to, {ok, S2}),
356
408
    {next_state, ready, State};
357
409
410
describing(timeout, State) ->
411
    #state{timeout = Timeout} = State,
412
    Reply = {error, timeout},
413
    send(State, $S, []),
414
    {next_state, describing, State#state{reply = Reply}, Timeout};
415
358
416
%% ErrorResponse
359
417
describing({error, E}, State) ->
418
    #state{timeout = Timeout} = State,
360
419
    Reply = {error, E},
361
420
    send(State, $S, []),
362
    {next_state, describing, State#state{reply = Reply}};
421
    {next_state, describing, State#state{reply = Reply}, Timeout};
363
422
364
423
%% ReadyForQuery
365
424
describing({$Z, <<Status:8>>}, State) ->
@@ -369,10 +428,10 @@ describing({$Z, <<Status:8>>}, State) ->
369
428
370
429
%% DataRow
371
430
executing({$D, <<_Count:?int16, Bin/binary>>}, State) ->
372
    #state{statement = #statement{columns = Columns}} = State,
431
    #state{timeout = Timeout, statement = #statement{columns = Columns}} = State,
373
432
    Data = decode_data(Columns, Bin),
374
433
    notify(State, {data, Data}),
375
    {next_state, executing, State};
434
    {next_state, executing, State, Timeout};
376
435
377
436
%% PortalSuspended
378
437
executing({$s, <<>>}, State) ->
@@ -389,16 +448,27 @@ executing({$I, _Bin}, State) ->
389
448
    notify(State, {complete, empty}),
390
449
    {next_state, ready, State};
391
450
451
executing(timeout, State) ->
452
    #state{sock = Sock, timeout = Timeout, backend = {Pid, Key}} = State,
453
    pgsql_sock:cancel(Sock, Pid, Key),
454
    send(State, $S, []),
455
    {next_state, timeout, State, Timeout};
456
392
457
%% ErrorResponse
393
458
executing({error, E}, State) ->
459
    #state{timeout = Timeout} = State,
394
460
    notify(State, {error, E}),
395
    {next_state, executing, State}.
461
    {next_state, executing, State, Timeout}.
396
462
397
463
%% CloseComplete
398
464
closing({$3, <<>>}, State) ->
399
465
    gen_fsm:reply(State#state.reply_to, ok),
400
466
    {next_state, ready, State};
401
467
468
closing(timeout, State) ->
469
    gen_fsm:reply(State#state.reply_to, {error, timeout}),
470
    {next_state, ready, State};
471
402
472
%% ErrorResponse
403
473
closing({error, E}, State) ->
404
474
    Error = {error, E},
@@ -407,8 +477,14 @@ closing({error, E}, State) ->
407
477
408
478
%% ErrorResponse
409
479
synchronizing({error, E}, State) ->
480
    #state{timeout = Timeout} = State,
410
481
    Reply = {error, E},
411
    {next_state, synchronizing, State#state{reply = Reply}};
482
    {next_state, synchronizing, State#state{reply = Reply}, Timeout};
483
484
synchronizing(timeout, State) ->
485
    #state{timeout = Timeout} = State,
486
    Reply = {error, timeout},
487
    {next_state, synchronizing, State#state{reply = Reply}, Timeout};
412
488
413
489
%% ReadyForQuery
414
490
synchronizing({$Z, <<Status:8>>}, State) ->
@@ -416,6 +492,18 @@ synchronizing({$Z, <<Status:8>>}, State)
416
492
    gen_fsm:reply(Reply_To, Reply),
417
493
    {next_state, ready, State#state{reply = undefined, txstatus = Status}}.
418
494
495
timeout({$Z, <<Status:8>>}, State) ->
496
    notify(State, timeout),
497
    {next_state, ready, State#state{txstatus = Status}};
498
499
timeout(timeout, State) ->
500
    {stop, timeout, State};
501
502
%% ignore events that occur after timeout
503
timeout(_Event, State) ->
504
    #state{timeout = Timeout} = State,
505
    {next_state, timeout, State, Timeout}.
506
419
507
%% -- internal functions --
420
508
421
509
%% decode data

Up to file-list src/pgsql_sock.erl:

4
4
5
5
-behavior(gen_server).
6
6
7
-export([start_link/4, send/2, send/3]).
7
-export([start_link/4, send/2, send/3, cancel/3]).
8
8
-export([decode_string/1, lower_atom/1]).
9
9
10
10
-export([handle_call/3, handle_cast/2, handle_info/2]).
@@ -32,6 +32,9 @@ send(S, Data) ->
32
32
    Msg = <<(byte_size(Bin) + 4):?int32, Bin/binary>>,
33
33
    gen_server:cast(S, {send, Msg}).
34
34
35
cancel(S, Pid, Key) ->
36
    gen_server:cast(S, {cancel, Pid, Key}).
37
35
38
%% -- gen_server implementation --
36
39
37
40
init([C, Host, Username, Opts]) ->
@@ -74,6 +77,15 @@ handle_cast({send, Data}, State) ->
74
77
    ok = Mod:send(Sock, Data),
75
78
    {noreply, State};
76
79
80
handle_cast({cancel, Pid, Key}, State) ->
81
    {ok, {Addr, Port}} = inet:peername(State#state.sock),
82
    SockOpts = [{active, false}, {packet, raw}, binary],
83
    {ok, Sock} = gen_tcp:connect(Addr, Port, SockOpts),
84
    Msg = <<16:?int32, 80877102:?int32, Pid:?int32, Key:?int32>>,
85
    ok = gen_tcp:send(Sock, Msg),
86
    gen_tcp:close(Sock),
87
    {noreply, State};
88
77
89
handle_cast(Cast, State) ->
78
90
    {stop, {unsupported_cast, Cast}, State}.
79
91

Up to file-list test_src/pgsql_tests.erl:

@@ -406,6 +406,31 @@ text_format_test() ->
406
406
              Select("numeric", "123456")
407
407
      end).
408
408
409
connect_timeout_test() ->
410
    {error, timeout} = pgsql:connect(?host, [{port, ?port}, {timeout, 0}]).
411
412
query_timeout_test() ->
413
    with_connection(
414
      fun(C) ->
415
              {error, timeout} = pgsql:squery(C, "select pg_sleep(1)"),
416
              {error, timeout} = pgsql:equery(C, "select pg_sleep(2)"),
417
              {ok, _Cols, [{1}]} = pgsql:equery(C, "select 1")
418
      end,
419
      [{timeout, 10}]).
420
421
execute_timeout_test() ->
422
    with_connection(
423
      fun(C) ->
424
              {ok, S} = pgsql:parse(C, "select pg_sleep($1)"),
425
              ok = pgsql:bind(C, S, [2]),
426
              {error, timeout} = pgsql:execute(C, S, 0),
427
              ok = pgsql:bind(C, S, [0]),
428
              {ok, [{<<>>}]} = pgsql:execute(C, S, 0),
429
              ok = pgsql:close(C, S),
430
              ok = pgsql:sync(C)
431
      end,
432
      [{timeout, 10}]).
433
409
434
%% -- run all tests --
410
435
411
436
run_tests() ->
@@ -423,6 +448,9 @@ connect_only(Args) ->
423
448
with_connection(F) ->
424
449
    with_connection(F, "epgsql_test", []).
425
450
451
with_connection(F, Args) ->
452
    with_connection(F, "epgsql_test", Args).
453
426
454
with_connection(F, Username, Args) ->
427
455
    Args2 = [{port, ?port}, {database, "epgsql_test_db1"} | Args],
428
456
    {ok, C} = pgsql:connect(?host, Username, Args2),