Browse Source

first work

Ery Lee 12 years ago
parent
commit
d33882456b

+ 97 - 56
src/epgsql.erl

@@ -2,18 +2,34 @@
 
 
 -module(epgsql).
 -module(epgsql).
 
 
--export([select/2,
-         insert/3,
+-import(proplists, [get_value/2,
+                    get_value/3]).
+
+-export([connect/2]).
+
+-export([select/2, select/3, select/4,
+         insert/3, insert/4,
          update/4,
          update/4,
          delete/3]).
          delete/3]).
 
 
--export([connect/2]).
--export([get_parameter/2, squery/2, equery/2, equery/3]).
--export([parse/2, parse/3, parse/4, describe/2, describe/3]).
--export([bind/3, bind/4, execute/2, execute/3, execute/4]).
--export([close/2, close/3, sync/1]).
+-export([get_parameter/2,
+         squery/2,
+         equery/2, equery/3]).
+
+-export([parse/2, parse/3, parse/4,
+         describe/2, describe/3]).
+
+-export([bind/3, bind/4,
+         execute/2, execute/3, execute/4]).
+
+-export([close/2, close/3,
+         sync/1]).
+
 -export([with_transaction/2]).
 -export([with_transaction/2]).
 
 
+-export([escape/1,
+         escape_like/1]).
+
 -include("pgsql.hrl").
 -include("pgsql.hrl").
 
 
 %========================
 %========================
@@ -21,60 +37,75 @@
 %========================
 %========================
 connect(Pool, Opts) ->
 connect(Pool, Opts) ->
     {ok, C} = pgsql_conn:start_link(),
     {ok, C} = pgsql_conn:start_link(),
-    Host = proplists:get_value(host, Opts, "localhost"),
-    Username = proplists:get_value(username, Opts),
-    Password = proplists:get_value(password, Opts),
-    DB = proplists:get_value(database, Opts),
-    Timeout = proplists:get_value(timeout, Opts, 10000),
-    Port = proplists:get_value(port, Opts, 5432),
-    pgsql_conn:connect(C, Host, Username, Password,
-        [{database, DB}, {port, Port}, {timeout, Timeout}]),
+    Host = get_value(host, Opts, "localhost"),
+    Username = get_value(username, Opts),
+    Password = get_value(password, Opts),
+    PgOpts = [{database, get_value(database, Opts)},
+            {timeout, get_value(timeout, Opts, 10000)},
+            {port, get_value(port, Opts, 5432)}],
+    pgsql_conn:connect(C, Host, Username, Password, PgOpts),
     epgsql_pool:add_conn(Pool, C),
     epgsql_pool:add_conn(Pool, C),
     {ok, C}.
     {ok, C}.
 
 
 %========================
 %========================
 % api 
 % api 
 %========================
 %========================
-select(Pool, Table) ->
-    SQL = encode({select, Table}),
-    decode(squery(epgsql_pool:get_conn(Pool), SQL)).
+select(Pool, Table) when is_atom(Pool) ->
+    squery(Pool, epgsql_builder:select(Table)).
+
+select(Pool, Table, Fields) when is_list(Fields) ->
+    squery(Pool, epgsql_builder:select(Table, Fields));
+
+select(Pool, Table, Where) when is_tuple(Where) ->
+    squery(Pool, epgsql_builder:select(Table, Where)).
+
+select(Pool, Table, Fields, Where) 
+    when is_list(Fields) and is_tuple(Where) ->
+    squery(Pool, epgsql_builder:select(Table, Fields, Where)).
 
 
 insert(Pool, Table, Record) ->
 insert(Pool, Table, Record) ->
-    SQL = encode({insert, Table, Record}),
-    decode(squery(epgsql_pool:get_conn(Pool), SQL)).
+    squery(Pool, epgsql_builder:insert(Table, Record)).
+
+insert(Pool, Table, Fields, Values) ->
+    squery(Pool, epgsql_builder:insert(Table, Fields, Values)).
 
 
 update(Pool, Table, Record, Where) ->
 update(Pool, Table, Record, Where) ->
-    SQL = encode({update, Table, Record, Where}),
-    decode(squery(epgsql_pool:get_conn(Pool), SQL)).
+    squery(Pool, epgsql_builder:update(Table, Record, Where)).
 
 
 delete(Pool, Table, Where) ->
 delete(Pool, Table, Where) ->
