%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2006-2022. 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.
%%
%% %CopyrightEnd%
%%

%%

-module(postgres).

%% Note: This directive should only be used in test suites.
-compile(export_all).

%-------------------------------------------------------------------------
connection_string() ->
    case os:type() of
	{unix, sunos} ->
	    "DSN=Postgres;UID=odbctest";
	{unix, linux} ->
	    Size = erlang:system_info({wordsize, external}),
	    linux_dist_connection_string(Size)  
    end.

linux_dist_connection_string(4) ->
    case linux_dist() of
	"ubuntu" ->
	    "DSN=PostgresLinuxUbuntu;UID=odbctest";  
	_ ->  
	    "DSN=PostgresLinux;UID=odbctest"
    end;

linux_dist_connection_string(_) ->
    case linux_dist() of
	"ubuntu" ->
	    "DSN=PostgresLinux64Ubuntu;UID=odbctest";
	_ ->
	    "DSN=PostgresLinux64;UID=odbctest"
    end.
			
linux_dist() ->
    case file:read_file("/etc/issue") of
	{ok, Binary} ->
	    [Dist | _ ] = string:tokens(binary_to_list(Binary), " "),
	    string:to_lower(Dist);
	{error, _} ->
	    other
    end.
	
	  
%-------------------------------------------------------------------------
insert_result() ->
    {selected,["id","data"],[{1,"bar"}]}.

update_result() ->
    {selected,["id","data"],[{1,"foo"}]}.

selected_ID(N, next) ->
    {selected,["id"],[{N}]};

selected_ID(_, _) ->
    {error, driver_does_not_support_function}.

selected_next_N(1)->
    {selected,["id"],
     [{1},
      {2},
      {3}]};

selected_next_N(2)->
    {selected,["id"],
     [{4},
      {5}]}.

selected_relative_N(_)->
    {error, driver_does_not_support_function}.

selected_absolute_N(_)->
    {error, driver_does_not_support_function}.

selected_list_rows() ->
    {selected,["id", "data"],[[1, "bar"],[2,"foo"]]}.
    
first_list_rows() ->
    {error, driver_does_not_support_function}.
last_list_rows() ->
    {error, driver_does_not_support_function}.
prev_list_rows() ->
    {error, driver_does_not_support_function}.
next_list_rows() ->
    {selected,["id","data"],[[1,"bar"]]}.

%% In case we get a better postgres driver that support this some day .....
multiple_select()->
    [{selected,["id", "data"],[{1, "bar"},{2, "foo"}]},
     {selected,["id"],[{"foo"}]}].

multiple_mix()->
    [{updated, 1},{updated, 1},
     {selected,["id", "data"],[{1, "foobar"},{2, "foo"}]},
     {updated, 1}, {selected,["data"],[{"foo"}]}].

%-------------------------------------------------------------------------
fixed_char_min() ->
    1.
fixed_char_max() ->
    2000. 

create_fixed_char_table(Size) ->
    " (FIELD char(" ++ integer_to_list(Size) ++ "))".

%-------------------------------------------------------------------------
var_char_min() ->
    1.
var_char_max() ->
    2000. 

create_var_char_table(Size) ->
    " (FIELD varchar(" ++ integer_to_list(Size) ++ "))".

%-------------------------------------------------------------------------
text_min() ->
    1.
text_max() ->
   2147483646. % 2147483647. %% 2^31 - 1 

create_text_table() ->
    " (FIELD text)". 

%-------------------------------------------------------------------------
create_timestamp_table() ->
    " (FIELD TIMESTAMP)". 

%-------------------------------------------------------------------------
small_int_min() ->
    -32768.
small_int_max() ->
    32767.

create_small_int_table() ->
     " (FIELD smallint)".

small_int_min_selected() ->
    {selected,["field"],[{-32768}]}.

small_int_max_selected() ->
    {selected,["field"], [{32767}]}.

%-------------------------------------------------------------------------
int_min() ->
   -2147483648.
int_max() ->
    2147483647.

create_int_table() ->
     " (FIELD int)".

int_min_selected() ->
    {selected,["field"],[{-2147483648}]}.

int_max_selected() ->
    {selected,["field"], [{2147483647}]}.

%-------------------------------------------------------------------------
big_int_min() ->
    -9223372036854775808.
   
big_int_max() ->
    9223372036854775807.

create_big_int_table() ->
     " (FIELD bigint )".

