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 1: a71f55eedb4f
parent 0: 0d769bdbad94
branch: default
properly decode all command complete tags
Will Glozer / will
15 months ago

Changed (Δ1.1 KB):

raw changeset »

src/pgsql_connection.erl (20 lines added, 14 lines removed)

test_src/pgsql_tests.erl (20 lines added, 1 lines removed)

Up to file-list src/pgsql_connection.erl:

@@ -83,7 +83,7 @@ handle_event({notice, Notice}, State_Nam
83
83
handle_event({parameter_status, Name, Value}, State_Name, State) ->
84
84
    Parameters2 = lists:keystore(Name, 1, State#state.parameters, {Name, Value}),
85
85
    {next_state, State_Name, State#state{parameters = Parameters2}};
86
    
86
87
87
handle_event(stop, _State_Name, State) ->
88
88
    {stop, normal, State};
89
89
@@ -98,8 +98,8 @@ handle_info({'EXIT', Pid, Reason}, _Stat
98
98
99
99
handle_info(Info, _State_Name, State) ->
100
100
    {stop, {unsupported_info, Info}, State}.
101
    
102
terminate(_Reason, _State_Name, State = #state{sock = Sock}) 
101
102
terminate(_Reason, _State_Name, State = #state{sock = Sock})
103
103
  when Sock =/= undefined ->
104
104
    send(State, $X, []),
105
105
    gen_tcp:close(Sock);
@@ -113,7 +113,7 @@ code_change(_Old_Vsn, State_Name, State,
113
113
%% -- states --
114
114
115
115
startup({connect, Host, Username, Password, Opts}, From, State) ->
116
    Port      = proplists:get_value(port, Opts, 5432),    
116
    Port      = proplists:get_value(port, Opts, 5432),
117
117
    Sock_Opts = [{active, false}, {packet, raw}, binary],
118
118
    case gen_tcp:connect(Host, Port, Sock_Opts) of
119
119
        {ok, Sock} ->
@@ -124,14 +124,14 @@ startup({connect, Host, Username, Passwo
124
124
                undefined -> Opts3 = Opts2;
125
125
                Database  -> Opts3 = [Opts2 | ["database", 0, Database, 0]]
126
126
            end,
127
            
127
128
128
            put(username, Username),
129
129
            put(password, Password),
130
130
            State2 = State#state{reader   = Reader,
131
131
                                 sock     = Sock,
132
132
                                 reply_to = From},
133
133
            send(State2, [<<196608:32>>, Opts3, 0]),
134
    
134
135
135
            {next_state, auth, State2};
136
136
        Error ->
137
137
            {stop, normal, Error, State}
@@ -284,7 +284,7 @@ querying({$3, <<>>}, State) ->
284
284
%% RowDescription
285
285
querying({$T, <<Count:?int16, Bin/binary>>}, State) ->
286
286
    Columns = decode_columns(Count, Bin),
287
    S2 = (State#state.statement)#statement{columns = Columns},    
287
    S2 = (State#state.statement)#statement{columns = Columns},
288
288
    notify(State, {columns, Columns}),
289
289
    {next_state, querying, State#state{statement = S2}};
290
290
@@ -496,12 +496,18 @@ decode_columns(N, Bin, Acc) ->
496
496
    decode_columns(N - 1, Rest2, [Desc | Acc]).
497
497
498
498
%% decode command complete msg
499
decode_complete(<<"SELECT", 0>>)   -> select;
500
decode_complete(<<"BEGIN", 0>>)    -> 'begin';
501
decode_complete(<<"ROLLBACK", 0>>) -> rollback;
499
502
decode_complete(Bin) ->
500
503
    {Str, _} = decode_string(Bin),
501
504
    case string:tokens(binary_to_list(Str), " ") of
502
        [Type]             -> lower_atom(Type);
503
        [Type, _Oid, Rows] -> {lower_atom(Type), list_to_integer(Rows)};
504
        [Type, Rows]       -> {lower_atom(Type), list_to_integer(Rows)}
505
        ["INSERT", _Oid, Rows] -> {insert, list_to_integer(Rows)};
506
        ["UPDATE", Rows]       -> {update, list_to_integer(Rows)};
507
        ["DELETE", Rows]       -> {delete, list_to_integer(Rows)};
508
        ["MOVE", Rows]         -> {move, list_to_integer(Rows)};
509
        ["FETCH", _Rows]       -> fetch;
510
        [Type | _Rest]         -> lower_atom(Type)
505
511
    end.
506
512
507
513
%% decode ErrorResponse
@@ -517,7 +523,7 @@ decode_error(Bin) ->
517
523
decode_error_extra(Fields) ->
518
524
    Types = [{$D, detail}, {$H, hint}, {$P, position}],
519
525
    decode_error_extra(Types, Fields, []).
520
    
526
521
527
decode_error_extra([], _Fields, Extra) ->
522
528
    Extra;
523
529
decode_error_extra([{Type, Name} | T], Fields, Extra) ->
@@ -608,7 +614,7 @@ hex(Bin) ->
608
614
609
615
send(#state{sock = Sock}, Type, Data) ->
610
616
    Bin = iolist_to_binary(Data),
611
    gen_tcp:send(Sock, <<Type:8, (byte_size(Bin) + 4):?int32, Bin/binary>>).    
617
    gen_tcp:send(Sock, <<Type:8, (byte_size(Bin) + 4):?int32, Bin/binary>>).
612
618
613
619
send(#state{sock = Sock}, Data) ->
614
620
    Bin = iolist_to_binary(Data),
@@ -636,7 +642,7 @@ decode(Fsm, Sock, <<Type:8, Len:?int32,
636
642
            gen_fsm:send_event(Fsm, {Type, Data}),
637
643
            decode(Fsm, Sock, Tail);
638
644
        _Other ->
639
            read(Fsm, Sock, Bin)
645
            ?MODULE:read(Fsm, Sock, Bin)
640
646
    end;
641
647
decode(Fsm, Sock, Bin) ->
642
    read(Fsm, Sock, Bin).
648
    ?MODULE:read(Fsm, Sock, Bin).

Up to file-list test_src/pgsql_tests.erl:

@@ -66,6 +66,25 @@ update_test() ->
66
66
              {ok, _, [{<<"2">>}]} = pgsql:squery(C, "select count(*) from test_table1 where value = 'foo'")
67
67
      end).
68
68
69
create_and_drop_table_test() ->
70
    with_rollback(
71
      fun(C) ->
72
              {ok, [], []} = pgsql:squery(C, "create table test_table3 (id int4)"),
73
              {ok, [#column{type = int4}], []} = pgsql:squery(C, "select * from test_table3"),
74
              {ok, [], []} = pgsql:squery(C, "drop table test_table3")
75
      end).
76
77
cursor_test() ->
78
    with_connection(
79
      fun(C) ->
80
              {ok, [], []} = pgsql:squery(C, "begin"),
81
              {ok, [], []} = pgsql:squery(C, "declare c cursor for select id from test_table1"),
82
              {ok, 2} = pgsql:squery(C, "move forward 2 from c"),
83
              {ok, 1} = pgsql:squery(C, "move backward 1 from c"),
84
              {ok, _Cols, [{<<"2">>}]} = pgsql:squery(C, "fetch next from c"),
85
              {ok, [], []} = pgsql:squery(C, "close c")
86
              end).
87
69
88
multiple_result_test() ->
70
89
    with_connection(
71
90
      fun(C) ->
@@ -219,7 +238,7 @@ describe_error_test() ->
219
238
              {ok, S} = pgsql:parse(C, "select * from test_table1"),
220
239
              {ok, S} = pgsql:describe(C, statement, ""),
221
240
              ok = pgsql:sync(C)
222
      
241
223
242
      end).
224
243
225
244
portal_test() ->