epgsql_cmd_parse.erl 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. %% @doc Asks server to parse SQL query and send information aboud bind-parameters and result columns.
  2. %%
  3. %% ```
  4. %% > Parse
  5. %% < ParseComplete
  6. %% > Describe
  7. %% < ParameterDescription
  8. %% < RowDescription | NoData
  9. %% '''
  10. -module(epgsql_cmd_parse).
  11. -behaviour(epgsql_command).
  12. -export([init/1, execute/2, handle_message/4]).
  13. -export_type([response/0]).
  14. -include("epgsql.hrl").
  15. -include("protocol.hrl").
  16. -type response() :: {ok, #statement{}} | {error, epgsql:query_error()}.
  17. -record(parse,
  18. {name :: iodata(),
  19. sql :: iodata(),
  20. types :: [atom()],
  21. parameter_typenames = [] :: [epgsql:type_name() | {array, epgsql:type_name()}],
  22. parameter_descr = [] :: [epgsql_oid_db:oid_info()]}).
  23. %% FIXME: make it use oids instead of type names!
  24. init({Name, Sql, Types}) ->
  25. #parse{name = Name, sql = Sql, types = Types}.
  26. execute(Sock, #parse{name = Name, sql = Sql, types = Types} = St) ->
  27. Codec = epgsql_sock:get_codec(Sock),
  28. Bin = epgsql_wire:encode_types(Types, Codec),
  29. epgsql_sock:send_multi(
  30. Sock,
  31. [
  32. {?PARSE, [Name, 0, Sql, 0, Bin]},
  33. {?DESCRIBE, [?PREPARED_STATEMENT, Name, 0]},
  34. {?FLUSH, []}
  35. ]),
  36. {ok, Sock, St}.
  37. handle_message(?PARSE_COMPLETE, <<>>, Sock, _State) ->
  38. {noaction, Sock};
  39. handle_message(?PARAMETER_DESCRIPTION, Bin, Sock, State) ->
  40. Codec = epgsql_sock:get_codec(Sock),
  41. TypeInfos = epgsql_wire:decode_parameters(Bin, Codec),
  42. OidInfos = [epgsql_binary:typeinfo_to_oid_info(Type, Codec) || Type <- TypeInfos],
  43. TypeNames = [epgsql_binary:typeinfo_to_name_array(Type, Codec) || Type <- TypeInfos],
  44. Sock2 = epgsql_sock:notify(Sock, {types, TypeNames}),
  45. {noaction, Sock2, State#parse{parameter_descr = OidInfos,
  46. parameter_typenames = TypeNames}};
  47. handle_message(?ROW_DESCRIPTION, <<Count:?int16, Bin/binary>>, Sock,
  48. #parse{name = Name, parameter_descr = Params,
  49. parameter_typenames = TypeNames}) ->
  50. Codec = epgsql_sock:get_codec(Sock),
  51. Columns = epgsql_wire:decode_columns(Count, Bin, Codec),
  52. Columns2 = [Col#column{format = epgsql_wire:format(Col, Codec)}
  53. || Col <- Columns],
  54. Result = {ok, #statement{name = Name,
  55. types = TypeNames,
  56. columns = Columns2,
  57. parameter_info = Params}},
  58. {finish, Result, {columns, Columns2}, Sock};
  59. handle_message(?NO_DATA, <<>>, Sock, #parse{name = Name, parameter_descr = Params,
  60. parameter_typenames = TypeNames}) ->
  61. Result = {ok, #statement{name = Name,
  62. types = TypeNames,
  63. parameter_info = Params,
  64. columns = []}},
  65. {finish, Result, no_data, Sock};
  66. handle_message(?ERROR, Error, _Sock, _State) ->
  67. Result = {error, Error},
  68. {sync_required, Result};
  69. handle_message(_, _, _, _) ->
  70. unknown.