big_int_min_selected() ->
    {selected,["field"], [{"-9223372036854775808"}]}.

big_int_max_selected() ->
    {selected,["field"], [{"9223372036854775807"}]}.

%-------------------------------------------------------------------------
bit_false() ->
    0.
bit_true() ->
    1.

create_bit_table() ->
     " (FIELD bit)".

bit_false_selected() ->
    {selected,["field"],[{"0"}]}.

bit_true_selected() ->
    {selected,["field"], [{"1"}]}.

%-------------------------------------------------------------------------
float_min() ->
    5.0e-324.
float_max() ->
    1.79e+308.

create_float_table() ->
    " (FIELD float)".

float_underflow() ->
    "2.4e-324".
float_overflow() ->
    "1.80e+308".

float_zero_selected() ->
    {selected,["field"],[{0.00000e+0}]}.

%-------------------------------------------------------------------------
real_min() ->
    -3.40e+38.
real_max() ->
    3.40e+38.

real_underflow() ->
    "-3.41e+38".

real_overflow() ->
    "3.41e+38".

create_real_table() ->
    " (FIELD real)".

real_zero_selected() ->
    {selected,["field"],[{0.00000e+0}]}.

%-------------------------------------------------------------------------
param_select_small_int() ->
    {selected,["field"],[{1}, {2}]}.

param_select_int() ->
    Int = small_int_max() + 1,
    {selected,["field"],[{1}, {Int}]}.

param_select_decimal() ->
    {selected,["field"],[{1},{2}]}.

param_select_numeric() ->
    {selected,["field"],[{1},{2}]}.

param_select_float() ->
    {selected,["field"],[{1.30000},{1.20000}]}.

param_select_real() ->
    {selected,["field"],[{1.30000},{1.20000}]}.

param_select_double() ->
    {selected,["field"],[{1.30000},{1.20000}]}.

param_select_mix() ->
    {selected,["id","data"],[{1, "foo"}, {2, "bar"}]}.

param_update() ->
    {selected,["id","data"],[{3, "baz"},{1, "foobar"}, {2, "foobar"}]}.

param_delete() ->
    {selected,["id","data"],[{3, "baz"}]}.

param_select() ->
    {selected,["id","data"],[{1, "foo"},{3, "foo"}]}.

%-------------------------------------------------------------------------
describe_integer() ->
    {ok,[{"myint1",sql_smallint},
	 {"myint2",sql_integer},
	 {"myint3",sql_integer}]}.

describe_string() ->
    {ok,[{"str1",{sql_char,10}},                           
	 {"str2",{sql_char,10}},
	 {"str3",{sql_varchar,10}},
	 {"str4",{sql_varchar,10}}]}.

describe_floating() ->
    {ok,[{"f",sql_real},{"r",sql_real},{"d",{sql_float,17}}]}.
describe_dec_num() ->
    {ok,[{"mydec",{sql_numeric,9,3}},{"mynum",{sql_numeric,9,2}}]}.

describe_timestamp() ->
    {ok, [{"field", sql_timestamp}]}.

%-------------------------------------------------------------------------
drop_proc() ->
    "drop function test_proc1(OUT integer, OUT integer);".

stored_proc_integer_out() ->
    "create or replace FUNCTION test_proc1(" ++
        "OUT int_a INTEGER, " ++
        "OUT int_b INTEGER) " ++
        "AS $$ " ++
        "BEGIN " ++
        " int_a := 123; " ++
        " int_b := 456; " ++
        "END " ++
        "$$ LANGUAGE plpgsql ".

%% This does not test what you might think it is supposed to test.
%% Since the stored procedure has got 2 out parameters and no
%% in parameters it is of arity 0 as called below.
%%
%% The port program odbcserver.c will marshal these out parameters
%% and hand them to ODBC. The ODBC driver for postgres will
%% apparently not give a hoot about these out parameters and instead
%% return the result in a regular result select set. The port program
%% will assume it has the result in the out parameters and marshal
%% these as they are i.e as it itself had packed them, so they
%% come back unchanged.
%%
%% The real function result goes into the void but the code in odbcserver.c
%% that marshals out parameters returned from ODBC will be run
%% so that is what this test tests...
%%
param_query(Ref) ->
    odbc:param_query(Ref, "select * from test_proc1()",
                     [{sql_integer, out, [111]},
                      {sql_integer, out, [444]}]).

query_result() ->
    {executed, 2, [{111, 444}]}.
