p1_pgsql-1.1.31/0000775000175000017500000000000014731607742013716 5ustar debalancedebalancep1_pgsql-1.1.31/rebar.config0000664000175000017500000000306514731607742016204 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : rebar.config %%% Author : Mickael Remond %%% Purpose : Rebar build script. Compliant with rebar and rebar3. %%% Created : 15 Dec 2015 by Mickael Remond %%% %%% Copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%---------------------------------------------------------------------- {erl_opts, [debug_info, {src_dirs, ["src"]}, {i, "include"}]}. {deps, [{xmpp, "~> 1.9.0", {git, "https://github.com/processone/xmpp", {tag, "1.9.2"}}}]}. {cover_enabled, true}. {cover_export_enabled, true}. {coveralls_coverdata , "_build/test/cover/eunit.coverdata"}. {coveralls_service_name , "github"}. {xref_checks, [undefined_function_calls, undefined_functions, deprecated_function_calls, deprecated_functions]}. {profiles, [{test, [{erl_opts, [{src_dirs, ["src", "test"]}]}]}]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: p1_pgsql-1.1.31/src/0000775000175000017500000000000014731607742014505 5ustar debalancedebalancep1_pgsql-1.1.31/src/pgsql_sasl.erl0000664000175000017500000001317714731607742017372 0ustar debalancedebalance%%%------------------------------------------------------------------- %%% @author Alexey Shchepin %%% %%% Copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%------------------------------------------------------------------- -module(pgsql_sasl). -author('alexey@process-one.net'). -export([client_new/3, client_step/2, client_finish/2]). -record(sasl_state, {user :: binary(), password :: binary(), nonce :: binary(), verify = <<>> :: binary() }). -spec client_new(binary(), binary(), list(binary())) -> {ok, binary(), binary(), #sasl_state{}} | {error, any()}. client_new(User, Password, Mechs) -> case lists:member(<<"SCRAM-SHA-256">>, Mechs) of true -> Nonce = base64:encode(crypto:strong_rand_bytes(18)), State = #sasl_state{user = User, password = Password, nonce = Nonce}, Msg = client_first_message_bare(User, Nonce), Response = <<"n,,", Msg/binary>>, {ok, <<"SCRAM-SHA-256">>, Response, State}; false -> {error, "No supported SASL mechs"} end. -spec client_step(#sasl_state{}, binary()) -> {ok, binary(), #sasl_state{}} | {error, any()}. client_step(State, ServerResponse) -> case parse(ServerResponse) of SResp when is_list(SResp) -> I = binary_to_integer(proplists:get_value(<<"i">>, SResp)), R = proplists:get_value(<<"r">>, SResp), S = base64:decode(proplists:get_value(<<"s">>, SResp)), Nonce = State#sasl_state.nonce, NonceSize = size(Nonce), case R of <> -> ClientMsg1 = client_first_message_bare( State#sasl_state.user, State#sasl_state.nonce), ClientMsg2 = <<"c=biws,r=", R/binary>>, AuthMessage = <>, Password = State#sasl_state.password, SaltedPassword = scram:salted_password( sha256, Password, S, I), ClientKey = scram:client_key(sha256, SaltedPassword), StoredKey = scram:stored_key(sha256, ClientKey), ClientSignature = scram:client_signature(sha256, StoredKey, AuthMessage), ClientProof = crypto:exor(ClientKey, ClientSignature), P = base64:encode(ClientProof), Msg = < {error, {"Error parsing server response", ServerResponse}} end. -spec client_finish(#sasl_state{}, binary()) -> ok | {error, any()}. client_finish(State, ServerResponse) -> case parse(ServerResponse) of SResp when is_list(SResp) -> V = base64:decode(proplists:get_value(<<"v">>, SResp)), if State#sasl_state.verify == V -> ok; true -> {error, "SASL server verification failed"} end; _ -> {error, {"Error parsing server response", ServerResponse}} end. client_first_message_bare(User, Nonce) -> <<"n=", User/binary, ",r=", Nonce/binary>>. parse(S) -> parse1(S, <<>>, []). parse1(<<$=, Cs/binary>>, S, Ts) -> parse2(Cs, S, <<>>, Ts); parse1(<<$,, Cs/binary>>, <<>>, Ts) -> parse1(Cs, <<>>, Ts); parse1(<<$\s, Cs/binary>>, <<>>, Ts) -> parse1(Cs, <<>>, Ts); parse1(<>, S, Ts) -> parse1(Cs, <>, Ts); parse1(<<>>, <<>>, T) -> lists:reverse(T); parse1(<<>>, _S, _T) -> bad. parse2(<<$", Cs/binary>>, Key, Val, Ts) -> parse3(Cs, Key, Val, Ts); parse2(<>, Key, Val, Ts) -> parse4(Cs, Key, <>, Ts); parse2(<<>>, _, _, _) -> bad. parse3(<<$", Cs/binary>>, Key, Val, Ts) -> parse4(Cs, Key, Val, Ts); parse3(<<$\\, C, Cs/binary>>, Key, Val, Ts) -> parse3(Cs, Key, <>, Ts); parse3(<>, Key, Val, Ts) -> parse3(Cs, Key, <>, Ts); parse3(<<>>, _, _, _) -> bad. parse4(<<$,, Cs/binary>>, Key, Val, Ts) -> parse1(Cs, <<>>, [{Key, Val} | Ts]); parse4(<<$\s, Cs/binary>>, Key, Val, Ts) -> parse4(Cs, Key, Val, Ts); parse4(<>, Key, Val, Ts) -> parse4(Cs, Key, <>, Ts); parse4(<<>>, Key, Val, Ts) -> parse1(<<>>, <<>>, [{Key, Val} | Ts]). p1_pgsql-1.1.31/src/pgsql_app.erl0000664000175000017500000000322114731607742017175 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-2024 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 %%%=================================================================== p1_pgsql-1.1.31/src/pgsql_proto.erl0000664000175000017500000006620414731607742017572 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). -define(STARTTLS, 80877103). %%% 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_socket for example. -export([decode_packet/3]). -export([encode_message/2]). -export([encode/2]). -import(pgsql_util, [option/3]). -import(pgsql_util, [socket/2, close/1, controlling_process/2, starttls/2]). -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, ssl_options, transport, sasl_state, 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. {CommonOpts, SSLOpts} = lists:partition( fun({host, _}) -> true; ({port, _}) -> true; ({as_binary, _}) -> true; ({user, _}) -> true; ({password, _}) -> true; ({database, _}) -> true; ({transport, _}) -> true; ({connect_timeout, _}) -> true; (_) -> false end, Options), Host = option(CommonOpts, host, "localhost"), Port = option(CommonOpts, port, 5432), AsBinary = option(CommonOpts, as_binary, false), Transport = option(CommonOpts, transport, tcp), Timeout = option(CommonOpts, connect_timeout, 5000), case socket({Host, Port}, Timeout) of {ok, Sock} -> State = #state{options = CommonOpts, transport = Transport, ssl_options = SSLOpts, driver = DriverPid, as_binary = AsBinary, socket = Sock}, case Transport of tcp -> connect(State); ssl -> starttls(State) end; Error -> Reason = {init, Error}, {stop, Reason} end. starttls(StateData) -> SSLRequest = <>, PacketSize = 4 + size(SSLRequest), Sock = StateData#state.socket, ok = pgsql_util:send(Sock, <>), process_starttls(StateData). 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 = pgsql_util:send(Sock, <>), authenticate(StateData). process_starttls(StateData) -> Sock = StateData#state.socket, case recv_byte(Sock, 5000) of {ok, $S} -> case starttls(Sock, StateData#state.ssl_options) of {ok, Sock1} -> connect(StateData#state{socket = Sock1}); {error, _} = Err -> throw(Err) end; {ok, $N} -> {stop, {starttls, denied}} end. 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, AuthData}} -> 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, AuthData}), ok = send(Sock, EncodedPass), authenticate(StateData); 10 -> % SASL Password = option(StateData#state.options, password, ""), User = option(StateData#state.options, user, ""), Mechs = binary:split(AuthData, <<0>>, [global, trim_all]), BUser = list_to_binary(User), BPassword = list_to_binary(Password), case pgsql_sasl:client_new(BUser, BPassword, Mechs) of {ok, Mech, Resp, SASLState} -> Response = encode_message(sasl_initial_response, {Mech, Resp}), ok = send(Sock, Response), authenticate( StateData#state{sasl_state = SASLState}); {error, Error} -> {stop, {authentication, Error}} end; 11 -> % SASL continue case pgsql_sasl:client_step(StateData#state.sasl_state, AuthData) of {ok, Resp, SASLState} -> Response = encode_message(sasl_response, Resp), ok = send(Sock, Response), authenticate( StateData#state{sasl_state = SASLState}); {error, Error} -> {stop, {authentication, Error}} end; 12 -> % SASL final case pgsql_sasl:client_finish(StateData#state.sasl_state, AuthData) of ok -> authenticate( StateData#state{sasl_state = undefined}); {error, Error} -> {stop, {authentication, Error}} end; _ -> {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} -> 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_socket:start_link(Sock, self(), AsBin), ok = 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), 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, %{value, {TextOid, _}} = % lists:keysearch(text, 2, dict:to_list(State#state.oidmap)), %ParamOids = [TextOid || _ <- lists:seq(1, 10)], send_message(Sock, parse, {Name, Query, []}), send_message(Sock, describe, {prepared_statement, Name}), send_message(Sock, sync, []), case process_prepare({[], []}) of {ok, Status, ParamDesc, ResultDesc} -> 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, Status, ParamTypes, ResultNameTypes}, {reply, Reply, State}; {error, Error} -> Reply = {error, Error}, {reply, Reply, State} end; %% 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. case process_execute(State, Sock) of {ok, Command, Result} -> 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; {error, _} = Error -> begin SyncP = encode_message(sync, []), ok = send(Sock, [SyncP]) end, receive {pgsql, {ready_for_query, _Status}} -> Reply = Error, {reply, Reply, State}; {pgsql, Unknown} -> {stop, Unknown, {error, Unknown}, State} end end; {pgsql, {error_message, Error}} -> begin SyncP = encode_message(sync, []), ok = send(Sock, [SyncP]) end, receive %% Collect response to sync message. {pgsql, {ready_for_query, _Status}} -> %%io:format("execute: ~p ~p ~p~n", %% [Status, Command, Result]), Reply = {error, Error}, {reply, Reply, State}; {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) -> %%io:format("pgsql unexpected info ~p~n", [_Info]), {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}} -> case process_squery_cols([], AsBin) of {ok, Command, Rows} -> process_squery([{Command, Cols, Rows}|Log], AsBin); {error, _} = Error -> process_squery([Error | Log], AsBin) end; {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)}; {pgsql, {error_message, Error}} -> {error, Error}; {pgsql, _Any} -> process_squery_cols(Log, AsBin) 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, {error_message, Error}} -> process_prepare_error(Error); {pgsql, _Any} -> %%io:format("process_prepare: ~p~n", [Any]), process_prepare(Info) end. process_prepare_error(Error) -> receive {pgsql, {ready_for_query, _Status}} -> {error, Error}; {pgsql, _Any} -> %%io:format("process_prepare: ~p~n", [Any]), process_prepare_error(Error) 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, _}} -> process_execute_nodata(); {pgsql, {row_description, Descs}} -> OidMap = State#state.oidmap, AsBin = State#state.as_binary, {ok, Types} = pgsql_util:decode_descs(OidMap, Descs), 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}; "UPDATE "++Rest -> {ok, [{integer, _, NRows}], _} = erl_scan:string(Rest), {ok, 'UPDATE', NRows}; Any -> {ok, nyi, Any} end; {pgsql, {error_message, Error}} -> {error, Error}; {pgsql, Unknown} -> exit(Unknown) end. process_execute_resultset(Sock, Types, Log, AsBin) -> receive {pgsql, {command_complete, Command}} -> {ok, 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, {error_message, Error}} -> {error, Error}; {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(sasl_initial_response, {Mech, Resp}) -> RespLen = size(Resp), Msg = <>, encode($p, Msg); encode_message(sasl_response, Resp) -> encode($p, Resp); 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), Params = length(Oids), Types = iolist_to_binary([<> || Oid <- Oids]), 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, <<>>). p1_pgsql-1.1.31/src/pgsql_util.erl0000664000175000017500000003234614731607742017404 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/2, close/1, controlling_process/2, starttls/2]). -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]). -include_lib("kernel/include/inet.hrl"). %% 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 connection socket({Host, Port}, Timeout) -> case connect(Host, Port, Timeout) of {ok, Sock} -> {ok, {gen_tcp, Sock}}; {error, _} = Err -> Err end. close({Mod, Sock}) -> Mod:close(Sock). controlling_process({Mod, Sock}, Owner) -> Mod:controlling_process(Sock, Owner). starttls({gen_tcp, Sock}, Opts) -> inet:setopts(Sock, [{active, once}]), case ssl:connect(Sock, Opts, 5000) of {ok, SSLSock} -> ssl:setopts(SSLSock, [{active, false}]), {ok, {ssl, SSLSock}}; {error, _} = Err -> Err end. send({Mod, Sock}, Packet) -> Mod:send(Sock, Packet). send_int({Mod, Sock}, Int) -> Packet = <>, Mod:send(Sock, Packet). send_msg({Mod, Sock}, Code, Packet) when is_binary(Packet) -> Len = size(Packet) + 4, Msg = <>, Mod:send(Sock, Msg). recv_msg({Mod, Sock}, Timeout) -> {ok, Head} = Mod:recv(Sock, 5, Timeout), <> = Head, %%io:format("Code: ~p, Size: ~p~n", [Code, Size]), if Size > 4 -> {ok, Packet} = Mod:recv(Sock, Size-4, Timeout), {ok, Code, Packet}; true -> {ok, Code, <<>>} end. recv_msg({Mod, Sock}) -> recv_msg({Mod, Sock}, infinity). recv_byte({Mod, Sock}) -> recv_byte({Mod, Sock}, infinity). recv_byte({Mod, Sock}, Timeout) -> case Mod: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, bool, _Size, _Modifier, _TableOID}, Value, _AsBin) -> B = case Value of <<0>> -> <<"0">>; <<1>> -> <<"1">> end, {bool, B}; decode_col({_Name, _Format, _ColNumber, int2, _Size, _Modifier, _TableOID}, Value, _AsBin) -> <> = Value, {int2, integer_to_binary(Int)}; decode_col({_Name, _Format, _ColNumber, int4, _Size, _Modifier, _TableOID}, Value, _AsBin) -> <> = Value, {int4, integer_to_binary(Int)}; decode_col({_Name, _Format, _ColNumber, int8, _Size, _Modifier, _TableOID}, Value, _AsBin) -> <> = Value, {int8, integer_to_binary(Int)}; decode_col({_Name, _Format, _ColNumber, numeric, _Size, _Modifier, _TableOID}, Value, _AsBin) -> N = decode_numeric(Value), {numeric, N}; 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. -define(NBASE, 10000). -define(NUMERIC_POS, 16#0000). -define(NUMERIC_NEG, 16#4000). -define(NUMERIC_NAN, 16#C000). decode_numeric(<<_Length:16/unsigned, Weight:16/signed, Sign:16/unsigned, _DScale:16/unsigned, Data/binary>>) -> case Sign of ?NUMERIC_NAN -> <<"NaN">>; _ -> N = decode_numeric_digits(Data, Weight, 0), SN = case Sign of ?NUMERIC_POS -> N; ?NUMERIC_NEG -> -N end, if is_integer(SN) -> integer_to_binary(SN); is_float(SN) -> float_to_binary(SN) end end. decode_numeric_digits(<<>>, Weight, Res) when Weight < 0 -> Res; decode_numeric_digits(<<>>, Weight, Res) -> decode_numeric_digits(<<>>, Weight - 1, Res * ?NBASE); decode_numeric_digits(Data, Weight, Res) when Weight < 0 -> decode_numeric_digits1(Data, Weight, Res); decode_numeric_digits(<>, Weight, Res) -> decode_numeric_digits(Data, Weight - 1, Res * ?NBASE + D). decode_numeric_digits1(<<>>, _Weight, Res) -> Res; decode_numeric_digits1(<>, Weight, Res) -> decode_numeric_digits1(Data, Weight - 1, Res + D * math:pow(?NBASE, Weight)). %%-------------------------------------------------------------------- %% Connecting stuff %%-------------------------------------------------------------------- connect(Host, Port, Timeout) -> case lookup(Host, Timeout) of {ok, AddrsFamilies} -> do_connect(AddrsFamilies, Port, Timeout, {error, nxdomain}); {error, _} = Err -> Err end. do_connect([{IP, Family}|AddrsFamilies], Port1, Timeout, _Err) -> Port = case Family of local -> 0; _ -> Port1 end, case gen_tcp:connect(IP, Port, [{active, false}, binary, {packet, raw}, Family], Timeout) of {ok, Sock} -> {ok, Sock}; {error, _} = Err -> do_connect(AddrsFamilies, Port, Timeout, Err) end; do_connect([], _Port, _Timeout, Err) -> Err. lookup([$u,$n,$i,$x,$: | Path], _Timeout) -> {ok, [{{local, Path}, local}]}; lookup(Host, Timeout) -> case inet:parse_address(Host) of {ok, IP} -> {ok, [{IP, get_addr_type(IP)}]}; {error, _} -> do_lookup([{Host, Family} || Family <- [inet6, inet]], Timeout, [], {error, nxdomain}) end. do_lookup([{Host, Family}|HostFamilies], Timeout, AddrFamilies, Err) -> case inet:gethostbyname(Host, Family, Timeout) of {ok, HostEntry} -> Addrs = host_entry_to_addrs(HostEntry), AddrFamilies1 = [{Addr, Family} || Addr <- Addrs], do_lookup(HostFamilies, Timeout, AddrFamilies ++ AddrFamilies1, Err); {error, _} = Err1 -> do_lookup(HostFamilies, Timeout, AddrFamilies, Err1) end; do_lookup([], _Timeout, [], Err) -> Err; do_lookup([], _Timeout, AddrFamilies, _Err) -> {ok, AddrFamilies}. host_entry_to_addrs(#hostent{h_addr_list = AddrList}) -> lists:filter( fun(Addr) -> try get_addr_type(Addr) of _ -> true catch _:badarg -> false end end, AddrList). get_addr_type({_, _, _, _}) -> inet; get_addr_type({_, _, _, _, _, _, _, _}) -> inet6; get_addr_type(_) -> erlang:error(badarg). p1_pgsql-1.1.31/src/p1_pgsql.app.src0000664000175000017500000000264514731607742017532 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : p1_pgsql.app.src %%% Author : Evgeniy Khramtsov %%% Purpose : Application package description %%% Created : 4 Apr 2013 by Evgeniy Khramtsov %%% %%% %%% Copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%---------------------------------------------------------------------- {application, p1_pgsql, [{description, "PostgreSQL driver"}, {vsn, "1.1.31"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib, ssl, xmpp]}, {mod, {pgsql_app,[]}}, %% hex.pm packaging: {licenses, ["EPL 1.1", "Apache 2.0"]}, {links, [{"Github", "https://github.com/processone/p1_pgsql"}]}]}. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: p1_pgsql-1.1.31/src/pgsql_socket.erl0000664000175000017500000000541514731607742017714 0ustar debalancedebalance%%% File : pgsql_socket.erl %%% Author : Blah %%% Description : Unwrapping of TCP/SSL line protocol packages to postgres messages. %%% Created : 22 Jul 2005 -module(pgsql_socket). -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, sockmod, 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([{Mod, Sock}, ProtoPid, AsBin]) -> setopts({Mod, Sock}, [{active, once}]), {ok, #state{socket = Sock, sockmod = Mod, 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({Tag, Sock, Bin}, #state{socket = Sock, sockmod = Mod, protopid = ProtoPid, as_binary = AsBin, buffer = Buffer} = State) when Tag == tcp; Tag == ssl -> {ok, Rest} = process_buffer(ProtoPid, AsBin, <>), setopts({Mod, Sock}, [{active, once}]), {noreply, State#state{buffer = Rest}}; handle_info({Tag, Sock}, #state{socket = Sock, sockmod = Mod, protopid = ProtoPid} = State) when Tag == tcp_closed; Tag == ssl_closed -> io:format("Sock closed~n", []), ProtoPid ! {socket, {Mod, Sock}, closed}, {stop, tcp_close, State}; handle_info({Tag, Sock, Reason}, #state{socket = Sock, sockmod = Mod, protopid = ProtoPid} = State) when Tag == tcp_error; Tag == ssl_error -> io:format("Sock error~n", []), ProtoPid ! {socket, {Mod, 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}. setopts({gen_tcp, Sock}, Opts) -> inet:setopts(Sock, Opts); setopts({ssl, Sock}, Opts) -> ssl:setopts(Sock, Opts). p1_pgsql-1.1.31/src/pgsql_sup.erl0000664000175000017500000000360714731607742017234 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-2024 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 %%%=================================================================== p1_pgsql-1.1.31/src/pgsql.erl0000664000175000017500000000610714731607742016343 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, connect/6]). -export([squery/2, squery/3, 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(Host, Database, User, Password, Port, ConnectTimeout) -> connect([{database, Database}, {host, Host}, {user, User}, {port, Port}, {password, Password}, {connect_timeout, ConnectTimeout}]). 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). %%% squery(Db, Query, Timeout) -> {ok, Results} | ... no real error handling %%% Query = string() %%% Timeout = milliseconds() %%% Results = [Result] %%% Result = {"SELECT", RowDesc, ResultSet} | ... squery(Db, Query, Timeout) -> gen_server:call(Db, {squery, Query}, Timeout). %%% 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) -> gen_server:call(Db, {prepare, {Name, Query}}). %%% unprepare(Db, Name) -> ok | timeout | ... %%% Name = atom() unprepare(Db, Name) -> gen_server:call(Db, {unprepare, 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_list(Params) -> gen_server:call(Db, {execute, {Name, Params}}). p1_pgsql-1.1.31/Makefile0000664000175000017500000000021114731607742015350 0ustar debalancedebalanceREBAR ?= rebar all: src src: $(REBAR) get-deps $(REBAR) compile clean: $(REBAR) clean xref: all $(REBAR) xref .PHONY: clean src p1_pgsql-1.1.31/rebar.config.script0000664000175000017500000001174714731607742017515 0ustar debalancedebalance%%%---------------------------------------------------------------------- %%% File : rebar.config.script %%% Author : Mickael Remond %%% Purpose : Rebar build script. Compliant with rebar and rebar3. %%% Created : 24 Nov 2015 by Mickael Remond %%% %%% Copyright (C) 2002-2024 ProcessOne, SARL. All Rights Reserved. %%% %%% Licensed under the Apache License, Version 2.0 (the "License"); %%% you may not use this file except in compliance with the License. %%% You may obtain a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, software %%% distributed under the License is distributed on an "AS IS" BASIS, %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %%% See the License for the specific language governing permissions and %%% limitations under the License. %%% %%%---------------------------------------------------------------------- IsRebar3 = case application:get_key(rebar, vsn) of {ok, VSN} -> [VSN1 | _] = string:tokens(VSN, "-"), [Maj|_] = string:tokens(VSN1, "."), (list_to_integer(Maj) >= 3); undefined -> lists:keymember(mix, 1, application:loaded_applications()) end, ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) -> {OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of {value, {_, V1}, V2} -> {V1, V2}; false -> {if Tail == [] -> Default; true -> [] end, Cfg} end, case Tail of [] -> [{Key, Op(OldVal)} | PartCfg]; _ -> [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg] end end, ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end, ModCfgS = fun(Cfg, Keys, Val) -> ModCfg0(ModCfg0, Cfg, Keys, fun(_V) -> Val end, "") end, FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) -> F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail); (F, Cfg, [_ | Tail]) -> F(F, Cfg, Tail); (F, Cfg, []) -> Cfg end, AppendStr = fun(Append) -> fun("") -> Append; (Val) -> Val ++ " " ++ Append end end, AppendList = fun(Append) -> fun(Val) -> Val ++ Append end end, % Convert our rich deps syntax to rebar2 format: % https://github.com/rebar/rebar/wiki/Dependency-management Rebar2DepsFilter = fun(DepsList) -> lists:map(fun({DepName, _HexVersion, Source}) -> {DepName, ".*", Source} end, DepsList) end, % Convert our rich deps syntax to rebar3 version definition format: % https://rebar3.org/docs/configuration/dependencies/#dependency-version-handling % https://hexdocs.pm/elixir/Version.html Rebar3DepsFilter = fun(DepsList) -> lists:map(fun({DepName, HexVersion, {git, _, {tag, GitVersion}} = Source}) -> case HexVersion == ".*" of true -> {DepName, GitVersion}; false -> {DepName, HexVersion} end; ({DepName, _HexVersion, Source}) -> {DepName, ".*", Source} end, DepsList) end, GlobalDepsFilter = fun(Deps) -> DepNames = lists:map(fun({DepName, _, _}) -> DepName; ({DepName, _}) -> DepName end, Deps), lists:filtermap(fun(Dep) -> case code:lib_dir(Dep) of {error, _} -> {true,"Unable to locate dep '"++atom_to_list(Dep)++"' in system deps."}; _ -> false end end, DepNames) end, GithubConfig = case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of {"true", Token} when is_list(Token) -> CONFIG1 = [{coveralls_repo_token, Token}, {coveralls_service_job_id, os:getenv("GITHUB_RUN_ID")}, {coveralls_commit_sha, os:getenv("GITHUB_SHA")}, {coveralls_service_number, os:getenv("GITHUB_RUN_NUMBER")}], case os:getenv("GITHUB_EVENT_NAME") =:= "pull_request" andalso string:tokens(os:getenv("GITHUB_REF"), "/") of [_, "pull", PRNO, _] -> [{coveralls_service_pull_request, PRNO} | CONFIG1]; _ -> CONFIG1 end; _ -> [] end, Rules = [ {[deps], (not IsRebar3), Rebar2DepsFilter, []}, {[deps], IsRebar3, Rebar3DepsFilter, []}, {[plugins], IsRebar3, AppendList([pc]), []}, {[plugins], os:getenv("COVERALLS") == "true", AppendList([{coveralls, {git, "https://github.com/processone/coveralls-erl.git", {branch, "addjsonfile"}}} ]), []}, {[deps], os:getenv("USE_GLOBAL_DEPS") /= false, GlobalDepsFilter, []} ], Config = FilterConfig(FilterConfig, CONFIG, Rules) ++ GithubConfig, %io:format("Rules:~n~p~n~nCONFIG:~n~p~n~nConfig:~n~p~n", [Rules, CONFIG, Config]), Config. %% Local Variables: %% mode: erlang %% End: %% vim: set filetype=erlang tabstop=8: p1_pgsql-1.1.31/README.md0000664000175000017500000000137114731607742015177 0ustar debalancedebalance# p1_pgsql [![CI](https://github.com/processone/p1_pgsql/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/processone/p1_pgsql/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/processone/p1_pgsql/badge.svg?branch=master&service=github)](https://coveralls.io/github/processone/p1_pgsql?branch=master) [![Hex version](https://img.shields.io/hexpm/v/p1_pgsql.svg "Hex version")](https://hex.pm/packages/p1_pgsql) Pure Erlang PostgreSQL driver ## Building PostgreSQL driver can be build as follow: make It is a rebar-compatible OTP application. Alternatively, you can build it with rebar: rebar compile ## Development ### Test #### Unit test You can run eunit test with the command: $ rebar eunit p1_pgsql-1.1.31/CHANGELOG.md0000664000175000017500000000453514731607742015536 0ustar debalancedebalance# Version 1.1.31 * Updating xmpp to version 1.9.2. # Version 1.1.30 * Updating xmpp to version a1dd8d3ab94fd251f20598e6f002eba38905e218. # Version 1.1.29 * Updating xmpp to version 1.9.1. # Version 1.1.28 * Updating xmpp to version 1.9.0. # Version 1.1.27 * Updating xmpp to version 9728433608e25d196a49ed0f609208e353621135. # Version 1.1.26 * Updating xmpp to version 1.8.2. * Fix issues with building on OTP27 * Add support for unix domain sockets # Version 1.1.25 * Updating xmpp to version 1.8.1. # Version 1.1.24 * Updating xmpp to version 1.8.0. # Version 1.1.23 * Updating xmpp to version 1.7.0. # Version 1.1.22 * Fix version of xmpp # Version 1.1.21 * Updating xmpp to version 46b6c192d353e60e13a5cf8ed467c5696729b624. # Version 1.1.20 * Updating xmpp to version 1.6.1. # Version 1.1.19 * Updating xmpp to version 1.6.0. # Version 1.1.18 * Updating xmpp to version 1.5.8. # Version 1.1.17 * Updating xmpp to version 1.5.7. # Version 1.1.16 * Updating xmpp to version 1.5.6. # Version 1.1.15 * Fix 'make' compilation with rebar2 # Version 1.1.14 * Fix versioning and hex.pm docs publish # Version 1.1.13 * Add SASL auth support # Version 1.1.12 * Switch from using Travis to Github Actions as CI # Version 1.1.11 * Update copyright year to 2021 * recv_byte returns {ok, _} or throws an error, but never returns {error, _} # Version 1.1.10 * Fix Coveralls command call * Fix Travis setup using Rebar3 # Version 1.1.9 * Update copyright to 2020 # Version 1.1.8 * Update for hex.pm release # Version 1.1.7 * Add contribution guide # Version 1.1.6 * Add support for ipv6 connections # Version 1.1.5 * Fix compilation with rebar3 # Version 1.1.4 * Update coverall script # Version 1.1.3 * Make it possible to set connect timeout # Version 1.1.2 * Add SSL support (Evgeniy Khramtsov) # Version 1.1.1 * adding timeout to pgsql:squery (Felipe Ripoll) * Decode int4 and int8 as signed integers (Alexey Shchepin) * Better error handling in pgsql:squery (Alexey Shchepin) # Version 1.1.0 * Fixes 'prepare' and 'execute' calls (Alexey Shchepin) * Refactor (Alexey Shchepin) # Version 1.0.1 * Repository is now called p1_pgsql for consistency (Mickaël Rémond) * Initial release on Hex.pm (Mickaël Rémond) * Standard ProcessOne build chain (Mickaël Rémond) * Setup Travis-CI and test coverage, tests still needed (Mickaël Rémond) p1_pgsql-1.1.31/CODE_OF_CONDUCT.md0000664000175000017500000000643314731607742016523 0ustar debalancedebalance# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@process-one.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq p1_pgsql-1.1.31/CONTRIBUTING.md0000664000175000017500000001361314731607742016153 0ustar debalancedebalance# Contributing We'd love for you to contribute to our source code and to make our project even better than it is today! Here are the guidelines we'd like you to follow: * [Code of Conduct](#coc) * [Questions and Problems](#question) * [Issues and Bugs](#issue) * [Feature Requests](#feature) * [Issue Submission Guidelines](#submit) * [Pull Request Submission Guidelines](#submit-pr) * [Signing the CLA](#cla) ## Code of Conduct Help us keep our community open-minded and inclusive. Please read and follow our [Code of Conduct][coc]. ## Questions, Bugs, Features ### Got a Question or Problem? Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on dedicated support platforms, the best being [Stack Overflow][stackoverflow]. Stack Overflow is a much better place to ask questions since: - there are thousands of people willing to help on Stack Overflow - questions and answers stay available for public viewing so your question / answer might help someone else - Stack Overflow's voting system assures that the best answers are prominently visible. To save your and our time, we will systematically close all issues that are requests for general support and redirect people to the section you are reading right now. ### Found an Issue or Bug? If you find a bug in the source code, you can help us by submitting an issue to our [GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. ### Missing a Feature? You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. If you would like to implement a new feature then consider what kind of change it is: * **Major Changes** that you wish to contribute to the project should be discussed first in an [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. * **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr). ## Issue Submission Guidelines Before you submit your issue search the archive, maybe your question was already answered. If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to make it easier to understand and categorize the issue. ## Pull Request Submission Guidelines By submitting a pull request for a code or doc contribution, you need to have the right to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla] for details. Before you submit your pull request consider the following guidelines: * Search [GitHub][github-pr] for an open or closed Pull Request that relates to your submission. You don't want to duplicate effort. * Make your changes in a new git branch: ```shell git checkout -b my-fix-branch master ``` * Test your changes and, if relevant, expand the automated test suite. * Create your patch commit, including appropriate test cases. * If the changes affect public APIs, change or add relevant documentation. * Commit your changes using a descriptive commit message. ```shell git commit -a ``` Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. * Push your branch to GitHub: ```shell git push origin my-fix-branch ``` * In GitHub, send a pull request to `master` branch. This will trigger the continuous integration and run the test. We will also notify you if you have not yet signed the [contribution agreement][cla]. * If you find that the continunous integration has failed, look into the logs to find out if your changes caused test failures, the commit message was malformed etc. If you find that the tests failed or times out for unrelated reasons, you can ping a team member so that the build can be restarted. * If we suggest changes, then: * Make the required updates. * Test your changes and test cases. * Commit your changes to your branch (e.g. `my-fix-branch`). * Push the changes to your GitHub repository (this will update your Pull Request). You can also amend the initial commits and force push them to the branch. ```shell git rebase master -i git push origin my-fix-branch -f ``` This is generally easier to follow, but separate commits are useful if the Pull Request contains iterations that might be interesting to see side-by-side. That's it! Thank you for your contribution! ## Signing the Contributor License Agreement (CLA) Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done so before. It's a quick process, we promise, and you will be able to do it all online You can read [ProcessOne Contribution License Agreement][cla] in PDF. This is part of the legal framework of the open-source ecosystem that adds some red tape, but protects both the contributor and the company / foundation behind the project. It also gives us the option to relicense the code with a more permissive license in the future. [coc]: https://github.com/processone/p1_pgsql/blob/master/CODE_OF_CONDUCT.md [stackoverflow]: https://stackoverflow.com/ [github]: https://github.com/processone/p1_pgsql [github-issues]: https://github.com/processone/p1_pgsql/issues [github-new-issue]: https://github.com/processone/p1_pgsql/issues/new [github-pr]: https://github.com/processone/p1_pgsql/pulls [cla]: https://www.process-one.net/resources/ejabberd-cla.pdf [license]: https://github.com/processone/p1_pgsql/blob/master/EPLICENSE p1_pgsql-1.1.31/EPLICENSE0000664000175000017500000003336114731607742015156 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.''