Просмотр исходного кода

Merge pull request #113 from seriyps/error_extra

Decode more error extra fields. Fixes #110
Sergey Prokhorov 8 лет назад
Родитель
Сommit
0c3bdd53b5
4 измененных файлов с 62 добавлено и 20 удалено
  1. 4 1
      include/epgsql.hrl
  2. 1 1
      src/epgsql_sock.erl
  3. 35 13
      src/epgsql_wire.erl
  4. 22 5
      test/epgsql_tests.erl

+ 4 - 1
include/epgsql.hrl

@@ -20,5 +20,8 @@
     code :: binary(),
     codename :: atom(),
     message :: binary(),
-    extra :: [{detail, binary()} | {hint, binary()} | {position, binary()}]
+    extra :: [{severity_en | detail | hint | position | internal_position | internal_query
+               | where | schema_name | table_name | column_name | data_type_name
+               | constraint_name | file | line | routine,
+               binary()}]
 }).

+ 1 - 1
src/epgsql_sock.erl

@@ -845,4 +845,4 @@ on_message({?COPY_DATA, <<?X_LOG_DATA, StartLSN:?int64, EndLSN:?int64, _Timestam
         CbModule:handle_x_log_data(StartLSN, EndLSN, WALRecord, CbState),
     {noreply, State#state{repl_feedback_required = true, repl_last_received_lsn = EndLSN,
         repl_last_flushed_lsn = LastFlushedLSN, repl_last_applied_lsn = LastAppliedLSN,
-        repl_cbstate = NewCbState}}.
+        repl_cbstate = NewCbState}}.

+ 35 - 13
src/epgsql_wire.erl

@@ -57,7 +57,7 @@ decode_fields(<<Type:8, Rest/binary>>, Acc) ->
     decode_fields(Rest2, [{Type, Str} | Acc]).
 
 %% decode ErrorResponse
-%% TODO add fields from http://www.postgresql.org/docs/9.0/interactive/protocol-error-fields.html
+%% See http://www.postgresql.org/docs/current/interactive/protocol-error-fields.html
 decode_error(Bin) ->
     Fields = decode_fields(Bin),
     ErrCode = proplists:get_value($C, Fields),
@@ -67,20 +67,42 @@ decode_error(Bin) ->
       code     = ErrCode,
       codename = ErrName,
       message  = proplists:get_value($M, Fields),
-      extra    = decode_error_extra(Fields)},
+      extra    = lists:sort(lists:foldl(fun decode_error_extra/2, [], Fields))},
     Error.
 
-decode_error_extra(Fields) ->
-    Types = [{$D, detail}, {$H, hint}, {$P, position}],
-    decode_error_extra(Types, Fields, []).
-
-decode_error_extra([], _Fields, Extra) ->
-    Extra;
-decode_error_extra([{Type, Name} | T], Fields, Extra) ->
-    case proplists:get_value(Type, Fields) of
-        undefined -> decode_error_extra(T, Fields, Extra);
-        Value     -> decode_error_extra(T, Fields, [{Name, Value} | Extra])
-    end.
+%% consider updating #error.extra typespec when changing/adding extras
+decode_error_extra({$V, Val}, Acc) ->
+    [{severity_en, Val} | Acc];
+decode_error_extra({$D, Val}, Acc) ->
+    [{detail, Val} | Acc];
+decode_error_extra({$H, Val}, Acc) ->
+    [{hint, Val} | Acc];
+decode_error_extra({$P, Val}, Acc) ->
+    [{position, Val} | Acc];
+decode_error_extra({$p, Val}, Acc) ->
+    [{internal_position, Val} | Acc];
+decode_error_extra({$q, Val}, Acc) ->
+    [{internal_query, Val} | Acc];
+decode_error_extra({$W, Val}, Acc) ->
+    [{where, Val} | Acc];
+decode_error_extra({$s, Val}, Acc) ->
+    [{schema_name, Val} | Acc];
+decode_error_extra({$t, Val}, Acc) ->
+    [{table_name, Val} | Acc];
+decode_error_extra({$c, Val}, Acc) ->
+    [{column_name, Val} | Acc];
+decode_error_extra({$d, Val}, Acc) ->
+    [{data_type_name, Val} | Acc];
+decode_error_extra({$n, Val}, Acc) ->
+    [{constraint_name, Val} | Acc];
+decode_error_extra({$F, Val}, Acc) ->
+    [{file, Val} | Acc];
+decode_error_extra({$L, Val}, Acc) ->
+    [{line, Val} | Acc];
+decode_error_extra({$R, Val}, Acc) ->
+    [{routine, Val} | Acc];
+decode_error_extra({_, _}, Acc) ->
+    Acc.
 
 lower_atom(Str) when is_binary(Str) ->
     lower_atom(binary_to_list(Str));

+ 22 - 5
test/epgsql_tests.erl

@@ -26,7 +26,9 @@
         code = <<"57014">>,
         codename = query_canceled,
         message = <<"canceling statement due to statement timeout">>,
-        extra = []
+        extra = [{file, <<"postgres.c">>},
+                 {line, _},
+                 {routine, _}]
         }}).
 
 %% From uuid.erl in http://gitorious.org/avtobiff/erlang-uuid
@@ -305,7 +307,10 @@ parse_error_test(Module) ->
     with_connection(
       Module,
       fun(C) ->
-              {error, #error{}} = Module:parse(C, "select _ from test_table1"),
+              {error, #error{extra = [{file, _},
+                                      {line, _},
+                                      {position, <<"8">>},
+                                      {routine, _}]}} = Module:parse(C, "select _ from test_table1"),
               {ok, S} = Module:parse(C, "select * from test_table1"),
               [#column{name = <<"id">>}, #column{name = <<"value">>}] = S#statement.columns,
               ok = Module:close(C, S),
@@ -375,7 +380,14 @@ execute_error_test(Module) ->
       fun(C) ->
           {ok, S} = Module:parse(C, "insert into test_table1 (id, value) values ($1, $2)"),
           ok = Module:bind(C, S, [1, <<"foo">>]),
-          {error, #error{code = <<"23505">>, codename = unique_violation}} = Module:execute(C, S, 0),
+          {error, #error{code = <<"23505">>, codename = unique_violation,
+                         extra = [{constraint_name, <<"test_table1_pkey">>},
+                                  {detail, _},
+                                  {file, _},
+                                  {line, _},
+                                  {routine, _},
+                                  {schema_name, <<"public">>},
+                                  {table_name, <<"test_table1">>}]}} = Module:execute(C, S, 0),
           {error, sync_required} = Module:bind(C, S, [3, <<"quux">>]),
           ok = Module:sync(C),
           ok = Module:bind(C, S, [3, <<"quux">>]),
@@ -802,7 +814,11 @@ warning_notice_test(Module) ->
                select pg_temp.raise()",
           [{ok, _, _}, _] = Module:squery(C, Q),
           receive
-              {epgsql, C, {notice, #error{message = <<"oops">>}}} -> ok
+              {epgsql, C, {notice, #error{message = <<"oops">>, extra = Extra}}} ->
+                  ?assertMatch([{file, _},
+                                {line, _},
+                                {routine, _}], Extra),
+                  ok
           after
               100 -> erlang:error(didnt_receive_notice)
           end
@@ -878,7 +894,8 @@ set_notice_receiver_test(Module) ->
           receive
               {epgsql, C, {notice, #error{severity = warning,
                                           code = <<"01000">>,
-                                          message = <<"test notice">>}}} -> ok
+                                          message = <<"test notice">>,
+                                          extra = _}}} -> ok
           after
               100 -> erlang:error(didnt_receive_notice)
           end,