epgsql_cmd_parse.erl 3.3 KB

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