pgsql-1.0.0/0000755000232200023220000000000012642720573013214 5ustar debalancedebalancepgsql-1.0.0/rebar.config0000644000232200023220000000015612642720573015500 0ustar debalancedebalance{erl_opts, [debug_info]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: pgsql-1.0.0/EPLICENSE0000644000232200023220000003336112642720573014454 0ustar debalancedebalanceERLANG PUBLIC LICENSE Version 1.1 1. Definitions. 1.1. ``Contributor'' means each entity that creates or contributes to the creation of Modifications. 1.2. ``Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. ``Executable'' means Covered Code in any form other than Source Code. 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. ``License'' means this document. 1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities,``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. 2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code or as part of a Larger Work; and (b) under patents now or hereafter owned or controlled by Contributor, to Utilize the Contributor Version (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Contributor Version (or portions thereof), and not to any greater extent that may be necessary to Utilize further Modifications or combinations. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which you contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters (a) Third Party Claims. If You have knowledge that a party claims an intellectual property right in particular functionality or code (or its utilization under this License), you must include a text file with the source code distribution titled ``LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If you obtain such knowledge after You make Your Modification available as described in Section 3.2, You shall promptly modify the LEGAL file in all copies You make available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Your Modification is an application programming interface and You own or control patents which are reasonably necessary to implement that API, you must also include this information in the LEGAL file. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code, and this License in any documentation for the Source Code, where You describe recipients' rights relating to Covered Code. If You created one or more Modification(s), You may add your name as a Contributor to the notice described in Exhibit A. If it is not possible to put such notice in a particular Source Code file due to its structure, then you must include such notice in a location (such as a relevant directory file) where a user would be likely to look for such a notice. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Application of this License. This License applies to code to which the Initial Developer has attached the notice in Exhibit A, and to related Covered Code. 6. CONNECTION TO MOZILLA PUBLIC LICENSE This Erlang License is a derivative work of the Mozilla Public License, Version 1.0. It contains terms which differ from the Mozilla Public License, Version 1.0. 7. DISCLAIMER OF WARRANTY. COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 8. TERMINATION. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 9. DISCLAIMER OF LIABILITY Any utilization of Covered Code shall not cause the Initial Developer or any Contributor to be liable for any damages (neither direct nor indirect). 10. MISCELLANEOUS This License represents the complete agreement concerning the subject matter hereof. If any provision is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be construed by and in accordance with the substantive laws of Sweden. Any dispute, controversy or claim arising out of or relating to this License, or the breach, termination or invalidity thereof, shall be subject to the exclusive jurisdiction of Swedish courts, with the Stockholm City Court as the first instance. EXHIBIT A. ``The contents of this file are subject to the Erlang Public License, Version 1.1, (the "License"); you may not use this file except in compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved via the world wide web at http://www.erlang.org/. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Initial Developer of the Original Code is Ericsson Utvecklings AB. Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings AB. All Rights Reserved.'' pgsql-1.0.0/src/0000755000232200023220000000000012642720573014003 5ustar debalancedebalancepgsql-1.0.0/src/pgsql_util.erl0000644000232200023220000002135312642720573016676 0ustar debalancedebalance%%% File : pgsql_util.erl %%% Author : Christian Sunesson %%% Description : utility functions used in implementation of %%% postgresql driver. %%% Created : 11 May 2005 by Blah -module(pgsql_util). %% Key-Value handling -export([option/3]). %% Networking -export([socket/1]). -export([send/2, send_int/2, send_msg/3]). -export([recv_msg/2, recv_msg/1, recv_byte/2, recv_byte/1]). %% Protocol packing -export([string/1, make_pair/2, split_pair/2]). -export([split_pair_rec/2]). -export([count_string/1, to_string/2]). -export([oids/2, coldescs/3, datacoldescs/3]). -export([decode_row/3, decode_descs/2]). -export([errordesc/2]). -export([to_integer/1, to_atom/1]). -export([zip/2]). %% Constructing authentication messages. -export([pass_plain/1, pass_md5/3]). -import(erlang, [md5/1]). -export([hexlist/2]). %% Lookup key in a plist stored in process dictionary under 'options'. %% Default is returned if there is no value for Key in the plist. option(Opts, Key, Default) -> case proplists:get_value(Key, Opts, Default) of Default -> Default; Value when is_binary(Value) -> binary_to_list(Value); Value -> Value end. %% Open a TCP connection socket({tcp, Host, Port}) -> gen_tcp:connect(Host, Port, [{active, false}, binary, {packet, raw}], 5000). send(Sock, Packet) -> gen_tcp:send(Sock, Packet). send_int(Sock, Int) -> Packet = <>, gen_tcp:send(Sock, Packet). send_msg(Sock, Code, Packet) when is_binary(Packet) -> Len = size(Packet) + 4, Msg = <>, gen_tcp:send(Sock, Msg). recv_msg(Sock, Timeout) -> {ok, Head} = gen_tcp:recv(Sock, 5, Timeout), <> = Head, %%io:format("Code: ~p, Size: ~p~n", [Code, Size]), if Size > 4 -> {ok, Packet} = gen_tcp:recv(Sock, Size-4, Timeout), {ok, Code, Packet}; true -> {ok, Code, <<>>} end. recv_msg(Sock) -> recv_msg(Sock, infinity). recv_byte(Sock) -> recv_byte(Sock, infinity). recv_byte(Sock, Timeout) -> case gen_tcp:recv(Sock, 1, Timeout) of {ok, <>} -> {ok, Byte}; E={error, _Reason} -> throw(E) end. %% Convert String to binary string(String) when is_list(String) -> Bin = list_to_binary(String), <>; string(Bin) when is_binary(Bin) -> <>. %%% Two zero terminated strings. make_pair(Key, Value) when is_atom(Key) -> make_pair(atom_to_list(Key), Value); make_pair(Key, Value) when is_atom(Value) -> make_pair(Key, atom_to_list(Value)); make_pair(Key, Value) when is_list(Key), is_list(Value) -> BinKey = list_to_binary(Key), BinValue = list_to_binary(Value), make_pair(BinKey, BinValue); make_pair(Key, Value) when is_binary(Key), is_binary(Value) -> <>. split_pair(Bin, AsBin) when is_binary(Bin) -> split_pair(binary_to_list(Bin), AsBin); split_pair(Str, AsBin) -> split_pair_rec(Str, norec, AsBin). split_pair_rec(Bin, AsBin) when is_binary(Bin) -> split_pair_rec(binary_to_list(Bin), AsBin); split_pair_rec(Arg, AsBin) -> split_pair_rec(Arg,[], AsBin). split_pair_rec([], Acc, _AsBin) -> lists:reverse(Acc); split_pair_rec([0], Acc, _AsBin) -> lists:reverse(Acc); split_pair_rec(S, Acc, AsBin) -> Fun = fun(C) -> C /= 0 end, {K, [0|S1]} = lists:splitwith(Fun, S), {V, [0|Tail]} = lists:splitwith(Fun, S1), {Key, Value} = if AsBin -> {list_to_binary(K), list_to_binary(V)}; true -> {K, V} end, case Acc of norec -> {Key, Value}; _ -> split_pair_rec(Tail, [{Key, Value}| Acc], AsBin) end. count_string(Bin) when is_binary(Bin) -> count_string(Bin, 0). count_string(<<>>, N) -> {N, <<>>}; count_string(<<0/integer, Rest/binary>>, N) -> {N, Rest}; count_string(<<_C/integer, Rest/binary>>, N) -> count_string(Rest, N+1). to_string(Bin, AsBin) when is_binary(Bin) -> {Count, _} = count_string(Bin, 0), <> = Bin, if AsBin -> {String, Count}; true -> {binary_to_list(String), Count} end. oids(<<>>, Oids) -> lists:reverse(Oids); oids(<>, Oids) -> oids(Rest, [Oid|Oids]). coldescs(<<>>, Descs, _AsBin) -> lists:reverse(Descs); coldescs(Bin, Descs, AsBin) -> {Name, Count} = to_string(Bin, AsBin), <<_:Count/binary, 0/integer, TableOID:32/integer, ColumnNumber:16/integer, TypeId:32/integer, TypeSize:16/integer-signed, TypeMod:32/integer-signed, FormatCode:16/integer, Rest/binary>> = Bin, Format = case FormatCode of 0 -> text; 1 -> binary end, Desc = {Name, Format, ColumnNumber, TypeId, TypeSize, TypeMod, TableOID}, coldescs(Rest, [Desc|Descs], AsBin). datacoldescs(N, <<16#ffffffff:32, Rest/binary>>, Descs) when N >= 0 -> datacoldescs(N-1, Rest, [null|Descs]); datacoldescs(N, <>, Descs) when N >= 0 -> datacoldescs(N-1, Rest, [Data|Descs]); datacoldescs(_N, _, Descs) -> lists:reverse(Descs). decode_descs(OidMap, Cols) -> decode_descs(OidMap, Cols, []). decode_descs(_OidMap, [], Descs) -> {ok, lists:reverse(Descs)}; decode_descs(OidMap, [Col|ColTail], Descs) -> {Name, Format, ColNumber, Oid, _, _, _} = Col, OidName = dict:fetch(Oid, OidMap), decode_descs(OidMap, ColTail, [{Name, Format, ColNumber, OidName, [], [], []}|Descs]). decode_row(Types, Values, AsBin) -> decode_row(Types, Values, [], AsBin). decode_row([], [], Out, _AsBin) -> {ok, lists:reverse(Out)}; decode_row([Type|TypeTail], [Value|ValueTail], Out0, AsBin) -> Out1 = decode_col(Type, Value, AsBin), decode_row(TypeTail, ValueTail, [Out1|Out0], AsBin). decode_col({_, text, _, _, _, _, _}, Value, AsBin) -> if AsBin -> Value; true -> binary_to_list(Value) end; decode_col({_Name, _Format, _ColNumber, varchar, _Size, _Modifier, _TableOID}, Value, AsBin) -> if AsBin -> Value; true -> binary_to_list(Value) end; decode_col({_Name, _Format, _ColNumber, int4, _Size, _Modifier, _TableOID}, Value, _AsBin) -> <> = Value, Int4; decode_col({_Name, _Format, _ColNumber, Oid, _Size, _Modifier, _TableOID}, Value, _AsBin) -> {Oid, Value}. errordesc(Bin, AsBin) -> errordesc(Bin, [], AsBin). errordesc(<<0/integer, _Rest/binary>>, Lines, _AsBin) -> lists:reverse(Lines); errordesc(<>, Lines, AsBin) -> {String, Count} = to_string(Rest, AsBin), <<_:Count/binary, 0, Rest1/binary>> = Rest, Msg = case Code of $S -> {severity, to_atom(String)}; $C -> {code, String}; $M -> {message, String}; $D -> {detail, String}; $H -> {hint, String}; $P -> {position, to_integer(String)}; $p -> {internal_position, to_integer(String)}; $W -> {where, String}; $F -> {file, String}; $L -> {line, to_integer(String)}; $R -> {routine, String}; Unknown -> {Unknown, String} end, errordesc(Rest1, [Msg|Lines], AsBin). %%% Zip two lists together zip(List1, List2) -> zip(List1, List2, []). zip(List1, List2, Result) when List1 =:= []; List2 =:= [] -> lists:reverse(Result); zip([H1|List1], [H2|List2], Result) -> zip(List1, List2, [{H1, H2}|Result]). %%% Authentication utils pass_plain(Password) -> Pass = [Password, 0], list_to_binary(Pass). %% MD5 authentication patch from %% Juhani Rankimies %% (patch slightly rewritten, new bugs are mine :] /Christian Sunesson) %% %% MD5(MD5(password + user) + salt) %% pass_md5(User, Password, Salt) -> Digest = hex(md5([Password, User])), Encrypt = hex(md5([Digest, Salt])), Pass = ["md5", Encrypt, 0], list_to_binary(Pass). to_integer(B) when is_binary(B) -> to_integer(binary_to_list(B)); to_integer(S) -> list_to_integer(S). to_atom(B) when is_binary(B) -> to_atom(binary_to_list(B)); to_atom(S) -> list_to_atom(S). hex(B) when is_binary(B) -> hexlist(binary_to_list(B), []). hexlist([], Acc) -> lists:reverse(Acc); hexlist([N|Rest], Acc) -> HighNibble = (N band 16#f0) bsr 4, LowNibble = (N band 16#0f), hexlist(Rest, [hexdigit(LowNibble), hexdigit(HighNibble)|Acc]). hexdigit(0) -> $0; hexdigit(1) -> $1; hexdigit(2) -> $2; hexdigit(3) -> $3; hexdigit(4) -> $4; hexdigit(5) -> $5; hexdigit(6) -> $6; hexdigit(7) -> $7; hexdigit(8) -> $8; hexdigit(9) -> $9; hexdigit(10) -> $a; hexdigit(11) -> $b; hexdigit(12) -> $c; hexdigit(13) -> $d; hexdigit(14) -> $e; hexdigit(15) -> $f. pgsql-1.0.0/src/p1_pgsql.app.src0000644000232200023220000000117012642720573017020 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Evgeniy Khramtsov %%% @copyright (C) 2013, Evgeniy Khramtsov %%% @doc %%% %%% @end %%% Created : 4 Apr 2013 by Evgeniy Khramtsov %%%------------------------------------------------------------------- {application, p1_pgsql, [{description, "PostgreSQL driver"}, {vsn, "1.0.0"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib]}, {mod, {pgsql_app,[]}}]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: pgsql-1.0.0/src/pgsql_tcp.erl0000644000232200023220000000467612642720573016520 0ustar debalancedebalance%%% File : pgsql_tcp.erl %%% Author : Blah %%% Description : Unwrapping of TCP line protocol packages to postgres messages. %%% Created : 22 Jul 2005 -module(pgsql_tcp). -behaviour(gen_server). -export([start/3, start_link/3]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, code_change/3, handle_info/2, terminate/2]). -record(state, {socket, protopid, buffer, as_binary}). start(Sock, ProtoPid, AsBin) -> gen_server:start(?MODULE, [Sock, ProtoPid, AsBin], []). start_link(Sock, ProtoPid, AsBin) -> gen_server:start_link(?MODULE, [Sock, ProtoPid, AsBin], []). init([Sock, ProtoPid, AsBin]) -> inet:setopts(Sock, [{active, once}]), {ok, #state{socket = Sock, protopid = ProtoPid, buffer = <<>>, as_binary = AsBin}}. handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. handle_info({tcp, Sock, Bin}, #state{socket = Sock, protopid = ProtoPid, as_binary = AsBin, buffer = Buffer} = State) -> {ok, Rest} = process_buffer(ProtoPid, AsBin, <>), inet:setopts(Sock, [{active, once}]), {noreply, State#state{buffer = Rest}}; handle_info({tcp_closed, Sock}, #state{socket = Sock, protopid = ProtoPid} = State) -> io:format("Sock closed~n", []), ProtoPid ! {socket, Sock, closed}, {stop, tcp_close, State}; handle_info({tcp_error, Sock, Reason}, #state{socket = Sock, protopid = ProtoPid} = State) -> io:format("Sock error~n", []), ProtoPid ! {socket, Sock, {error, Reason}}, {stop, tcp_error, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. %% Given a binary that begins with a proper message header the binary %% will be processed for each full message it contains, and it will %% return any trailing incomplete messages. process_buffer(ProtoPid, AsBin, Bin = <>) -> Payload = Size - 4, if size(Rest) >= Payload -> <> = Rest, {ok, Message} = pgsql_proto:decode_packet(Code, Packet, AsBin), ProtoPid ! {pgsql, Message}, process_buffer(ProtoPid, AsBin, Rest1); true -> {ok, Bin} end; process_buffer(_ProtoPid, _AsBin, Bin) when is_binary(Bin) -> {ok, Bin}. pgsql-1.0.0/src/pgsql_sup.erl0000644000232200023220000000360712642720573016532 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pgsql_sup.erl %%% Author : Evgeniy Khramtsov %%% Purpose : PostgreSQL erlang driver supervisor %%% Created : 15 May 2013 by Evgeniy Khramtsov %%% %%% %%% p1_pgsql, Copyright (C) 2002-2015 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%---------------------------------------------------------------------- -module(pgsql_sup). -behaviour(supervisor). %% API -export([start_link/0]). %% Supervisor callbacks -export([init/1]). -define(SERVER, ?MODULE). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). %%%=================================================================== %%% Supervisor callbacks %%%=================================================================== init([]) -> {ok, {{one_for_one, 10, 1}, []}}. %%%=================================================================== %%% Internal functions %%%=================================================================== pgsql-1.0.0/src/pgsql_proto.erl0000644000232200023220000005155112642720573017067 0ustar debalancedebalance%%% File : pgsql_proto.erl %%% Author : Christian Sunesson %%% Description : PostgreSQL protocol driver %%% Created : 9 May 2005 %%% This is the protocol handling part of the PostgreSQL driver, it turns packages into %%% erlang term messages and back. -module(pgsql_proto). -behaviour(gen_server). %% TODO: %% When factorizing make clear distinction between message and packet. %% Packet == binary on-wire representation %% Message = parsed Packet as erlang terms. %%% Version 3.0 of the protocol. %%% Supported in postgres from version 7.4 -define(PROTOCOL_MAJOR, 3). -define(PROTOCOL_MINOR, 0). %%% PostgreSQL protocol message codes -define(PG_BACKEND_KEY_DATA, $K). -define(PG_PARAMETER_STATUS, $S). -define(PG_ERROR_MESSAGE, $E). -define(PG_NOTICE_RESPONSE, $N). -define(PG_EMPTY_RESPONSE, $I). -define(PG_ROW_DESCRIPTION, $T). -define(PG_DATA_ROW, $D). -define(PG_READY_FOR_QUERY, $Z). -define(PG_AUTHENTICATE, $R). -define(PG_BIND, $B). -define(PG_PARSE, $P). -define(PG_COMMAND_COMPLETE, $C). -define(PG_PARSE_COMPLETE, $1). -define(PG_BIND_COMPLETE, $2). -define(PG_CLOSE_COMPLETE, $3). -define(PG_PORTAL_SUSPENDED, $s). -define(PG_NO_DATA, $n). -export([start/1, start_link/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, code_change/3, handle_info/2, terminate/2]). %% For protocol unwrapping, pgsql_tcp for example. -export([decode_packet/3]). -export([encode_message/2]). -export([encode/2]). -import(pgsql_util, [option/3]). -import(pgsql_util, [socket/1]). -import(pgsql_util, [send/2, send_int/2, send_msg/3]). -import(pgsql_util, [recv_msg/2, recv_msg/1, recv_byte/2, recv_byte/1]). -import(pgsql_util, [string/1, make_pair/2, split_pair/2]). -import(pgsql_util, [count_string/1, to_string/2]). -import(pgsql_util, [coldescs/3, datacoldescs/3]). -import(pgsql_util, [to_integer/1, to_atom/1]). -record(state, {options, driver, params, socket, oidmap, as_binary}). start(Options) -> gen_server:start(?MODULE, [self(), Options], []). start_link(Options) -> gen_server:start_link(?MODULE, [self(), Options], []). init([DriverPid, Options]) -> %%io:format("Init~n", []), %% Default values: We connect to localhost on the standard TCP/IP %% port. Host = option(Options, host, "localhost"), Port = option(Options, port, 5432), AsBinary = option(Options, as_binary, false), case socket({tcp, Host, Port}) of {ok, Sock} -> connect(#state{options = Options, driver = DriverPid, as_binary = AsBinary, socket = Sock}); Error -> Reason = {init, Error}, {stop, Reason} end. connect(StateData) -> %%io:format("Connect~n", []), %% Connection settings for database-login. %% TODO: Check if the default values are relevant: UserName = option(StateData#state.options, user, "cos"), DatabaseName = option(StateData#state.options, database, "template1"), %% Make protocol startup packet. Version = <>, User = make_pair(user, UserName), Database = make_pair(database, DatabaseName), StartupPacket = <>, %% Backend will continue with authentication after the startup packet PacketSize = 4 + size(StartupPacket), Sock = StateData#state.socket, ok = gen_tcp:send(Sock, <>), authenticate(StateData). authenticate(StateData) -> %% Await authentication request from backend. Sock = StateData#state.socket, AsBin = StateData#state.as_binary, {ok, Code, Packet} = recv_msg(Sock, 5000), {ok, Value} = decode_packet(Code, Packet, AsBin), case Value of %% Error response {error_message, Message} -> {stop, {authentication, Message}}; {authenticate, {AuthMethod, Salt}} -> case AuthMethod of 0 -> % Auth ok setup(StateData, []); 1 -> % Kerberos 4 {stop, {nyi, auth_kerberos4}}; 2 -> % Kerberos 5 {stop, {nyi, auth_kerberos5}}; 3 -> % Plaintext password Password = option(StateData#state.options, password, ""), EncodedPass = encode_message(pass_plain, Password), ok = send(Sock, EncodedPass), authenticate(StateData); 4 -> % Hashed password {stop, {nyi, auth_crypt}}; 5 -> % MD5 password Password = option(StateData#state.options, password, ""), User = option(StateData#state.options, user, ""), EncodedPass = encode_message(pass_md5, {User, Password, Salt}), ok = send(Sock, EncodedPass), authenticate(StateData); _ -> {stop, {authentication, {unknown, AuthMethod}}} end; %% Unknown message received Any -> {stop, {protocol_error, Any}} end. setup(StateData, Params) -> %% Receive startup messages until ReadyForQuery Sock = StateData#state.socket, AsBin = StateData#state.as_binary, {ok, Code, Package} = recv_msg(Sock, 5000), {ok, Pair} = decode_packet(Code, Package, AsBin), case Pair of %% BackendKeyData, necessary for issuing cancel requests {backend_key_data, {Pid, Secret}} -> Params1 = [{secret, {Pid, Secret}} | Params], setup(StateData, Params1); %% ParameterStatus, a key-value pair. {parameter_status, {Key, Value}} -> Params1 = [{{parameter, Key}, Value} | Params], setup(StateData, Params1); %% Error message, with a sequence of <> %% of error descriptions. Code==0 terminates the Reason. {error_message, Message} -> gen_tcp:close(Sock), {stop, {error_response, Message}}; %% Notice Response, with a sequence of <> %% identified fields. Code==0 terminates the Notice. {notice_response, Notice} -> deliver(StateData, {pgsql_notice, Notice}), setup(StateData, Params); %% Ready for Query, backend is ready for a new query cycle {ready_for_query, _Status} -> connected(StateData#state{params = Params}, Sock); Any -> {stop, {unknown_setup, Any}} end. %% Connected state. Can now start to push messages %% between frontend and backend. But first some setup. connected(StateData, Sock) -> %% Protocol unwrapping process. Factored out to make future %% SSL and unix domain support easier. Store process under %% 'socket' in the process dictionary. AsBin = StateData#state.as_binary, {ok, Unwrapper} = pgsql_tcp:start_link(Sock, self(), AsBin), ok = gen_tcp:controlling_process(Sock, Unwrapper), %% Lookup oid to type names and store them in a dictionary under %% 'oidmap' in the process dictionary. Packet = encode_message(squery, "SELECT oid, typname FROM pg_type"), ok = send(Sock, Packet), {ok, [{_, _ColDesc, Rows}]} = process_squery([], AsBin), Rows1 = lists:map(fun ([CodeS, NameS]) -> Code = to_integer(CodeS), Name = to_atom(NameS), {Code, Name} end, Rows), OidMap = dict:from_list(Rows1), {ok, StateData#state{oidmap = OidMap}}. handle_call(terminate, _From, State) -> Sock = State#state.socket, Packet = encode_message(terminate, []), ok = send(Sock, Packet), gen_tcp:close(Sock), Reply = ok, {stop, normal, Reply, State}; %% Simple query handle_call({squery, Query}, _From, State) -> Sock = State#state.socket, AsBin = State#state.as_binary, Packet = encode_message(squery, Query), ok = send(Sock, Packet), {ok, Result} = process_squery([], AsBin), case lists:keymember(error, 1, Result) of true -> RBPacket = encode_message(squery, "ROLLBACK"), ok = send(Sock, RBPacket), {ok, _RBResult} = process_squery([], AsBin); _ -> ok end, Reply = {ok, Result}, {reply, Reply, State}; %% Extended query %% simplistic version using the unnammed prepared statement and portal. handle_call({equery, {Query, Params}}, _From, State) -> Sock = State#state.socket, ParseP = encode_message(parse, {"", Query, []}), BindP = encode_message(bind, {"", "", Params, [binary]}), DescribeP = encode_message(describe, {portal, ""}), ExecuteP = encode_message(execute, {"", 0}), SyncP = encode_message(sync, []), ok = send(Sock, [ParseP, BindP, DescribeP, ExecuteP, SyncP]), {ok, Command, Desc, Status, Logs} = process_equery(State, []), OidMap = State#state.oidmap, NameTypes = lists:map(fun({Name, _Format, _ColNo, Oid, _, _, _}) -> {Name, dict:fetch(Oid, OidMap)} end, Desc), Reply = {ok, Command, Status, NameTypes, Logs}, {reply, Reply, State}; %% Prepare a statement, so it can be used for queries later on. handle_call({prepare, {Name, Query}}, _From, State) -> Sock = State#state.socket, send_message(Sock, parse, {Name, Query, []}), send_message(Sock, describe, {prepared_statement, Name}), send_message(Sock, sync, []), {ok, State, ParamDesc, ResultDesc} = process_prepare({[], []}), OidMap = State#state.oidmap, ParamTypes = lists:map(fun (Oid) -> dict:fetch(Oid, OidMap) end, ParamDesc), ResultNameTypes = lists:map(fun ({ColName, _Format, _ColNo, Oid, _, _, _}) -> {ColName, dict:fetch(Oid, OidMap)} end, ResultDesc), Reply = {ok, State, ParamTypes, ResultNameTypes}, {reply, Reply, State}; %% Close a prepared statement. handle_call({unprepare, Name}, _From, State) -> Sock = State#state.socket, send_message(Sock, close, {prepared_statement, Name}), send_message(Sock, sync, []), {ok, _Status} = process_unprepare(), Reply = ok, {reply, Reply, State}; %% Execute a prepared statement handle_call({execute, {Name, Params}}, _From, State) -> Sock = State#state.socket, %%io:format("execute: ~p ~p ~n", [Name, Params]), begin % Issue first requests for the prepared statement. BindP = encode_message(bind, {"", Name, Params, [binary]}), DescribeP = encode_message(describe, {portal, ""}), ExecuteP = encode_message(execute, {"", 0}), FlushP = encode_message(flush, []), ok = send(Sock, [BindP, DescribeP, ExecuteP, FlushP]) end, receive {pgsql, {bind_complete, _}} -> % Bind reply first. %% Collect response to describe message, %% which gives a hint of the rest of the messages. {ok, Command, Result} = process_execute(State, Sock), begin % Close portal and end extended query. CloseP = encode_message(close, {portal, ""}), SyncP = encode_message(sync, []), ok = send(Sock, [CloseP, SyncP]) end, receive %% Collect response to close message. {pgsql, {close_complete, _}} -> receive %% Collect response to sync message. {pgsql, {ready_for_query, _Status}} -> %%io:format("execute: ~p ~p ~p~n", %% [Status, Command, Result]), Reply = {ok, {Command, Result}}, {reply, Reply, State}; {pgsql, Unknown} -> {stop, Unknown, {error, Unknown}, State} end; {pgsql, Unknown} -> {stop, Unknown, {error, Unknown}, State} end; {pgsql, Unknown} -> {stop, Unknown, {error, Unknown}, State} end; handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. handle_cast(_Msg, State) -> {noreply, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% Socket closed or socket error messages. handle_info({socket, _Sock, Condition}, State) -> {stop, {socket, Condition}, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. deliver(State, Message) -> DriverPid = State#state.driver, DriverPid ! Message. %% In the process_squery state we collect responses until the backend is %% done processing. process_squery(Log, AsBin) -> receive {pgsql, {row_description, Cols}} -> {ok, Command, Rows} = process_squery_cols([], AsBin), process_squery([{Command, Cols, Rows}|Log], AsBin); {pgsql, {command_complete, Command}} -> process_squery([Command|Log], AsBin); {pgsql, {ready_for_query, _Status}} -> {ok, lists:reverse(Log)}; {pgsql, {error_message, Error}} -> process_squery([{error, Error}|Log], AsBin); {pgsql, _Any} -> process_squery(Log, AsBin) end. process_squery_cols(Log, AsBin) -> receive {pgsql, {data_row, Row}} -> process_squery_cols( [lists:map( fun(null) -> null; (R) when AsBin == true -> R; (R) -> binary_to_list(R) end, Row) | Log], AsBin); {pgsql, {command_complete, Command}} -> {ok, Command, lists:reverse(Log)} end. process_equery(State, Log) -> receive %% Consume parse and bind complete messages when waiting for the first %% first row_description message. What happens if the equery doesnt %% return a result set? {pgsql, {parse_complete, _}} -> process_equery(State, Log); {pgsql, {bind_complete, _}} -> process_equery(State, Log); {pgsql, {row_description, Descs}} -> OidMap = State#state.oidmap, AsBin = State#state.as_binary, {ok, Descs1} = pgsql_util:decode_descs(OidMap, Descs), process_equery_datarow(Descs1, Log, {undefined, Descs, undefined}, AsBin); {pgsql, Any} -> process_equery(State, [Any|Log]) end. process_equery_datarow(Types, Log, Info={Command, Desc, Status}, AsBin) -> receive %% {pgsql, {command_complete, Command1}} -> process_equery_datarow(Types, Log, {Command1, Desc, Status}, AsBin); {pgsql, {ready_for_query, Status1}} -> {ok, Command, Desc, Status1, lists:reverse(Log)}; {pgsql, {data_row, Row}} -> {ok, DecodedRow} = pgsql_util:decode_row(Types, Row, AsBin), process_equery_datarow(Types, [DecodedRow|Log], Info, AsBin); {pgsql, Any} -> process_equery_datarow(Types, [Any|Log], Info, AsBin) end. process_prepare(Info={ParamDesc, ResultDesc}) -> receive {pgsql, {no_data, _}} -> process_prepare({ParamDesc, []}); {pgsql, {parse_complete, _}} -> process_prepare(Info); {pgsql, {parameter_description, Oids}} -> process_prepare({Oids, ResultDesc}); {pgsql, {row_description, Desc}} -> process_prepare({ParamDesc, Desc}); {pgsql, {ready_for_query, Status}} -> {ok, Status, ParamDesc, ResultDesc}; {pgsql, Any} -> io:format("process_prepare: ~p~n", [Any]), process_prepare(Info) end. process_unprepare() -> receive {pgsql, {ready_for_query, Status}} -> {ok, Status}; {pgsql, {close_complate, []}} -> process_unprepare(); {pgsql, Any} -> io:format("process_unprepare: ~p~n", [Any]), process_unprepare() end. process_execute(State, Sock) -> %% Either the response begins with a no_data or a row_description %% Needs to return {ok, Status, Result} %% where Result = {Command, ...} receive {pgsql, {no_data, _}} -> {ok, _Command, _Result} = process_execute_nodata(); {pgsql, {row_description, Descs}} -> OidMap = State#state.oidmap, AsBin = State#state.as_binary, {ok, Types} = pgsql_util:decode_descs(OidMap, Descs), {ok, _Command, _Result} = process_execute_resultset(Sock, Types, [], AsBin); {pgsql, Unknown} -> exit(Unknown) end. process_execute_nodata() -> receive {pgsql, {command_complete, Cmd}} -> Command = if is_binary(Cmd) -> binary_to_list(Cmd); true -> Cmd end, case Command of "INSERT "++Rest -> {ok, [{integer, _, _Table}, {integer, _, NRows}], _} = erl_scan:string(Rest), {ok, 'INSERT', NRows}; "SELECT" -> {ok, 'SELECT', should_not_happen}; "DELETE "++Rest -> {ok, [{integer, _, NRows}], _} = erl_scan:string(Rest), {ok, 'DELETE', NRows}; Any -> {ok, nyi, Any} end; {pgsql, Unknown} -> exit(Unknown) end. process_execute_resultset(Sock, Types, Log, AsBin) -> receive {pgsql, {command_complete, Command}} -> {ok, to_atom(Command), lists:reverse(Log)}; {pgsql, {data_row, Row}} -> {ok, DecodedRow} = pgsql_util:decode_row(Types, Row, AsBin), process_execute_resultset(Sock, Types, [DecodedRow|Log], AsBin); {pgsql, {portal_suspended, _}} -> throw(portal_suspended); {pgsql, Any} -> %%process_execute_resultset(Types, [Any|Log]) exit(Any) end. %% With a message type Code and the payload Packet apropriate %% decoding procedure can proceed. decode_packet(Code, Packet, AsBin) -> Ret = fun(CodeName, Values) -> {ok, {CodeName, Values}} end, case Code of ?PG_ERROR_MESSAGE -> Message = pgsql_util:errordesc(Packet, AsBin), Ret(error_message, Message); ?PG_EMPTY_RESPONSE -> Ret(empty_response, []); ?PG_ROW_DESCRIPTION -> <<_Columns:16/integer, ColDescs/binary>> = Packet, Descs = coldescs(ColDescs, [], AsBin), Ret(row_description, Descs); ?PG_READY_FOR_QUERY -> <> = Packet, case State of $I -> Ret(ready_for_query, idle); $T -> Ret(ready_for_query, transaction); $E -> Ret(ready_for_query, failed_transaction) end; ?PG_COMMAND_COMPLETE -> {Task, _} = to_string(Packet, AsBin), Ret(command_complete, Task); ?PG_DATA_ROW -> <> = Packet, ColData = datacoldescs(NumberCol, RowData, []), Ret(data_row, ColData); ?PG_BACKEND_KEY_DATA -> <> = Packet, Ret(backend_key_data, {Pid, Secret}); ?PG_PARAMETER_STATUS -> {Key, Value} = split_pair(Packet, AsBin), Ret(parameter_status, {Key, Value}); ?PG_NOTICE_RESPONSE -> Ret(notice_response, []); ?PG_AUTHENTICATE -> <> = Packet, Ret(authenticate, {AuthMethod, Salt}); ?PG_PARSE_COMPLETE -> Ret(parse_complete, []); ?PG_BIND_COMPLETE -> Ret(bind_complete, []); ?PG_PORTAL_SUSPENDED -> Ret(portal_suspended, []); ?PG_CLOSE_COMPLETE -> Ret(close_complete, []); $t -> <<_NParams:16/integer, OidsP/binary>> = Packet, Oids = pgsql_util:oids(OidsP, []), Ret(parameter_description, Oids); ?PG_NO_DATA -> Ret(no_data, []); _Any -> Ret(unknown, [Code]) end. send_message(Sock, Type, Values) -> %%io:format("send_message:~p~n", [{Type, Values}]), Packet = encode_message(Type, Values), ok = send(Sock, Packet). %% Add header to a message. encode(Code, Packet) -> Len = size(Packet) + 4, <>. %% Encode a message of a given type. encode_message(pass_plain, Password) -> Pass = pgsql_util:pass_plain(Password), encode($p, Pass); encode_message(pass_md5, {User, Password, Salt}) -> Pass = pgsql_util:pass_md5(User, Password, Salt), encode($p, Pass); encode_message(terminate, _) -> encode($X, <<>>); encode_message(squery, Query) -> % squery as in simple query. encode($Q, string(Query)); encode_message(close, {Object, Name}) -> Type = case Object of prepared_statement -> $S; portal -> $P end, String = string(Name), encode($C, <>); encode_message(describe, {Object, Name}) -> ObjectP = case Object of prepared_statement -> $S; portal -> $P end, NameP = string(Name), encode($D, <>); encode_message(flush, _) -> encode($H, <<>>); encode_message(parse, {Name, Query, _Oids}) -> StringName = string(Name), StringQuery = string(Query), encode($P, <>); encode_message(bind, {NamePortal, NamePrepared, Parameters, ResultFormats}) -> PortalP = string(NamePortal), PreparedP = string(NamePrepared), ParamFormatsList = lists:map( fun (Bin) when is_binary(Bin) -> <<1:16/integer>>; (_Text) -> <<0:16/integer>> end, Parameters), ParamFormatsP = erlang:list_to_binary(ParamFormatsList), NParameters = length(Parameters), ParametersList = lists:map( fun (null) -> Minus = -1, <>; (Bin) when is_binary(Bin) -> Size = size(Bin), <>; (Integer) when is_integer(Integer) -> List = integer_to_list(Integer), Bin = list_to_binary(List), Size = size(Bin), <>; (Text) -> Bin = list_to_binary(Text), Size = size(Bin), <> end, Parameters), ParametersP = erlang:list_to_binary(ParametersList), NResultFormats = length(ResultFormats), ResultFormatsList = lists:map( fun (binary) -> <<1:16/integer>>; (text) -> <<0:16/integer>> end, ResultFormats), ResultFormatsP = erlang:list_to_binary(ResultFormatsList), %%io:format("encode bind: ~p~n", [{PortalP, PreparedP, %% NParameters, ParamFormatsP, %% NParameters, ParametersP, %% NResultFormats, ResultFormatsP}]), encode($B, <>); encode_message(execute, {Portal, Limit}) -> String = string(Portal), encode($E, <>); encode_message(sync, _) -> encode($S, <<>>). pgsql-1.0.0/src/pgsql.erl0000644000232200023220000000541212642720573015637 0ustar debalancedebalance%%% File : pgsql.erl %%% Author : Christian Sunesson %%% Description : PostgresQL interface %%% Created : 11 May 2005 %% %% API for accessing the postgres driver. %% -module(pgsql). -export([connect/1, connect/4, connect/5]). -export([squery/2, pquery/3, terminate/1, prepare/3, unprepare/2, execute/3]). connect(Host, Database, User, Password) -> connect([{database, Database}, {host, Host}, {user, User}, {password, Password}]). connect(Host, Database, User, Password, Port) -> connect([{database, Database}, {host, Host}, {user, User}, {port, Port}, {password, Password}]). connect(Options) -> pgsql_proto:start(Options). %% Close a connection terminate(Db) -> gen_server:call(Db, terminate). %%% In the "simple query" protocol, the frontend just sends a %%% textual query string, which is parsed and immediately %%% executed by the backend. %% A simple query can contain multiple statements (separated with a semi-colon), %% and each statement's response. %%% squery(Db, Query) -> {ok, Results} | ... no real error handling %%% Query = string() %%% Results = [Result] %%% Result = {"SELECT", RowDesc, ResultSet} | ... squery(Db, Query) -> gen_server:call(Db, {squery, Query}, infinity). %%% In the "extended query" protocol, processing of queries is %%% separated into multiple steps: parsing, binding of parameter %%% values, and execution. This offers flexibility and performance %%% benefits, at the cost of extra complexity. %%% pquery(Db, Query, Params) -> {ok, Command, Status, NameTypes, Rows} | timeout | ... %%% Query = string() %%% Params = [term()] %%% Command = string() %%% Status = idle | transaction | failed_transaction %%% NameTypes = [{ColName, ColType}] %%% Rows = [list()] pquery(Db, Query, Params) -> gen_server:call(Db, {equery, {Query, Params}}). %%% prepare(Db, Name, Query) -> {ok, Status, ParamTypes, ResultTypes} %%% Status = idle | transaction | failed_transaction %%% ParamTypes = [atom()] %%% ResultTypes = [{ColName, ColType}] prepare(Db, Name, Query) when is_atom(Name) -> gen_server:call(Db, {prepare, {atom_to_list(Name), Query}}). %%% unprepare(Db, Name) -> ok | timeout | ... %%% Name = atom() unprepare(Db, Name) when is_atom(Name) -> gen_server:call(Db, {unprepare, atom_to_list(Name)}). %%% execute(Db, Name, Params) -> {ok, Result} | timeout | ... %%% Result = {'INSERT', NRows} | %%% {'DELETE', NRows} | %%% {'SELECT', ResultSet} | %%% ... %%% ResultSet = [Row] %%% Row = list() execute(Db, Name, Params) when is_atom(Name), is_list(Params) -> Ref = make_ref(), Db ! {execute, Ref, self(), {atom_to_list(Name), Params}}, receive {pgsql, Ref, Result} -> {ok, Result} after 5000 -> timeout end. pgsql-1.0.0/src/pgsql_app.erl0000644000232200023220000000322112642720573016473 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : pgsql_app.erl %%% Author : Evgeniy Khramtsov %%% Purpose : PostgreSQL erlang driver application %%% Created : 15 May 2013 by Evgeniy Khramtsov %%% %%% %%% p1_pgsql, Copyright (C) 2002-2015 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%---------------------------------------------------------------------- -module(pgsql_app). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %%%=================================================================== %%% Application callbacks %%%=================================================================== start(_StartType, _StartArgs) -> pgsql_sup:start_link(). stop(_State) -> ok. %%%=================================================================== %%% Internal functions %%%=================================================================== pgsql-1.0.0/Makefile0000644000232200023220000000010612642720573014651 0ustar debalancedebalanceall: src src: rebar compile clean: rebar clean .PHONY: clean src