-    SQL = encode({delete, Table, Where}),
-    decode(squery(epgsql_pool:get_conn(Pool), SQL)).
+    squery(Pool, epgsql_builder:delete(Table, Where)).
 
 
-get_parameter(C, Name) ->
-    pgsql_conn:get_parameter(C, Name).
-
-squery(C, Sql) ->
-    ok = pgsql_conn:squery(C, Sql),
-    case receive_results(C, []) of
+squery(Pool, Sql) ->
+    with_conn(Pool, fun(C) ->
+        ok = pgsql_conn:squery(C, Sql),
+        case receive_results(C, []) of
         [Result] -> Result;
         [Result] -> Result;
         Results  -> Results
         Results  -> Results
-    end.
+        end
+    end).
 
 
-equery(C, Sql) ->
-    equery(C, Sql, []).
-
-equery(C, Sql, Parameters) ->
-    case pgsql_conn:parse(C, "", Sql, []) of
-        {ok, #statement{types = Types} = S} ->
-            Typed_Parameters = lists:zip(Types, Parameters),
-            ok = pgsql_conn:equery(C, S, Typed_Parameters),
-            receive_result(C, undefined);
-        Error ->
-            Error
-    end.
+equery(Pool, Sql) ->
+    equery(Pool, Sql, []).
+
+equery(Pool, Sql, Parameters) ->
+    with_conn(Pool, fun(C) ->
+        case pgsql_conn:parse(C, "", Sql, []) of
+            {ok, #statement{types = Types} = S} ->
+                Typed_Parameters = lists:zip(Types, Parameters),
+                ok = pgsql_conn:equery(C, S, Typed_Parameters),
+                receive_result(C, undefined);
+            Error ->
+                Error
+        end
+    end).
 
 
-%% parse
+with_conn(Pool, Fun) ->
+    Fun(epgsql_pool:get_conn(Pool)).
+
+% api with connection
+
+get_parameter(C, Name) ->
+    pgsql_conn:get_parameter(C, Name).
 
 
 parse(C, Sql) ->
 parse(C, Sql) ->
     parse(C, "", Sql, []).
     parse(C, "", Sql, []).
@@ -134,19 +165,29 @@ with_transaction(C, F) ->
             {rollback, Why}
             {rollback, Why}
     end.
     end.
 
 
-%% -- internal functions --
+%% -- utility functions --
+
+escape_like(S) when is_list(S) ->
+    [escape_like(C) || C <- S];
+escape_like($%) -> "\\%";
+escape_like($_) -> "\\_";
+escape_like(C)  -> escape(C).
+
+%% Escape character that will confuse an SQL engine
+escape(S) when is_list(S) ->
+	[escape(C) || C <- S];
+%% Characters to escape
+escape($\0) -> "\\0";
+escape($\n) -> "\\n";
+escape($\t) -> "\\t";
+escape($\b) -> "\\b";
+escape($\r) -> "\\r";
+escape($')  -> "\\'";
+escape($")  -> "\\\"";
+escape($\\) -> "\\\\";
+escape(C)   -> C.
 
 
-decode(_) ->
-    ok.
-
-encode({select, _Table}) ->
-    "";
-encode({insert, _Table, _Record}) ->
-    "";
-encode({update, _Table, _Record, _Where}) ->
-    "";
-encode({delete, _Table, _Where}) ->
-    "".
+%% -- internal functions --
 
 
 receive_result(C, Result) ->
 receive_result(C, Result) ->
     try receive_result(C, [], []) of
     try receive_result(C, [], []) of

+ 152 - 0
src/epgsql_builder.erl

@@ -0,0 +1,152 @@
+-module(epgsql_builder).
+
+-import(io_lib, [format/2]).
+-import(list, [concat/1]).
+-import(string, [join/2]).
+
+-export([select/1, select/2, select/3]).
+-export([insert/2, insert/3]).
+-export([update/3]).
+-export([delete/1, delete/2]).
+-export([truncate/1]).
+
+select(Table) ->
+    binary(format("select * from ~s;", [Table])).
+
+select(Table, Fields) when is_list(Fields) ->
+    binary(format("select ~s from ~s;", [fields(Fields), Table]));
+
+select(Table, Where) when is_tuple(Where) ->
+    binary(format("select * from ~s where ~s;", [Table, where(Where)])).
+
+select(Table, Fields, Where) when is_list(Fields) and is_tuple(Where) ->
+    binary(format("select ~s from ~s where ~s;", 
+                   [fields(Fields), Table, where(Where)])).
+
+insert(Table, Record) ->
+	{Fields, Values} = lists:unzip(Record),
+    insert(Table, Fields, [Values]).
+
+insert(Table, Fields, Rows) -> 
+	Str = fun(Row) -> join([value(V) || V <- Row], ",") end,
+	Rows1 = [concat(["(", Str(Row), ")"]) || Row <- Rows],
+    binary(format("insert into ~s(~s) values~s;", 
+                  [Table, fields(Fields), join(Rows1, ",")])).
+
+update(Table, Record, Where) when is_list(Record) ->
+    Columns = join([concat([list(F), "=", value(V)]) 
+                    || {F, V} <- Record], ","),
+    binary(format("update ~s set ~s where ~s;", 
+                  [Table, Columns, where(Where)])).
+
+delete(Table) ->
+    binary(format("delete from ~s;", [Table])).
+
+delete(Table, Where) when is_tuple(Where) ->
+    binary(format("delete from ~s where ~s;", 
+                   [Table, where(Where)])).
+
+truncate(Table) ->
+    binary(format("truncate table ~s;", [Table])).
+
+where({'and', L, R}) ->
+	where(L) ++ " and " ++ where(R);
+
+where({'and', List}) when is_list(List) ->
+	string:join([where(E) || E <- List], " and ");
+
+where({'or', L, R}) ->
+	where(L) ++ " or " ++ where(R);
+
+where({'or', List}) when is_list(List) ->
+	string:join([where(E) || E <- List], " or ");
+
+where({like, Field, Value}) ->	
+	list(Field) ++ " like " ++ value(Value);
+
+where({'<', Field, Value}) ->	
+	atom_to_list(Field) ++ " < " ++ value(Value);
+
+where({'>', Field, Value}) ->	
+	atom_to_list(Field) ++ " > " ++ value(Value);
+
+where({'in', Field, Values}) ->	
+	InStr = string:join([value(Value) || Value <- Values], ","),
+	atom_to_list(Field) ++ " in (" ++ InStr ++ ")";
+
+where({Field, Value}) ->
+	atom_to_list(Field) ++ " = " ++ value(Value).
+
+fields(Fields) ->
+    string:join([list(F) || F <- Fields], " ,").
+
+value(Val) when Val == undefined; Val == null ->
+    "NULL";
+value(Val) when is_binary(Val) ->
+    quote(Val);
+value(Val) when is_atom(Val) ->
+    quote(atom_to_list(Val));
+value(Val) when is_list(Val) ->
+    quote(Val);
+value(Val) when is_integer(Val) ->
+    integer_to_list(Val);
+value(Val) when is_float(Val) ->
+    [Res] = io_lib:format("~w", [Val]),
+    Res;
+value({datetime, Val}) ->
+    value(Val);
+value({{Year, Month, Day}, {Hour, Minute, Second}}) ->
+    Res = two_digits([Year, Month, Day, Hour, Minute, Second]),
+    lists:flatten(Res);
+value({TimeType, Val})
+  when TimeType == 'date';
+       TimeType == 'time' ->
+    value(Val);
+value({Time1, Time2, Time3}) ->
+    Res = two_digits([Time1, Time2, Time3]),
+    lists:flatten(Res);
+value(Val) ->
+    {error, {unrecognized_value, Val}}.
+
+two_digits(Nums) when is_list(Nums) ->
+    [two_digits(Num) || Num <- Nums];
+two_digits(Num) ->
+    [Str] = io_lib:format("~b", [Num]),
+    case length(Str) of
+	1 -> [$0 | Str];
+	_ -> Str
+    end.
+
+%%  Quote a string or binary value so that it can be included safely in a query.
+quote(Bin) when is_binary(Bin) ->
+    quote(binary_to_list(Bin));
+quote(String) when is_list(String) ->
+    [39 | lists:reverse([39 | quote(String, [])])].
+
+quote([], Acc) ->
+    Acc;
+quote([0 | Rest], Acc) ->
+    quote(Rest, [$0, $\\ | Acc]);
+quote([10 | Rest], Acc) ->
+    quote(Rest, [$n, $\\ | Acc]);
+quote([13 | Rest], Acc) ->
+    quote(Rest, [$r, $\\ | Acc]);
+quote([$\\ | Rest], Acc) ->
+    quote(Rest, [$\\ , $\\ | Acc]);
+quote([39 | Rest], Acc) ->		%% 39 is $'
+    quote(Rest, [39, $\\ | Acc]);	%% 39 is $'
+quote([34 | Rest], Acc) ->		%% 34 is $"
+    quote(Rest, [34, $\\ | Acc]);	%% 34 is $"
+quote([26 | Rest], Acc) ->
+    quote(Rest, [$Z, $\\ | Acc]);
+quote([C | Rest], Acc) ->
+    quote(Rest, [C | Acc]).
+
+binary(L) when is_list(L) -> iolist_to_binary(L).
+
+list(L) when is_list(L) -> L;
+
+list(A) when is_atom(A) -> atom_to_list(A);
+
+list(B) when is_binary(B) -> binary_to_list(B).
+

+ 7 - 0
src/epgsql_pool.erl

@@ -4,6 +4,7 @@
 
 
 -export([start_link/1,
 -export([start_link/1,
          id/1,
          id/1,
+         all_conns/1,
          add_conn/2,
          add_conn/2,
          get_conn/1]).
          get_conn/1]).
 
 
@@ -25,6 +26,9 @@ id(Pool) when is_atom(Pool) ->
 start_link(Pool) ->
 start_link(Pool) ->
     gen_server:start_link({local, id(Pool)}, ?MODULE, [], [{timeout, infinity}]).
     gen_server:start_link({local, id(Pool)}, ?MODULE, [], [{timeout, infinity}]).
 
 
+all_conns(Pool) ->
+    gen_server:call(id(Pool), all_conns).
+
 get_conn(Pool) ->
 get_conn(Pool) ->
     case get(pgsql_conn) of
     case get(pgsql_conn) of
     undefined -> 
     undefined -> 
@@ -41,6 +45,9 @@ add_conn(Pool, Pid) ->
 init([]) ->
 init([]) ->
     {ok, #state { queue = queue:new() }}.
     {ok, #state { queue = queue:new() }}.
 
 
+handle_call(all_conns, _From, #state{queue = Q} = State) ->
+    {reply, queue:to_list(Q), State};
+
 handle_call(get_conn, _From, State = #state{queue = Q}) ->
 handle_call(get_conn, _From, State = #state{queue = Q}) ->
     case queue:out(Q) of
     case queue:out(Q) of
     {empty, _} ->
     {empty, _} ->

+ 0 - 18
test_data/epgsql.crt

@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC6jCCAlOgAwIBAgIJAKwIWpOFC5EWMA0GCSqGSIb3DQEBBQUAMGQxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp
-c2NvMRQwEgYDVQQKEwtsYW1iZGFXb3JrczESMBAGA1UEAxMJZXBnc3FsIENBMB4X
-DTA5MDMyNzA0MzAxM1oXDTE5MDIwMzA0MzAxM1owazELMAkGA1UEBhMCVVMxEzAR
-BgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNV
-BAoTC2xhbWJkYVdvcmtzMRkwFwYDVQQDFBBlcGdzcWxfdGVzdF9jZXJ0MIGfMA0G
-CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxnVk30edKqkIWWzTeWJHlc5Zco5MASAqz
-J6fC4HmR4y+StFB88NZE/ESKbWXNOD464fku72m5i4DI1NftGgZLVjjrFmLEE05S
-hymkqWtVb+H9RBD2SHl4VjwDUsMDHZGiXL3n02uWF+NtpeQHkacfav10ZQO0nnub
-njCUV3EHoQIDAQABo4GcMIGZMIGWBgNVHSMEgY4wgYuAFO5OzTWlO3ao7YAytIBW
-A3A1GXDaoWikZjBkMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
-MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEUMBIGA1UEChMLbGFtYmRhV29ya3MxEjAQ
-BgNVBAMTCWVwZ3NxbCBDQYIJAIFM8k/soL/qMA0GCSqGSIb3DQEBBQUAA4GBAHhy
-nE3/U1DiqHNf+AaCktVoHlsjfXvfrhouqR7T84kxCjAHzM6q183ga4S9NhLeyhCT
-oiDnIoUlUg7v1h9wTvXAoO2NwkCpSbIUncj6Q2h6J8obWq/BDSJwbdPcCHe4un8P
-hEpkiEK1QMMm5h9d+jgE8mrgGZXMnvzw40ovJHON
------END CERTIFICATE-----

+ 0 - 15
test_data/epgsql.key

@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQCxnVk30edKqkIWWzTeWJHlc5Zco5MASAqzJ6fC4HmR4y+StFB8
-8NZE/ESKbWXNOD464fku72m5i4DI1NftGgZLVjjrFmLEE05ShymkqWtVb+H9RBD2
-SHl4VjwDUsMDHZGiXL3n02uWF+NtpeQHkacfav10ZQO0nnubnjCUV3EHoQIDAQAB
-AoGARXSemvF+XPhPd6aa+gfwpaWZuwhMR+PkK0Lqm45ke+Q3ikrw3qrfX4K22tsE
-4EeKLkSHyQ7ebSxcZCy3c4SlyNES88wk7epGYbui4L0Iv/1WXfg1zIRqdNgBMr6M
-ZUZoUJx1gyRY2S3zGjTBn8b4Wh9EwsD0KTluvtH74DtLPQECQQDiLhIVasTsgKpn
-SoLVJ+UqpQ8oe17m4gHbwMOK2s+o479oKuAbmwgUX8U2waoncq06vG+x3gziVIOF
-Qkj6s6rZAkEAyQgfN01SoNOwp61Nis8TWeltqZdh0VHYqpu/ARfUpsTAWHGhc4eK
-Ee+J1DmxrUAP+s25Z640Ps9jNTugrWB2CQJBAJ+XyHTKQKdsZlC517VWEDLWAusa
-mi0pvgv0aUW5/Zr7EJ0M29M+wiW2Ke7oGgr5tNfkDKAhwU+WOLM3wUz8p4kCQQCr
-5zcSShtzDTINYCNjpElO5E3y7FEn9g4Jbd7550/fP3We66P8r5VAWw4IHUGy/Yns
-lIiLgSqJ3ztdZNy0BT1JAkEAhPz9yMZN7NBfdTjM1ebp4VirL8uQAdod/d3oRN87
-dqVxywRm4dZ4hMD2Fr6CuLsZylCQKEt1jNEfjZzRC4hR2g==
------END RSA PRIVATE KEY-----

+ 0 - 63
test_data/root.crt

@@ -1,63 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number:
-            81:4c:f2:4f:ec:a0:bf:ea
-        Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=US, ST=California, L=San Francisco, O=lambdaWorks, CN=epgsql CA
-        Validity
-            Not Before: Mar 27 03:52:34 2009 GMT
-            Not After : Feb  3 03:52:34 2019 GMT
-        Subject: C=US, ST=California, L=San Francisco, O=lambdaWorks, CN=epgsql CA
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-            RSA Public Key: (1024 bit)
-                Modulus (1024 bit):
-                    00:bd:03:59:e5:ce:5d:24:45:ae:bf:cd:a4:4a:d4:
-                    33:7a:48:08:79:8a:20:4c:b6:28:51:f8:f0:9a:1d:
-                    1e:fb:b8:de:a3:b7:10:95:d7:a3:58:b7:94:b4:7e:
-                    36:0a:0c:68:1c:e8:21:a5:5d:9d:0a:3a:5d:26:dd:
-                    bb:5b:62:59:e0:1f:b8:48:a7:3d:28:dd:f3:b9:de:
-                    27:d7:25:4b:f6:8a:ac:ef:a3:0e:b3:fb:1b:b8:dd:
-                    db:01:72:01:1f:79:5b:f8:c3:54:7e:1a:94:68:1d:
-                    81:2c:05:11:05:2c:5b:81:05:21:19:c0:c7:94:4f:
-                    77:f5:76:4c:98:8d:ab:68:5b
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Subject Key Identifier: 
-                EE:4E:CD:35:A5:3B:76:A8:ED:80:32:B4:80:56:03:70:35:19:70:DA
-            X509v3 Authority Key Identifier: 
-                keyid:EE:4E:CD:35:A5:3B:76:A8:ED:80:32:B4:80:56:03:70:35:19:70:DA
-                DirName:/C=US/ST=California/L=San Francisco/O=lambdaWorks/CN=epgsql CA
-                serial:81:4C:F2:4F:EC:A0:BF:EA
-
-            X509v3 Basic Constraints: 
-                CA:TRUE
-    Signature Algorithm: sha1WithRSAEncryption
-        27:4c:04:ee:27:46:23:9b:6f:7c:8f:5b:9e:c6:65:74:33:40:
-        06:be:ca:e0:55:91:1c:9e:1c:77:27:82:03:4e:67:91:5d:14:
-        e4:74:b7:88:9e:49:d6:02:5b:71:94:b3:62:2a:5e:58:00:7d:
-        8c:42:09:db:ca:27:20:71:33:16:09:d2:17:36:d4:4f:63:09:
-        0a:48:80:d7:36:13:24:57:e3:7a:7e:25:4e:b8:f0:71:c6:34:
-        69:4e:e1:4b:5a:ec:b3:be:14:78:1e:af:85:b2:56:91:62:03:
-        6b:b2:85:2e:8e:ef:4b:5a:bf:ac:54:43:24:cb:0e:c6:f8:58:
-        b5:a1
------BEGIN CERTIFICATE-----
-MIIDEDCCAnmgAwIBAgIJAIFM8k/soL/qMA0GCSqGSIb3DQEBBQUAMGQxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp
-c2NvMRQwEgYDVQQKEwtsYW1iZGFXb3JrczESMBAGA1UEAxMJZXBnc3FsIENBMB4X
-DTA5MDMyNzAzNTIzNFoXDTE5MDIwMzAzNTIzNFowZDELMAkGA1UEBhMCVVMxEzAR
-BgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNV
-BAoTC2xhbWJkYVdvcmtzMRIwEAYDVQQDEwllcGdzcWwgQ0EwgZ8wDQYJKoZIhvcN
-AQEBBQADgY0AMIGJAoGBAL0DWeXOXSRFrr/NpErUM3pICHmKIEy2KFH48JodHvu4
-3qO3EJXXo1i3lLR+NgoMaBzoIaVdnQo6XSbdu1tiWeAfuEinPSjd87neJ9clS/aK
-rO+jDrP7G7jd2wFyAR95W/jDVH4alGgdgSwFEQUsW4EFIRnAx5RPd/V2TJiNq2hb
-AgMBAAGjgckwgcYwHQYDVR0OBBYEFO5OzTWlO3ao7YAytIBWA3A1GXDaMIGWBgNV
-HSMEgY4wgYuAFO5OzTWlO3ao7YAytIBWA3A1GXDaoWikZjBkMQswCQYDVQQGEwJV
-UzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEU
-MBIGA1UEChMLbGFtYmRhV29ya3MxEjAQBgNVBAMTCWVwZ3NxbCBDQYIJAIFM8k/s
-oL/qMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJ0wE7idGI5tvfI9b
-nsZldDNABr7K4FWRHJ4cdyeCA05nkV0U5HS3iJ5J1gJbcZSzYipeWAB9jEIJ28on
-IHEzFgnSFzbUT2MJCkiA1zYTJFfjen4lTrjwccY0aU7hS1rss74UeB6vhbJWkWID
-a7KFLo7vS1q/rFRDJMsOxvhYtaE=
------END CERTIFICATE-----

+ 0 - 15
test_data/root.key

@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQC9A1nlzl0kRa6/zaRK1DN6SAh5iiBMtihR+PCaHR77uN6jtxCV
-16NYt5S0fjYKDGgc6CGlXZ0KOl0m3btbYlngH7hIpz0o3fO53ifXJUv2iqzvow6z
-+xu43dsBcgEfeVv4w1R+GpRoHYEsBREFLFuBBSEZwMeUT3f1dkyYjatoWwIDAQAB
-AoGBAKg3UUyayn47nfiJtgv6qw3LCe/RJEnhXCUIHmmqPSbeMxcVF6ej0HZme+ve
-34012XrQhRE9LUQrCThL4jDEaSLsZ64PY+XL0ZdNCS4RX6OHGp6EyHC1HNSHn8a2
-zQuAzBsBHM39h/EVid9m0acfcEuN7TYAKF+sH6qjEBiSAWdRAkEA5hnwRsecGht6
-ViW4uuwHadNrc19mMPpXxFtIb79ONH+FmUkSQ0pRNOkEVICC7yokZbnhxcxSb76k
-r3S7rDa8xQJBANJJgpzuxbF0/NTXl5aH1gcucpIp6XBJfRmn1DpFq3Y20qGPr+Ez
-SiiDaqxoYjYRQ6FJg26kWnonWPawsiXSIp8CQQDQuQazra10ISi/rEf9hszSuezm
-IstX8j5a51K1yxrtlB9kBFyEnY08KYK8BDbBK8EIZaze95BvvMc2QPVcKerhAkB+
-Qh7HBOHz827eiHd+rR5Hf47QzZNYlPck0UyulCgnuTDsSi5qw3XSL118GMxm9CSs
-EUx1wP6F+1wB+gNsi+e3AkAR39uESbaaVOZmh1Uvvz0RVckXlJOEPY8Rp6kxhFS2
-QBsWbMrb5jraFy54iCmj8o3stp+LjBBv4PFA0LKq4vIa
------END RSA PRIVATE KEY-----

+ 0 - 80
test_data/test_schema.sql

@@ -1,80 +0,0 @@
--- script to create test schema for epgsql unit tests --
---
--- this script should be run as the same user the tests will be run as,
--- so that the test for connecting as the 'current user' succeeds
---
--- the following lines must be added to pg_hba.conf for all tests to
--- succeed:
---
--- host    epgsql_test_db1 epgsql_test             127.0.0.1/32    trust
--- host    epgsql_test_db1 epgsql_test_md5         127.0.0.1/32    md5
--- host    epgsql_test_db1 epgsql_test_cleartext   127.0.0.1/32    password
--- hostssl epgsql_test_db1 epgsql_test_cert        127.0.0.1/32    cert
---
--- any 'trust all' must be commented out for the invalid password test
--- to succeed.
---
--- ssl support must be configured, and the sslinfo contrib module
--- loaded for the ssl tests to succeed.
-
-
-CREATE USER epgsql_test;
-CREATE USER epgsql_test_md5 WITH PASSWORD 'epgsql_test_md5';
-CREATE USER epgsql_test_cleartext WITH PASSWORD 'epgsql_test_cleartext';
-CREATE USER epgsql_test_cert;
-
-CREATE DATABASE epgsql_test_db1 WITH ENCODING 'UTF8';
-CREATE DATABASE epgsql_test_db2 WITH ENCODING 'UTF8';
-
-GRANT ALL ON DATABASE epgsql_test_db1 to epgsql_test;
-GRANT ALL ON DATABASE epgsql_test_db1 to epgsql_test_md5;
-GRANT ALL ON DATABASE epgsql_test_db1 to epgsql_test_cleartext;
-GRANT ALL ON DATABASE epgsql_test_db2 to epgsql_test;
-
-\c epgsql_test_db1;
-
-CREATE TABLE test_table1 (id integer primary key, value text);
-
-INSERT INTO test_table1 (id, value) VALUES (1, 'one');
-INSERT INTO test_table1 (id, value) VALUES (2, 'two');
-
-CREATE TABLE test_table2 (
-  c_bool bool,
-  c_char char,  
-  c_int2 int2,
-  c_int4 int4,
-  c_int8 int8,
-  c_float4 float4,
-  c_float8 float8,
-  c_bytea bytea,
-  c_text text,
-  c_varchar varchar(64),
-  c_date date,
-  c_time time,
-  c_timetz timetz,
-  c_timestamp timestamp,
-  c_timestamptz timestamptz,
-  c_interval interval);
-
-CREATE LANGUAGE plpgsql;
-
-CREATE OR REPLACE FUNCTION insert_test1(_id integer, _value text)
-returns integer
-as $$
-begin
-  insert into test_table1 (id, value) values (_id, _value);
-  return _id;
-end
-$$ language plpgsql;
-
-CREATE OR REPLACE FUNCTION do_nothing()
-returns void
-as $$
-begin
-end
-$$ language plpgsql;
-
-GRANT ALL ON TABLE test_table1 TO epgsql_test;
-GRANT ALL ON TABLE test_table2 TO epgsql_test;
-GRANT ALL ON FUNCTION insert_test1(integer, text) TO epgsql_test;
-GRANT ALL ON FUNCTION do_nothing() TO epgsql_test;

+ 0 - 0
test_ebin/.empty


+ 0 - 675
test_src/pgsql_tests.erl

@@ -1,675 +0,0 @@
--module(pgsql_tests).
-
--export([run_tests/0]).
-
--include_lib("eunit/include/eunit.hrl").
--include_lib("public_key/include/public_key.hrl").
--include("pgsql.hrl").
-
--define(host, "localhost").
--define(port, 5432).
-
--define(ssl_apps, [crypto, public_key, ssl]).
-
-connect_test() ->
-    connect_only([]).
-
-connect_to_db_test() ->
-    connect_only([{database, "epgsql_test_db1"}]).
-
-connect_as_test() ->
-    connect_only(["epgsql_test", [{database, "epgsql_test_db1"}]]).
-
-connect_with_cleartext_test() ->
-    connect_only(["epgsql_test_cleartext",
-                  "epgsql_test_cleartext",
-                  [{database, "epgsql_test_db1"}]]).
-
-connect_with_md5_test() ->
-    connect_only(["epgsql_test_md5",
-                  "epgsql_test_md5",
-                  [{database, "epgsql_test_db1"}]]).
-
-connect_with_invalid_user_test() ->
-    {error, invalid_authorization_specification} =
-        pgsql:connect(?host,
-                      "epgsql_test_invalid",
-                      "epgsql_test_invalid",
-                      [{port, ?port}, {database, "epgsql_test_db1"}]).
-
-connect_with_invalid_password_test() ->
-    {error, Why} =
-        pgsql:connect(?host,
-                      "epgsql_test_md5",
-                      "epgsql_test_invalid",
-                      [{port, ?port}, {database, "epgsql_test_db1"}]),
-    case Why of
-        invalid_authorization_specification -> ok; % =< 8.4
-        invalid_password                    -> ok  % >= 9.0
-    end.
-
-
-connect_with_ssl_test() ->
-    lists:foreach(fun application:start/1, ?ssl_apps),
-    with_connection(
-      fun(C) ->
-              {ok, _Cols, [{true}]} = pgsql:equery(C, "select ssl_is_used()")
-      end,
-      "epgsql_test",
-      [{ssl, true}]).
-
-connect_with_client_cert_test() ->
-    lists:foreach(fun application:start/1, ?ssl_apps),
-    Dir = filename:join(filename:dirname(code:which(pgsql_tests)), "../test_data"),
-    File = fun(Name) -> filename:join(Dir, Name) end,
-    {ok, Pem} = file:read_file(File("epgsql.crt")),
-    [{'Certificate', Der, not_encrypted}] = public_key:pem_decode(Pem),
-    Cert = public_key:pkix_decode_cert(Der, plain),
-    #'TBSCertificate'{serialNumber = Serial} = Cert#'Certificate'.tbsCertificate,
-    Serial2 = list_to_binary(integer_to_list(Serial)),
-
-    with_connection(
-      fun(C) ->
-              {ok, _, [{true}]} = pgsql:equery(C, "select ssl_is_used()"),
-              {ok, _, [{Serial2}]} = pgsql:equery(C, "select ssl_client_serial()")
-      end,
-      "epgsql_test_cert",
-      [{ssl, true}, {keyfile, File("epgsql.key")}, {certfile, File("epgsql.crt")}]).
-
-select_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, Cols, Rows} = pgsql:squery(C, "select * from test_table1"),
-              [#column{name = <<"id">>, type = int4, size = 4},
-               #column{name = <<"value">>, type = text, size = -1}] = Cols,
-              [{<<"1">>, <<"one">>}, {<<"2">>, <<"two">>}] = Rows
-      end).
-
-insert_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 1} = pgsql:squery(C, "insert into test_table1 (id, value) values (3, 'three')")
-      end).
-
-update_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 1} = pgsql:squery(C, "insert into test_table1 (id, value) values (3, 'three')"),
-              {ok, 1} = pgsql:squery(C, "insert into test_table1 (id, value) values (4, 'four')"),
-              {ok, 2} = pgsql:squery(C, "update test_table1 set value = 'foo' where id > 2"),
-              {ok, _, [{<<"2">>}]} = pgsql:squery(C, "select count(*) from test_table1 where value = 'foo'")
-      end).
-
-delete_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 1} = pgsql:squery(C, "insert into test_table1 (id, value) values (3, 'three')"),
-              {ok, 1} = pgsql:squery(C, "insert into test_table1 (id, value) values (4, 'four')"),
-              {ok, 2} = pgsql:squery(C, "delete from test_table1 where id > 2"),
-              {ok, _, [{<<"2">>}]} = pgsql:squery(C, "select count(*) from test_table1")
-      end).
-
-create_and_drop_table_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, [], []} = pgsql:squery(C, "create table test_table3 (id int4)"),
-              {ok, [#column{type = int4}], []} = pgsql:squery(C, "select * from test_table3"),
-              {ok, [], []} = pgsql:squery(C, "drop table test_table3")
-      end).
-
-cursor_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, [], []} = pgsql:squery(C, "begin"),
-              {ok, [], []} = pgsql:squery(C, "declare c cursor for select id from test_table1"),
-              {ok, 2} = pgsql:squery(C, "move forward 2 from c"),
-              {ok, 1} = pgsql:squery(C, "move backward 1 from c"),
-              {ok, 1, _Cols, [{<<"2">>}]} = pgsql:squery(C, "fetch next from c"),
-              {ok, [], []} = pgsql:squery(C, "close c")
-      end).
-
-multiple_result_test() ->
-    with_connection(
-      fun(C) ->
-              [{ok, _, [{<<"1">>}]}, {ok, _, [{<<"2">>}]}] = pgsql:squery(C, "select 1; select 2"),
-              [{ok, _, [{<<"1">>}]}, {error, #error{}}] = pgsql:squery(C, "select 1; select foo;")
-      end).
-
-extended_select_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, Cols, Rows} = pgsql:equery(C, "select * from test_table1", []),
-              [#column{name = <<"id">>, type = int4, size = 4},
-               #column{name = <<"value">>, type = text, size = -1}] = Cols,
-              [{1, <<"one">>}, {2, <<"two">>}] = Rows
-      end).
-
-extended_sync_ok_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, _Cols, [{<<"one">>}]} = pgsql:equery(C, "select value from test_table1 where id = $1", [1]),
-              {ok, _Cols, [{<<"two">>}]} = pgsql:equery(C, "select value from test_table1 where id = $1", [2])
-      end).
-
-extended_sync_error_test() ->
-    with_connection(
-      fun(C) ->
-              {error, #error{}} = pgsql:equery(C, "select _alue from test_table1 where id = $1", [1]),
-              {ok, _Cols, [{<<"one">>}]} = pgsql:equery(C, "select value from test_table1 where id = $1", [1])
-      end).
-
-returning_from_insert_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 1, _Cols, [{3}]} = pgsql:equery(C, "insert into test_table1 (id) values (3) returning id")
-      end).
-
-returning_from_update_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 2, _Cols, [{1}, {2}]} = pgsql:equery(C, "update test_table1 set value = 'hi' returning id")
-      end).
-
-returning_from_delete_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, 2, _Cols, [{1}, {2}]} = pgsql:equery(C, "delete from test_table1 returning id")
-      end).
-
-parse_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select * from test_table1"),
-              [#column{name = <<"id">>}, #column{name = <<"value">>}] = S#statement.columns,
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-parse_column_format_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select 1::int4, false::bool, 2.0::float4"),
-              [#column{type = int4},
-               #column{type = bool},
-               #column{type = float4}] = S#statement.columns,
-              ok = pgsql:bind(C, S, []),
-              {ok, [{1, false, 2.0}]} = pgsql:execute(C, S, 0),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-parse_error_test() ->
-    with_connection(
-      fun(C) ->
-              {error, #error{}} = pgsql:parse(C, "select _ from test_table1"),
-              {ok, S} = pgsql:parse(C, "select * from test_table1"),
-              [#column{name = <<"id">>}, #column{name = <<"value">>}] = S#statement.columns,
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-parse_and_close_test() ->
-    with_connection(
-      fun(C) ->
-              Parse = fun() -> pgsql:parse(C, "test", "select * from test_table1", []) end,
-              {ok, S} = Parse(),
-              {error, #error{code = <<"42P05">>}} = Parse(),
-              pgsql:close(C, S),
-              {ok, S} = Parse(),
-              ok = pgsql:sync(C)
-      end).
-
-bind_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select value from test_table1 where id = $1"),
-              ok = pgsql:bind(C, S, [1]),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-bind_parameter_format_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select $1, $2, $3", [int2, text, bool]),
-              [int2, text, bool] = S#statement.types,
-              ok = pgsql:bind(C, S, [1, "hi", true]),
-              {ok, [{1, <<"hi">>, true}]} = pgsql:execute(C, S, 0),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-bind_error_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select $1::char"),
-              {error, #error{}} = pgsql:bind(C, S, [0]),
-              ok = pgsql:bind(C, S, [$A]),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-bind_and_close_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select * from test_table1"),
-              ok = pgsql:bind(C, S, "one", []),
-              {error, #error{code = <<"42P03">>}} = pgsql:bind(C, S, "one", []),
-              ok = pgsql:close(C, portal, "one"),
-              ok = pgsql:bind(C, S, "one", []),
-              ok = pgsql:sync(C)
-      end).
-
-execute_error_test() ->
-    with_connection(
-      fun(C) ->
-          {ok, S} = pgsql:parse(C, "insert into test_table1 (id, value) values ($1, $2)"),
-          ok = pgsql:bind(C, S, [1, <<"foo">>]),
-          {error, #error{code = <<"23505">>}} = pgsql:execute(C, S, 0),
-          {error, sync_required} = pgsql:bind(C, S, [3, <<"quux">>]),
-          ok = pgsql:sync(C),
-          ok = pgsql:bind(C, S, [3, <<"quux">>]),
-          {ok, _} = pgsql:execute(C, S, 0),
-          {ok, 1} = pgsql:squery(C, "delete from test_table1 where id = 3")
-      end).
-
-describe_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select * from test_table1"),
-              [#column{name = <<"id">>}, #column{name = <<"value">>}] = S#statement.columns,
-              {ok, S} = pgsql:describe(C, S),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-describe_with_param_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select id from test_table1 where id = $1"),
-              [int4] = S#statement.types,
-              [#column{name = <<"id">>}] = S#statement.columns,
-              {ok, S} = pgsql:describe(C, S),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-describe_named_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "name", "select * from test_table1", []),
-              [#column{name = <<"id">>}, #column{name = <<"value">>}] = S#statement.columns,
-              {ok, S} = pgsql:describe(C, S),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-describe_error_test() ->
-    with_connection(
-      fun(C) ->
-              {error, #error{}} = pgsql:describe(C, statement, ""),
-              {ok, S} = pgsql:parse(C, "select * from test_table1"),
-              {ok, S} = pgsql:describe(C, statement, ""),
-              ok = pgsql:sync(C)
-
-      end).
-
-portal_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select value from test_table1"),
-              ok = pgsql:bind(C, S, []),
-              {partial, [{<<"one">>}]} = pgsql:execute(C, S, 1),
-              {partial, [{<<"two">>}]} = pgsql:execute(C, S, 1),
-              {ok, []} = pgsql:execute(C, S,1),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-returning_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "update test_table1 set value = $1 returning id"),
-              ok = pgsql:bind(C, S, ["foo"]),
-              {ok, 2, [{1}, {2}]} = pgsql:execute(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-multiple_statement_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S1} = pgsql:parse(C, "one", "select value from test_table1 where id = 1", []),
-              ok = pgsql:bind(C, S1, []),
-              {partial, [{<<"one">>}]} = pgsql:execute(C, S1, 1),
-              {ok, S2} = pgsql:parse(C, "two", "select value from test_table1 where id = 2", []),
-              ok = pgsql:bind(C, S2, []),
-              {partial, [{<<"two">>}]} = pgsql:execute(C, S2, 1),
-              {ok, []} = pgsql:execute(C, S1, 1),
-              {ok, []} = pgsql:execute(C, S2, 1),
-              ok = pgsql:close(C, S1),
-              ok = pgsql:close(C, S2),
-              ok = pgsql:sync(C)
-      end).
-
-multiple_portal_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select value from test_table1 where id = $1"),
-              ok = pgsql:bind(C, S, "one", [1]),
-              ok = pgsql:bind(C, S, "two", [2]),
-              {ok, [{<<"one">>}]} = pgsql:execute(C, S, "one", 0),
-              {ok, [{<<"two">>}]} = pgsql:execute(C, S, "two", 0),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end).
-
-execute_function_test() ->
-    with_rollback(
-      fun(C) ->
-              {ok, _Cols1, [{3}]} = pgsql:equery(C, "select insert_test1(3, 'three')"),
-              {ok, _Cols2, [{<<>>}]} = pgsql:equery(C, "select do_nothing()")
-      end).
-
-parameter_get_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, <<"off">>} = pgsql:get_parameter(C, "is_superuser")
-      end).
-
-parameter_set_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, [], []} = pgsql:squery(C, "set DateStyle = 'ISO, MDY'"),
-              {ok, <<"ISO, MDY">>} = pgsql:get_parameter(C, "DateStyle"),
-              {ok, _Cols, [{<<"2000-01-02">>}]} = pgsql:squery(C, "select '2000-01-02'::date"),
-              {ok, [], []} = pgsql:squery(C, "set DateStyle = 'German'"),
-              {ok, <<"German, DMY">>} = pgsql:get_parameter(C, "DateStyle"),
-              {ok, _Cols, [{<<"02.01.2000">>}]} = pgsql:squery(C, "select '2000-01-02'::date")
-      end).
-
-numeric_type_test() ->
-    check_type(int2, "1", 1, [0, 256, -32768, +32767]),
-    check_type(int4, "1", 1, [0, 512, -2147483648, +2147483647]),
-    check_type(int8, "1", 1, [0, 1024, -9223372036854775808, +9223372036854775807]),
-    check_type(float4, "1.0", 1.0, [0.0, 1.23456, -1.23456]),
-    check_type(float8, "1.0", 1.0, [0.0, 1.23456789012345, -1.23456789012345]).
-
-character_type_test() ->
-    Alpha = unicode:characters_to_binary([16#03B1]),
-    Ka    = unicode:characters_to_binary([16#304B]),
-    One   = unicode:characters_to_binary([16#10D360]),
-    check_type(bpchar, "'A'", $A, [1, $1, 16#7F, Alpha, Ka, One], "c_char"),
-    check_type(text, "'hi'", <<"hi">>, [<<"">>, <<"hi">>]),
-    check_type(varchar, "'hi'", <<"hi">>, [<<"">>, <<"hi">>]).
-
-date_time_type_test() ->
-    with_connection(
-      fun(C) ->
-              case pgsql:get_parameter(C, "integer_datetimes") of
-                  {ok, <<"on">>}  -> MaxTsDate = 294276;
-                  {ok, <<"off">>} -> MaxTsDate = 5874897
-              end,
-
-              check_type(date, "'2008-01-02'", {2008,1,2}, [{-4712,1,1}, {5874897,1,1}]),
-              check_type(time, "'00:01:02'", {0,1,2.0}, [{0,0,0.0}, {24,0,0.0}]),
-              check_type(timetz, "'00:01:02-01'", {{0,1,2.0},1*60*60},
-                         [{{0,0,0.0},0}, {{24,0,0.0},-13*60*60}]),
-              check_type(timestamp, "'2008-01-02 03:04:05'", {{2008,1,2},{3,4,5.0}},
-                         [{{-4712,1,1},{0,0,0.0}}, {{MaxTsDate,12,31}, {23,59,59.0}}]),
-              check_type(interval, "'1 hour 2 minutes 3.1 seconds'", {{1,2,3.1},0,0},
-                         [{{0,0,0.0},0,-178000000 * 12}, {{0,0,0.0},0,178000000 * 12}])
-      end).
-
-misc_type_test() ->
-    check_type(bool, "true", true, [true, false]),
-    check_type(bytea, "E'\001\002'", <<1,2>>, [<<>>, <<0,128,255>>]).
-
-array_type_test() ->
-    with_connection(
-      fun(C) ->
-          {ok, _, [{[1, 2]}]} = pgsql:equery(C, "select ($1::int[])[1:2]", [[1, 2, 3]]),
-          Select = fun(Type, A) ->
-                       Query = "select $1::" ++ atom_to_list(Type) ++ "[]",
-                       {ok, _Cols, [{A2}]} = pgsql:equery(C, Query, [A]),
-                       case lists:all(fun({V, V2}) -> compare(Type, V, V2) end, lists:zip(A, A2)) of
-                           true  -> ok;
-                           false -> ?assertMatch(A, A2)
-                       end
-                   end,
-          Select(int2,   []),
-          Select(int2,   [1, 2, 3, 4]),
-          Select(int2,   [[1], [2], [3], [4]]),
-          Select(int2,   [[[[[[1, 2]]]]]]),
-          Select(bool,   [true]),
-          Select(char,   [$a, $b, $c]),
-          Select(int4,   [[1, 2]]),
-          Select(int8,   [[[[1, 2]], [[3, 4]]]]),
-          Select(text,   [<<"one">>, <<"two>">>]),
-          Select(float4, [0.0, 1.0, 0.123]),
-          Select(float8, [0.0, 1.0, 0.123])
-      end).
-
-text_format_test() ->
-    with_connection(
-      fun(C) ->
-              Select = fun(Type, V) ->
-                               V2 = list_to_binary(V),
-                               Query = "select $1::" ++ Type,
-                               {ok, _Cols, [{V2}]} = pgsql:equery(C, Query, [V]),
-                               {ok, _Cols, [{V2}]} = pgsql:equery(C, Query, [V2])
-                       end,
-              Select("inet", "127.0.0.1"),
-              Select("numeric", "123456")
-      end).
-
-connect_timeout_test() ->
-    {error, timeout} = pgsql:connect(?host, [{port, ?port}, {timeout, 0}]).
-
-query_timeout_test() ->
-    with_connection(
-      fun(C) ->
-              {error, timeout} = pgsql:squery(C, "select pg_sleep(1)"),
-              {error, timeout} = pgsql:equery(C, "select pg_sleep(2)"),
-              {ok, _Cols, [{1}]} = pgsql:equery(C, "select 1")
-      end,
-      [{timeout, 10}]).
-
-execute_timeout_test() ->
-    with_connection(
-      fun(C) ->
-              {ok, S} = pgsql:parse(C, "select pg_sleep($1)"),
-              ok = pgsql:bind(C, S, [2]),
-              {error, timeout} = pgsql:execute(C, S, 0),
-              ok = pgsql:bind(C, S, [0]),
-              {ok, [{<<>>}]} = pgsql:execute(C, S, 0),
-              ok = pgsql:close(C, S),
-              ok = pgsql:sync(C)
-      end,
-      [{timeout, 10}]).
-
-connection_closed_test() ->
-    P = self(),
-    F = fun() ->
-                process_flag(trap_exit, true),
-                {ok, C} = pgsql:connect(?host, [{port, ?port}]),
-                P ! {connected, C},
-                receive
-                    Any -> P ! Any
-                end
-        end,
-    spawn_link(F),
-    receive
-        {connected, C} ->
-            timer:sleep(100),
-            pgsql:close(C),
-            {'EXIT', C, _} = receive R -> R end
-    end,
-    flush().
-
-active_connection_closed_test() ->
-    P = self(),
-    F = fun() ->
-                process_flag(trap_exit, true),
-                {ok, C} = pgsql:connect(?host, [{port, ?port}]),
-                P ! {connected, C},
-                R = pgsql:squery(C, "select pg_sleep(10)"),
-                P ! R
-        end,
-    spawn_link(F),
-    receive
-        {connected, C} ->
-            timer:sleep(100),
-            pgsql:close(C),
-            {error, closed} = receive R -> R end
-    end,
-    flush().
-
-warning_notice_test() ->
-    with_connection(
-      fun(C) ->
-          Q = "create function pg_temp.raise() returns void as $$
-               begin
-                 raise warning 'oops';
-               end;
-               $$ language plpgsql;
-               select pg_temp.raise()",
-          [{ok, _, _}, _] = pgsql:squery(C, Q),
-          receive
-              {pgsql, C, {notice, #error{message = <<"oops">>}}} -> ok
-          after
-              100 -> erlang:error(didnt_receive_notice)
-          end
-      end,
-      [{async, self()}]).
-
-listen_notify_test() ->
-    with_connection(
-      fun(C) ->
-          {ok, [], []}     = pgsql:squery(C, "listen epgsql_test"),
-          {ok, _, [{Pid}]} = pgsql:equery(C, "select pg_backend_pid()"),
-          {ok, [], []}     = pgsql:squery(C, "notify epgsql_test"),
-          receive
-              {pgsql, C, {notification, <<"epgsql_test">>, Pid, <<>>}} -> ok
-          after
-              100 -> erlang:error(didnt_receive_notification)
-          end
-      end,
-      [{async, self()}]).
-
-listen_notify_payload_test() ->
-    with_min_version(
-      9.0,
-      fun(C) ->
-          {ok, [], []}     = pgsql:squery(C, "listen epgsql_test"),
-          {ok, _, [{Pid}]} = pgsql:equery(C, "select pg_backend_pid()"),
-          {ok, [], []}     = pgsql:squery(C, "notify epgsql_test, 'test!'"),
-          receive
-              {pgsql, C, {notification, <<"epgsql_test">>, Pid, <<"test!">>}} -> ok
-          after
-              100 -> erlang:error(didnt_receive_notification)
-          end
-      end,
-      [{async, self()}]).
-
-application_test() ->
-    lists:foreach(fun application:start/1, ?ssl_apps),
-    ok = application:start(epgsql).
-
-%% -- run all tests --
-
-run_tests() ->
-    Files = filelib:wildcard("test_ebin/*tests.beam"),
-    Mods = [list_to_atom(filename:basename(F, ".beam")) || F <- Files],
-    eunit:test(Mods, []).
-
-%% -- internal functions --
-
-connect_only(Args) ->
-    TestOpts = [{port, ?port}],
-    case Args of
-        [User, Opts]       -> Args2 = [User, TestOpts ++ Opts];
-        [User, Pass, Opts] -> Args2 = [User, Pass, TestOpts ++ Opts];
-        Opts               -> Args2 = [TestOpts ++ Opts]
-    end,
-    {ok, C} = apply(pgsql, connect, [?host | Args2]),
-    pgsql:close(C),
-    flush().
-
-with_connection(F) ->
-    with_connection(F, "epgsql_test", []).
-
-with_connection(F, Args) ->
-    with_connection(F, "epgsql_test", Args).
-
-with_connection(F, Username, Args) ->
-    Args2 = [{port, ?port}, {database, "epgsql_test_db1"} | Args],
-    {ok, C} = pgsql:connect(?host, Username, Args2),
-    try
-        F(C)
-    after
-        pgsql:close(C)
-    end,
-    flush().
-
-with_rollback(F) ->
-    with_connection(
-      fun(C) ->
-              try
-                  pgsql:squery(C, "begin"),
-                  F(C)
-                  after
-                      pgsql:squery(C, "rollback")
-                  end
-      end).
-
-with_min_version(Min, F, Args) ->
-    with_connection(
-      fun(C) ->
-          {ok, Bin} = pgsql:get_parameter(C, <<"server_version">>),
-          {ok, [{float, 1, Ver} | _], _} = erl_scan:string(binary_to_list(Bin)),
-          case Ver >= Min of
-              true  -> F(C);
-              false -> ?debugFmt("skipping test requiring PostgreSQL >= ~.2f~n", [Min])
-          end
-      end,
-      Args).
-
-check_type(Type, In, Out, Values) ->
-    Column = "c_" ++ atom_to_list(Type),
-    check_type(Type, In, Out, Values, Column).
-
-check_type(Type, In, Out, Values, Column) ->
-    with_connection(
-      fun(C) ->
-              Select = io_lib:format("select ~s::~w", [In, Type]),
-              {ok, [#column{type = Type}], [{Out}]} = pgsql:equery(C, Select),
-              Sql = io_lib:format("insert into test_table2 (~s) values ($1) returning ~s", [Column, Column]),
-              {ok, #statement{columns = [#column{type = Type}]} = S} = pgsql:parse(C, Sql),
-              Insert = fun(V) ->
-                               pgsql:bind(C, S, [V]),
-                               {ok, 1, [{V2}]} = pgsql:execute(C, S),
-                               case compare(Type, V, V2) of
-                                   true  -> ok;
-                                   false -> ?debugFmt("~p =/= ~p~n", [V, V2]), ?assert(false)
-                               end,
-                               ok = pgsql:sync(C)
-                       end,
-              lists:foreach(Insert, [null | Values])
-      end).
-
-compare(_Type, null, null) -> true;
-compare(float4, V1, V2)    -> abs(V2 - V1) < 0.000001;
-compare(float8, V1, V2)    -> abs(V2 - V1) < 0.000000000000001;
-compare(_Type, V1, V2)     -> V1 =:= V2.
-
-%% flush mailbox
-flush() ->
-    ?assertEqual([], flush([])).
-
-flush(Acc) ->
-    receive
-        {'EXIT', _Pid, normal} -> flush(Acc);
-        M                      -> flush([M | Acc])
-    after
-        0 -> lists:reverse(Acc)
-    end.