From bb92b63e5203e85291601e6756407238bc677894 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 28 May 2010 08:18:06 +0000 Subject: odbc: Relese test suites --- lib/odbc/test/Makefile | 114 +++ lib/odbc/test/README | 86 ++ lib/odbc/test/odbc.dynspec | 31 + lib/odbc/test/odbc.spec | 9 + lib/odbc/test/odbc.spec.win | 5 + lib/odbc/test/odbc_connect_SUITE.erl | 816 +++++++++++++++++ lib/odbc/test/odbc_data_type_SUITE.erl | 1498 ++++++++++++++++++++++++++++++++ lib/odbc/test/odbc_query_SUITE.erl | 1453 +++++++++++++++++++++++++++++++ lib/odbc/test/odbc_start_SUITE.erl | 147 ++++ lib/odbc/test/odbc_test.hrl | 37 + lib/odbc/test/odbc_test_lib.erl | 77 ++ lib/odbc/test/oracle.erl | 246 ++++++ lib/odbc/test/postgres.erl | 294 +++++++ lib/odbc/test/sqlserver.erl | 298 +++++++ 14 files changed, 5111 insertions(+) create mode 100644 lib/odbc/test/Makefile create mode 100644 lib/odbc/test/README create mode 100644 lib/odbc/test/odbc.dynspec create mode 100644 lib/odbc/test/odbc.spec create mode 100644 lib/odbc/test/odbc.spec.win create mode 100644 lib/odbc/test/odbc_connect_SUITE.erl create mode 100644 lib/odbc/test/odbc_data_type_SUITE.erl create mode 100644 lib/odbc/test/odbc_query_SUITE.erl create mode 100644 lib/odbc/test/odbc_start_SUITE.erl create mode 100644 lib/odbc/test/odbc_test.hrl create mode 100644 lib/odbc/test/odbc_test_lib.erl create mode 100644 lib/odbc/test/oracle.erl create mode 100644 lib/odbc/test/postgres.erl create mode 100644 lib/odbc/test/sqlserver.erl diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile new file mode 100644 index 0000000000..935ecbf5a7 --- /dev/null +++ b/lib/odbc/test/Makefile @@ -0,0 +1,114 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# +# 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 online 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. +# +# %CopyrightEnd% +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + + +INCLUDES= -I. -I$(ERL_TOP)/lib/test_server/include/ -I$(ERL_TOP)/lib/odbc/src + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + odbc_start_SUITE \ + odbc_connect_SUITE \ + odbc_query_SUITE \ + odbc_data_type_SUITE \ + odbc_test_lib \ + oracle \ + sqlserver \ + postgres + +EBIN = . + +ERL_FILES= $(MODULES:%=%.erl) + +HRL_FILES= odbc_test.hrl\ + +TARGET_FILES= \ + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +SPEC_FILES = odbc.spec odbc.dynspec \ + odbc.spec.win + +EMAKEFILE = Emakefile +MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile) + +ifeq ($(MAKE_EMAKE),) +BUILDTARGET = $(TARGET_FILES) +RELTEST_FILES = $(SPEC_FILES) $(SOURCE) +else +BUILDTARGET = emakebuild +RELTEST_FILES = $(EMAKEFILE) $(SPEC_FILES) $(SOURCE) +endif + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/odbc_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_COMPILE_FLAGS += $(INCLUDES) \ + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +tests debug opt: $(BUILDTARGET) + +targets: $(TARGET_FILES) + +.PHONY: emakebuild + +emakebuild: $(EMAKEFILE) + +$(EMAKEFILE): + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE) + $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE) + +clean: + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: opt + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) + + +release_docs_spec: + + + + + + + diff --git a/lib/odbc/test/README b/lib/odbc/test/README new file mode 100644 index 0000000000..1f3c659e28 --- /dev/null +++ b/lib/odbc/test/README @@ -0,0 +1,86 @@ +------------------------------------------------------------------------- + TEST SUITE REQUIREMENTS +------------------------------------------------------------------------- +As third party products are involved when using ODBC you will have to +setup your own test environment to be able to run the ODBC test +suites. + +You need to install a database such as postgres, sql-server, oracle +etc, and ODBC-drivers for that database. + +Then you need to setup a test database, however you do not +need to create any tables that will be done by the test suites. +The test suites will also remove all tables that it creates when +the test is complete. + +------------------------------------------------------------------------- +ERLANG FILES YOU MAY NEED TO CHANGE +------------------------------------------------------------------------- + +A remote database management system has a callback module to handle +possible differences in data type handling etc, the callback module +also defines the ODBC connection string. Currently available callback +modules are postgres.erl, sqlserver.erl and oracle.erl. Depending on +how you set things up you might want to edit the connection string in +the callback module or even add your own callback module. + +The callback module used in each test case is defined by the ?RDBMS +macro defined in odbc_test.hrl so you might need to change this to +suite your purposes. + +------------------------------------------------------------------------- +EXAMPLE +------------------------------------------------------------------------- + +As an example say we have the database odbctestdb, with +the user odbctest that has the password Sesame. The database +runs on the host myhost. + +UINX/LINUX +----------- + +Set up a database and install the unixODBC drivers. +Then the unix/linux user that should run the test suits needs an .odbc.ini +file to map connection data. For example ODBC connection string: +"DSN=Postgres;UID=odbctest" will need an .odbc.ini entry that looks +something like this: + +--- Start example of .odbc.ini ---- + +[Postgres] +Driver=/usr/lib/psqlodbc.so +Description=Postgres driver +ServerName=myhost +Database=odbctestdb +Port=5432 +LogonID=odbctest +Password=Sesame + +---End example of .odbc.ini ------------ + + +WINDOWS MOST FLAVORS +-------------------- + +There will be a "ODBC data source administrator" tool under +Control Panel -> Administrative Tools, use this to set up +your database. Choose to connect with SQL Server authentication. +As odbc connection string use: "DSN=odbctestdb;UID=odbctest;PWD=Sesame" + + +> %CopyrightBegin% +> +> Copyright Ericsson AB 2010. All Rights Reserved. +> +> 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 online 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. +> +> %CopyrightEnd% diff --git a/lib/odbc/test/odbc.dynspec b/lib/odbc/test/odbc.dynspec new file mode 100644 index 0000000000..bb15edceed --- /dev/null +++ b/lib/odbc/test/odbc.dynspec @@ -0,0 +1,31 @@ +%% -*- erlang -*- +%% You can test this file using this command. +%% file:script("odbc.dynspec", [{'Os',"Unix"}]). + +Exists = +fun() -> + case code:lib_dir(odbc) of + {error,bad_name} -> + false; + P -> + %% Make sure that the odbc directory really + %% contains the application (and not only documentation). + case filelib:is_file(filename:join(P, "ebin/odbc.beam")) of + false -> false; + true -> + %% We know that we don't have any odbc libraries + %% installed on this computer. + {ok,Host} = inet:gethostname(), + Host =/= "netsim200" + end + end +end, +case Exists() of + false -> + NoOdbc = "No odbc application", + [{skip, {odbc_connect_SUITE, NoOdbc}}, + {skip, {odbc_data_type_SUITE, NoOdbc}}, + {skip, {odbc_query_SUITE, NoOdbc}}]; + true -> + [] +end. diff --git a/lib/odbc/test/odbc.spec b/lib/odbc/test/odbc.spec new file mode 100644 index 0000000000..acba9f8d98 --- /dev/null +++ b/lib/odbc/test/odbc.spec @@ -0,0 +1,9 @@ +{topcase, {dir, "../odbc_test"}}. +{skip, {odbc_data_type_SUITE, varchar_upper_limit, "Known bug in database"}}. +{skip, {odbc_data_type_SUITE, text_upper_limit, "Consumes too much resources"}}. +{skip, {odbc_data_type_SUITE, bit_true , "Not supported by driver"}}. +{skip, {odbc_data_type_SUITE, bit_false, "Not supported by driver"}}. +{skip, {odbc_query_SUITE, multiple_select_result_sets,"Not supported by driver"}}. +{skip, {odbc_query_SUITE, multiple_mix_result_sets, "Not supported by driver"}}. +{skip, {odbc_query_SUITE, multiple_result_sets_error, "Not supported by driver"}}. +{skip, {odbc_query_SUITE, param_insert_tiny_int, "Not supported by driver"}}. \ No newline at end of file diff --git a/lib/odbc/test/odbc.spec.win b/lib/odbc/test/odbc.spec.win new file mode 100644 index 0000000000..1fd349d2c3 --- /dev/null +++ b/lib/odbc/test/odbc.spec.win @@ -0,0 +1,5 @@ +{topcase, {dir, "../odbc_test"}}. +{skip, {odbc_data_type_SUITE, big_int_lower_limit, "Not supported by sqlserver 7.0"}}. +{skip, {odbc_data_type_SUITE, big_int_upper_limit, "Not supported by sqlserver7.0"}}. +{skip, {odbc_data_type_SUITE, text_upper_limit, "Consumes too much resources"}}. + diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl new file mode 100644 index 0000000000..4d37a8f543 --- /dev/null +++ b/lib/odbc/test/odbc_connect_SUITE.erl @@ -0,0 +1,816 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(odbc_connect_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). +-include("odbc_test.hrl"). + +-define(MAX_SEQ_TIMEOUTS, 10). + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- +all(doc) -> + ["Tests the ability to connect and disconnet to/from the database"]; +all(suite) -> + case odbc_test_lib:odbc_check() of + ok -> all(); + Other -> {skip, Other} + end. + +all() -> + [not_exist_db, commit, rollback, not_explicit_commit, + no_c_node, port_dies, control_process_dies, client_dies, + connect_timeout, timeout, many_timeouts, timeout_reset, + disconnect_on_timeout, connection_closed, + disable_scrollable_cursors, return_rows_as_lists, api_missuse]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + application:start(odbc), + case odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]) of + {ok, Ref} -> + odbc:disconnect(Ref), + [{tableName, odbc_test_lib:unique_table_name()} | Config]; + _ -> + {skip, "ODBC is not properly setup"} + end. +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + application:stop(odbc), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + Dog = test_server:timetrap(?default_timeout), + Temp = lists:keydelete(connection_ref, 1, Config), + NewConfig = lists:keydelete(watchdog, 1, Temp), + [{watchdog, Dog} | NewConfig]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + %% Clean up if needed + Table = ?config(tableName, Config), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), + io:format("Drop table: ~p ~p~n", [Table, Result]), + odbc:disconnect(Ref), + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +commit(doc)-> + ["Test the use of explicit commit"]; +commit(suite) -> []; +commit(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + + Table = ?config(tableName, Config), + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1,'bar')"), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + ok = odbc:commit(Ref, commit), + UpdateResult = ?RDBMS:update_result(), + UpdateResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'bar' WHERE ID = 1"), + ok = odbc:commit(Ref, commit, ?TIMEOUT), + InsertResult = ?RDBMS:insert_result(), + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT', {function_clause, _}} = + (catch odbc:commit(Ref, commit, -1)), + + ok = odbc:disconnect(Ref), + + ok. +%%------------------------------------------------------------------------- + +rollback(doc)-> + ["Test the use of explicit rollback"]; +rollback(suite) -> []; +rollback(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + ok = odbc:commit(Ref, commit), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + ok = odbc:commit(Ref, rollback), + InsertResult = ?RDBMS:insert_result(), + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + ok = odbc:commit(Ref, rollback, ?TIMEOUT), + InsertResult = ?RDBMS:insert_result(), + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + + {'EXIT', {function_clause, _}} = + (catch odbc:commit(Ref, rollback, -1)), + + ok = odbc:disconnect(Ref), + ok. + +%%------------------------------------------------------------------------- +not_explicit_commit(doc) -> + ["Test what happens if you try using commit on a auto_commit connection."]; +not_explicit_commit(suite) -> []; +not_explicit_commit(_Config) -> + {ok, Ref} = + odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}]), + {error, _} = odbc:commit(Ref, commit), + ok = odbc:disconnect(Ref), + ok. + +%%------------------------------------------------------------------------- +not_exist_db(doc) -> + ["Tests valid data format but invalid data in the connection parameters."]; +not_exist_db(suite) -> []; +not_exist_db(_Config) -> + {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", []), + %% So that the odbc control server can be stoped "in the correct way" + test_server:sleep(100), + ok. + +%%------------------------------------------------------------------------- +no_c_node(doc) -> + "Test what happens if the port-program can not be found"; +no_c_node(suite) -> []; +no_c_node(_Config) -> + process_flag(trap_exit, true), + Dir = filename:nativename(filename:join(code:priv_dir(odbc), + "bin")), + FileName1 = filename:nativename(os:find_executable("odbcserver", + Dir)), + FileName2 = filename:nativename(filename:join(Dir, "odbcsrv")), + ok = file:rename(FileName1, FileName2), + Result = + case catch odbc:connect(?RDBMS:connection_string(), []) of + {error, port_program_executable_not_found} -> + ok; + Else -> + Else + end, + + ok = file:rename(FileName2, FileName1), + ok = Result. +%%------------------------------------------------------------------------ + +port_dies(doc) -> + "Tests what happens if the port program dies"; +port_dies(suite) -> []; +port_dies(_Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {status, _} = process_info(Ref, status), + process_flag(trap_exit, true), + Port = lists:last(erlang:ports()), + exit(Port, kill), + %% Wait for exit_status from port 5000 ms (will not get a exit + %% status in this case), then wait a little longer to make sure + %% the port and the controlprocess has had time to terminate. + test_server:sleep(7000), + undefined = process_info(Ref, status), + ok. + +%%------------------------------------------------------------------------- +control_process_dies(doc) -> + "Tests what happens if the Erlang control process dies"; +control_process_dies(suite) -> []; +control_process_dies(_Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + process_flag(trap_exit, true), + Port = lists:last(erlang:ports()), + {connected, Ref} = erlang:port_info(Port, connected), + exit(Ref, kill), + test_server:sleep(100), + undefined = erlang:port_info(Port, connected), + %% Check for c-program still running, how? + ok. + +%%------------------------------------------------------------------------- +client_dies(doc) -> + ["Test that the odbc process is terminated when the client process " + "dies"]; +client_dies(suite) -> + [client_dies_normal, client_dies_timeout, client_dies_error]. + +%%------------------------------------------------------------------------- +client_dies_normal(doc) -> + ["Client dies with reason normal."]; +client_dies_normal(suite) -> []; +client_dies_normal(Config) when is_list(Config) -> + Pid = spawn(?MODULE, client_normal, [self()]), + + MonitorReference = + receive + {dbRef, Ref} -> + MRef = erlang:monitor(process, Ref), + Pid ! continue, + MRef + end, + + receive + {'DOWN', MonitorReference, _Type, _Object, _Info} -> + ok + after 5000 -> + test_server:fail(control_process_not_stopped) + end. + +client_normal(Pid) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + Pid ! {dbRef, Ref}, + receive + continue -> + ok + end, + exit(self(), normal). + + +%%------------------------------------------------------------------------- +client_dies_timeout(doc) -> + ["Client dies with reason timeout."]; +client_dies_timeout(suite) -> []; +client_dies_timeout(Config) when is_list(Config) -> + Pid = spawn(?MODULE, client_timeout, [self()]), + + MonitorReference = + receive + {dbRef, Ref} -> + MRef = erlang:monitor(process, Ref), + Pid ! continue, + MRef + end, + + receive + {'DOWN', MonitorReference, _Type, _Object, _Info} -> + ok + after 5000 -> + test_server:fail(control_process_not_stopped) + end. + +client_timeout(Pid) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + Pid ! {dbRef, Ref}, + receive + continue -> + ok + end, + exit(self(), timeout). + + +%%------------------------------------------------------------------------- +client_dies_error(doc) -> + ["Client dies with reason error."]; +client_dies_error(suite) -> []; +client_dies_error(Config) when is_list(Config) -> + Pid = spawn(?MODULE, client_error, [self()]), + + MonitorReference = + receive + {dbRef, Ref} -> + MRef = erlang:monitor(process, Ref), + Pid ! continue, + MRef + end, + + receive + {'DOWN', MonitorReference, _Type, _Object, _Info} -> + ok + after 5000 -> + test_server:fail(control_process_not_stopped) + end. + +client_error(Pid) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + Pid ! {dbRef, Ref}, + receive + continue -> + ok + end, + exit(self(), error). + + +%%------------------------------------------------------------------------- +connect_timeout(doc) -> + ["Test the timeout for the connect function."]; +connect_timeout(suite) -> []; +connect_timeout(Config) when is_list(Config) -> + {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(), + [{timeout, 0}])), + ok. +%%------------------------------------------------------------------------- +timeout(doc) -> + ["Test that timeouts don't cause unwanted behavior sush as receiving" + " an anwser to a previously tiemed out query."]; +timeout(suite) -> []; +timeout(Config) when is_list(Config) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + ok = odbc:commit(Ref, commit), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"), + + Pid = spawn_link(?MODULE, update_table_timeout, [Table, 5000, self()]), + + receive + timout_occurred -> + ok = odbc:commit(Ref, commit), + Pid ! continue + end, + + receive + altered -> + ok + end, + + {selected, Fields, [{"foobar"}]} = + odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"), + ["DATA"] = odbc_test_lib:to_upper(Fields), + + ok = odbc:commit(Ref, commit), + ok = odbc:disconnect(Ref), + ok. + + +update_table_timeout(Table, TimeOut, Pid) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", + + case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of + {'EXIT', timeout} -> + Pid ! timout_occurred; + {updated, 1} -> + test_server:fail(database_locker_failed) + end, + + receive + continue -> + ok + end, + + %% Make sure we receive the correct result and not the answer + %% to the previous query. + {selected, Fields, [{"baz"}]} = + odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), + ["DATA"] = odbc_test_lib:to_upper(Fields), + + {updated, 1} = odbc:sql_query(Ref, UpdateQuery, TimeOut), + + ok = odbc:commit(Ref, commit), + + Pid ! altered, + + ok = odbc:disconnect(Ref), + + ok. +%%------------------------------------------------------------------------- +many_timeouts(doc) -> + ["Tests that many consecutive timeouts lead to that the connection " + "is shutdown."]; +many_timeouts(suite) -> []; +many_timeouts(Config) when is_list(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + ok = odbc:commit(Ref, commit), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + _Pid = spawn_link(?MODULE, update_table_many_timeouts, + [Table, 5000, self()]), + + receive + many_timeouts_occurred -> + ok + end, + + ok = odbc:commit(Ref, commit), + ok = odbc:disconnect(Ref), + ok. + + +update_table_many_timeouts(Table, TimeOut, Pid) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", + + ok = loop_many_timouts(Ref, UpdateQuery, TimeOut), + + Pid ! many_timeouts_occurred, + + ok = odbc:disconnect(Ref), + ok. + + +loop_many_timouts(Ref, UpdateQuery, TimeOut) -> + case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of + {'EXIT',timeout} -> + loop_many_timouts(Ref, UpdateQuery, TimeOut); + {updated, 1} -> + test_server:fail(database_locker_failed); + {error, connection_closed} -> + ok + end. +%%------------------------------------------------------------------------- +timeout_reset(doc) -> + ["Check that the number of consecutive timouts is reset to 0 when " + "a successful call to the database is made."]; +timeout_reset(suite) -> []; +timeout_reset(Config) when is_list(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + ok = odbc:commit(Ref, commit), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"), + + + Pid = spawn_link(?MODULE, update_table_timeout_reset, + [Table, 5000, self()]), + + receive + many_timeouts_occurred -> + ok + end, + + ok = odbc:commit(Ref, commit), + Pid ! continue, + + receive + altered -> + ok + end, + + {selected, Fields, [{"foobar"}]} = + odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"), + ["DATA"] = odbc_test_lib:to_upper(Fields), + + ok = odbc:commit(Ref, commit), + ok = odbc:disconnect(Ref), + ok. + +update_table_timeout_reset(Table, TimeOut, Pid) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", + + ok = loop_timout_reset(Ref, UpdateQuery, TimeOut, + ?MAX_SEQ_TIMEOUTS-1), + + Pid ! many_timeouts_occurred, + + receive + continue -> + ok + end, + + {selected, Fields, [{"baz"}]} = + odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), + ["DATA"] = odbc_test_lib:to_upper(Fields), + + {updated,1} = odbc:sql_query(Ref, UpdateQuery, TimeOut), + + ok = odbc:commit(Ref, commit), + + Pid ! altered, + + ok = odbc:disconnect(Ref), + + ok. + +loop_timout_reset(_, _, _, 0) -> + ok; + +loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) -> + case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of + {'EXIT',timeout} -> + loop_timout_reset(Ref, UpdateQuery, + TimeOut, NumTimeouts - 1); + {updated, 1} -> + test_server:fail(database_locker_failed); + {error, connection_closed} -> + test_server:fail(connection_closed_premature) + end. + +%%------------------------------------------------------------------------- + +disconnect_on_timeout(doc) -> + ["Check that disconnect after a time out works properly"]; +disconnect_on_timeout(suite) -> []; +disconnect_on_timeout(Config) when is_list(Config) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + ok = odbc:commit(Ref, commit), + + {updated, 1} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + + _Pid = spawn_link(?MODULE, update_table_disconnect_on_timeout, + [Table, 5000, self()]), + receive + ok -> + ok = odbc:commit(Ref, commit); + nok -> + test_server:fail(database_locker_failed) + end. + +update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}]), + UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", + + case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of + {'EXIT', timeout} -> + ok = odbc:disconnect(Ref), + Pid ! ok; + {updated, 1} -> + Pid ! nok + end. + +%%------------------------------------------------------------------------- +connection_closed(doc) -> + ["Checks that you get an appropriate error message if you try to" + " use a connection that has been closed"]; +connection_closed(suite) -> []; +connection_closed(Config) when is_list(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + + Table = ?config(tableName, Config), + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA char(10), PRIMARY KEY(ID))"), + + ok = odbc:disconnect(Ref), + + {error, connection_closed} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + {error, connection_closed} = + odbc:select_count(Ref, "SELECT * FROM " ++ Table), + {error, connection_closed} = odbc:first(Ref), + {error, connection_closed} = odbc:last(Ref), + {error, connection_closed} = odbc:next(Ref), + {error, connection_closed} = odbc:prev(Ref), + {error, connection_closed} = odbc:select(Ref, next, 3), + {error, connection_closed} = odbc:commit(Ref, commit), + ok. + +%%------------------------------------------------------------------------- +disable_scrollable_cursors(doc) -> + ["Test disabling of scrollable cursors."]; +disable_scrollable_cursors(suite) -> []; +disable_scrollable_cursors(Config) when is_list(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{scrollable_cursors, off}]), + + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + {ok, _} = odbc:select_count(Ref, "SELECT ID FROM " ++ Table), + + NextResult = ?RDBMS:selected_ID(1, next), + + test_server:format("Expected: ~p~n", [NextResult]), + + Result = odbc:next(Ref), + test_server:format("Got: ~p~n", [Result]), + NextResult = Result, + + {error, scrollable_cursors_disabled} = odbc:first(Ref), + {error, scrollable_cursors_disabled} = odbc:last(Ref), + {error, scrollable_cursors_disabled} = odbc:prev(Ref), + {error, scrollable_cursors_disabled} = + odbc:select(Ref, {relative, 2}, 5), + {error, scrollable_cursors_disabled} = + odbc:select(Ref, {absolute, 2}, 5), + + {selected, _ColNames,[]} = odbc:select(Ref, next, 1), + ok. + +%%------------------------------------------------------------------------- +return_rows_as_lists(doc)-> + ["Test the option that a row may be returned as a list instead " + "of a tuple. Too be somewhat backward compatible."]; +return_rows_as_lists(suite) -> []; +return_rows_as_lists(Config) when is_list(Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{tuple_row, off}]), + + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'foo')"), + + ListRows = ?RDBMS:selected_list_rows(), + ListRows = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + First = ?RDBMS:first_list_rows(), + Last = ?RDBMS:last_list_rows(), + Prev = ?RDBMS:prev_list_rows(), + Next = ?RDBMS:next_list_rows(), + + Last = odbc:last(Ref), + Prev = odbc:prev(Ref), + First = odbc:first(Ref), + Next = odbc:next(Ref), + ok. + +%%------------------------------------------------------------------------- + +api_missuse(doc)-> + ["Test that behaviour of the control process if the api is abused"]; +api_missuse(suite) -> []; +api_missuse(Config) when is_list(Config)-> + + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + %% Serious programming fault, connetion will be shut down + gen_server:call(Ref, {self(), foobar, 10}, infinity), + test_server:sleep(10), + undefined = process_info(Ref, status), + + {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), []), + %% Serious programming fault, connetion will be shut down + gen_server:cast(Ref2, {self(), foobar, 10}), + test_server:sleep(10), + undefined = process_info(Ref2, status), + + {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), []), + %% Could be an innocent misstake the connection lives. + Ref3 ! foobar, + test_server:sleep(10), + {status, _} = process_info(Ref3, status), + ok. + diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl new file mode 100644 index 0000000000..7d4a0ca15f --- /dev/null +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -0,0 +1,1498 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(odbc_data_type_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include_lib("stdlib/include/ms_transform.hrl"). +-include("test_server_line.hrl"). +-include("odbc_test.hrl"). + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- +all(doc) -> + ["Tests data types"]; +all(suite) -> + case odbc_test_lib:odbc_check() of + ok -> all(); + Other -> {skip,Other} + end. + +all() -> + [char, int, floats, dec_and_num, timestamp]. + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + application:start(odbc), + [{tableName, odbc_test_lib:unique_table_name()} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + application:stop(odbc), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(Case, Config) -> + case atom_to_list(Case) of + "binary" ++ _ -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}]); + "unicode" -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{binary_strings, on}]); + _ -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []) + end, + Dog = test_server:timetrap(?default_timeout), + Temp = lists:keydelete(connection_ref, 1, Config), + NewConfig = lists:keydelete(watchdog, 1, Temp), + [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + Ref = ?config(connection_ref, Config), + ok = odbc:disconnect(Ref), + %% Clean up if needed + Table = ?config(tableName, Config), + {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []), + odbc:sql_query(NewRef, "DROP TABLE " ++ Table), + odbc:disconnect(NewRef), + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +char(doc) -> + ["Tests char data types"]; + +char(suite) -> + [char_fixed_lower_limit, char_fixed_upper_limit, + char_fixed_padding, varchar_lower_limit, varchar_upper_limit, + varchar_no_padding, text_lower_limit, text_upper_limit, unicode + ]. + +char_fixed_lower_limit(doc) -> + ["Tests fixed length char data type lower boundaries."]; +char_fixed_lower_limit(suite) -> + []; +char_fixed_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Below limit + {error, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + (?RDBMS:fixed_char_min() - 1))), + %% Lower limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_min())), + + %% Right length data + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) + ++ "')"), + %% Select data + {selected, Fields,[{"a"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:fixed_char_min() + + 1)) + ++ "')"), + ok. +%%------------------------------------------------------------------------- + +char_fixed_upper_limit(doc) -> + ["Tests fixed length char data type upper boundaries."]; +char_fixed_upper_limit(suite) -> + []; +char_fixed_upper_limit(Config) when is_list(Config) -> + + case ?RDBMS of + postgres -> + {skip, "Limit unknown"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Upper limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_max())), + {updated, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:fixed_char_max()) + ++ "')"), + %% Select data + {selected, Fields, [{CharStr}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = length(CharStr) == ?RDBMS:fixed_char_max(), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:fixed_char_max() + + 1)) + ++ "')"), + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + (?RDBMS:fixed_char_max() + 1))), + ok + end. + +%%------------------------------------------------------------------------- + +char_fixed_padding(doc) -> + ["Tests that data that is shorter than the given size is padded " + "with blanks."]; +char_fixed_padding(suite) -> + []; +char_fixed_padding(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Data should be padded with blanks + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_max())), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:fixed_char_min()) + ++ "')"), + + {selected, Fields, [{CharStr}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = length(CharStr) == ?RDBMS:fixed_char_max(), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. +%%------------------------------------------------------------------------- + +varchar_lower_limit(doc) -> + ["Tests variable length char data type lower boundaries."]; +varchar_lower_limit(suite) -> + []; +varchar_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Below limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min() - 1)), + %% Lower limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min())), + + %% Right length data + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_min()) + ++ "')"), + %% Select data + {selected, Fields, [{"a"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_min()+1)) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- + +varchar_upper_limit(doc) -> + ["Tests variable length char data type upper boundaries."]; +varchar_upper_limit(suite) -> + []; +varchar_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + case ?RDBMS of + oracle -> + {skip, "Known bug in database"}; + postgres -> + {skip, "Limit unknown"}; + _ -> + %% Upper limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:var_char_max()) + ++ "')"), + + {selected, Fields, [{CharStr}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = length(CharStr) == ?RDBMS:var_char_max(), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_max()+1)) + ++ "')"), + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + (?RDBMS:var_char_max() + 1))), + ok + end. +%%------------------------------------------------------------------------- + +varchar_no_padding(doc) -> + ["Tests that data that is shorter than the given max size is not padded " + "with blanks."]; +varchar_no_padding(suite) -> + []; +varchar_no_padding(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Data should NOT be padded with blanks + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_min()) + ++ "')"), + + {selected, Fields, [{CharStr}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = length(CharStr) /= ?RDBMS:var_char_max(), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- + +text_lower_limit(doc) -> + ["Tests 'long' char data type lower boundaries."]; +text_lower_limit(suite) -> + []; +text_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_text_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:text_min()) + ++ "')"), + + {selected, Fields, [{"a"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- + +text_upper_limit(doc) -> + []; +text_upper_limit(suite) -> + []; +text_upper_limit(Config) when is_list(Config) -> + + {skip,"Consumes too much resources" }. +%% Ref = ?config(connection_ref, Config), +%% Table = ?config(tableName, Config), + +%% {updated, _} = % Value == 0 || -1 driver dependent! +%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ +%% ?RDBMS:create_text_table()), +%% {updated, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, ?RDBMS:text_max()) +%% ++ "')"), + +%% {selected, Fields, [{CharStr}]} = +%% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), +%% length(CharStr) == ?RDBMS:text_max(), +%% ["FIELD"] = odbc_test_lib:to_upper(Fields), + +%% {error, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) +%% ++ "')"), +%% ok. + +%%------------------------------------------------------------------------- +binary_char(doc) -> + ["Tests char data types returned as erlang binaries"]; + +binary_char(suite) -> + [binary_char_fixed_lower_limit, binary_char_fixed_upper_limit, + binary_char_fixed_padding, binary_varchar_lower_limit, binary_varchar_upper_limit, + binary_varchar_no_padding, binary_text_lower_limit, binary_text_upper_limit, unicode + ]. + +binary_char_fixed_lower_limit(doc) -> + ["Tests fixed length char data type lower boundaries."]; +binary_char_fixed_lower_limit(suite) -> + []; +binary_char_fixed_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Below limit + {error, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + (?RDBMS:fixed_char_min() - 1))), + %% Lower limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_min())), + + %% Right length data + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) + ++ "')"), + %% Select data + {selected, Fields,[{<<"a">>}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:fixed_char_min() + + 1)) + ++ "')"), + ok. +%%------------------------------------------------------------------------- + +binary_char_fixed_upper_limit(doc) -> + ["Tests fixed length char data type upper boundaries."]; +binary_char_fixed_upper_limit(suite) -> + []; +binary_char_fixed_upper_limit(Config) when is_list(Config) -> + + case ?RDBMS of + postgres -> + {skip, "Limit unknown"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Upper limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_max())), + {updated, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:fixed_char_max()) + ++ "')"), + %% Select data + {selected, Fields, [{CharBin}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = size(CharBin) == ?RDBMS:fixed_char_max(), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:fixed_char_max() + + 1)) + ++ "')"), + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + (?RDBMS:fixed_char_max() + 1))), + ok + end. + +%%------------------------------------------------------------------------- + +binary_char_fixed_padding(doc) -> + ["Tests that data that is shorter than the given size is padded " + "with blanks."]; +binary_char_fixed_padding(suite) -> + []; +binary_char_fixed_padding(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Data should be padded with blanks + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_max())), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:fixed_char_min()) + ++ "')"), + + {selected, Fields, [{CharBin}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = size(CharBin) == ?RDBMS:fixed_char_max(), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. +%%------------------------------------------------------------------------- + +binary_varchar_lower_limit(doc) -> + ["Tests variable length char data type lower boundaries."]; +binary_varchar_lower_limit(suite) -> + []; +binary_varchar_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Below limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min() - 1)), + %% Lower limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min())), + + %% Right length data + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_min()) + ++ "')"), + %% Select data + {selected, Fields, [{<<"a">>}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_min()+1)) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- + +binary_varchar_upper_limit(doc) -> + ["Tests variable length char data type upper boundaries."]; +binary_varchar_upper_limit(suite) -> + []; +binary_varchar_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + case ?RDBMS of + oracle -> + {skip, "Known bug in database"}; + postgres -> + {skip, "Limit unknown"}; + _ -> + %% Upper limit + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + ?RDBMS:var_char_max()) + ++ "')"), + + {selected, Fields, [{CharBin}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = size(CharBin) == ?RDBMS:var_char_max(), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Too long data + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_max()+1)) + ++ "')"), + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + (?RDBMS:var_char_max() + 1))), + ok + end. +%%------------------------------------------------------------------------- + +binary_varchar_no_padding(doc) -> + ["Tests that data that is shorter than the given max size is not padded " + "with blanks."]; +binary_varchar_no_padding(suite) -> + []; +binary_varchar_no_padding(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + %% Data should NOT be padded with blanks + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_min()) + ++ "')"), + + {selected, Fields, [{CharBin}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = size(CharBin) /= ?RDBMS:var_char_max(), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- + +binary_text_lower_limit(doc) -> + ["Tests 'long' char data type lower boundaries."]; +binary_text_lower_limit(suite) -> + []; +binary_text_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_text_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:text_min()) + ++ "')"), + + {selected, Fields, [{<<"a">>}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- + +binary_text_upper_limit(doc) -> + []; +binary_text_upper_limit(suite) -> + []; +binary_text_upper_limit(Config) when is_list(Config) -> + + {skip,"Consumes too much resources" }. +%% Ref = ?config(connection_ref, Config), +%% Table = ?config(tableName, Config), + +%% {updated, _} = % Value == 0 || -1 driver dependent! +%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ +%% ?RDBMS:create_text_table()), +%% {updated, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, ?RDBMS:text_max()) +%% ++ "')"), + +%% {selected, Fields, [{CharBin}]} = +%% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), +%% size(CharBin) == ?RDBMS:text_max(), +%% ["FIELD"] = odbc_test_lib:to_upper(Fields), + +%% {error, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) +%% ++ "')"), +%% ok. + + +%%------------------------------------------------------------------------- + +int(doc) -> + ["Tests integer data types"]; + +int(suite) -> + [tiny_int_lower_limit, tiny_int_upper_limit, small_int_lower_limit, + small_int_upper_limit, int_lower_limit, int_upper_limit, + big_int_lower_limit, big_int_upper_limit, bit_false, bit_true]. + +%%------------------------------------------------------------------------- + +tiny_int_lower_limit(doc) -> + ["Tests integer of type tinyint."]; +tiny_int_lower_limit(suite) -> + []; +tiny_int_lower_limit(Config) when is_list(Config) -> + case ?RDBMS of + postgres -> + {skip, "Type tiniyint not supported"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_tiny_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:tiny_int_min()) + ++ "')"), + + SelectResult = ?RDBMS:tiny_int_min_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:tiny_int_min() + - 1) + ++ "')"), + ok + end. + +%%------------------------------------------------------------------------- + +tiny_int_upper_limit(doc) -> + ["Tests integer of type tinyint."]; +tiny_int_upper_limit(suite) -> + []; +tiny_int_upper_limit(Config) when is_list(Config) -> + case ?RDBMS of + postgres -> + {skip, "Type tiniyint not supported"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_tiny_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:tiny_int_max()) + ++ "')"), + + SelectResult = ?RDBMS:tiny_int_max_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:tiny_int_max() + + 1) + ++ "')"), + ok + end. + +%%------------------------------------------------------------------------- + +small_int_lower_limit(doc) -> + ["Tests integer of type smallint."]; +small_int_lower_limit(suite) -> + []; +small_int_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_small_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:small_int_min()) + ++ "')"), + + SelectResult = ?RDBMS:small_int_min_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:small_int_min() + - 1) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- + +small_int_upper_limit(doc) -> + ["Tests integer of type smallint."]; +small_int_upper_limit(suite) -> + []; +small_int_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_small_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:small_int_max()) + ++ "')"), + + SelectResult = ?RDBMS:small_int_max_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:small_int_max() + + 1) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- +int_lower_limit(doc) -> + ["Tests integer of type int."]; +int_lower_limit(suite) -> + []; +int_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:int_min()) + ++ "')"), + + SelectResult = ?RDBMS:int_min_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:int_min() - 1) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- + +int_upper_limit(doc) -> + ["Tests integer of type int."]; +int_upper_limit(suite) -> + []; +int_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:int_max()) + ++ "')"), + + SelectResult = ?RDBMS:int_max_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:int_max() + 1) + ++ "')"), + ok. + + +%%------------------------------------------------------------------------- +big_int_lower_limit(doc) -> + ["Tests integer of type bigint"]; +big_int_lower_limit(suite) -> + []; +big_int_lower_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_big_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_min()) + ++ "')"), + + SelectResult = ?RDBMS:big_int_min_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_min() + - 1) + ++ "')"), + ok. + +%%------------------------------------------------------------------------- + +big_int_upper_limit(doc) -> + ["Tests integer of type bigint."]; +big_int_upper_limit(suite) -> + []; +big_int_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_big_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_max()) + ++ "')"), + + SelectResult = ?RDBMS:big_int_max_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_max() + + 1) + ++ "')"), + ok. +%%------------------------------------------------------------------------- + +bit_false(doc) -> + [""]; +bit_false(suite) -> + []; +bit_false(Config) when is_list(Config) -> + case ?RDBMS of + oracle -> + {skip, "Not supported by driver"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_bit_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:bit_false()) + ++ "')"), + + SelectResult = ?RDBMS:bit_false_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(-1) + ++ "')"), + ok + end. + +%%------------------------------------------------------------------------- + +bit_true(doc) -> + [""]; +bit_true(suite) -> + []; +bit_true(Config) when is_list(Config) -> + case ?RDBMS of + oracle -> + {skip, "Not supported by driver"}; + _ -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_bit_table()), + + {updated, _} = + odbc:sql_query(Ref, + "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:bit_true()) + ++ "')"), + + SelectResult = ?RDBMS:bit_true_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(-1) + ++ "')"), + ok + end. + +%%------------------------------------------------------------------------- + +floats(doc) -> + ["Test the datatype float."]; +floats(suite) -> + [float_lower_limit, float_upper_limit, float_zero, real_zero]. + +%%------------------------------------------------------------------------- +float_lower_limit(doc) -> + [""]; +float_lower_limit(suite) -> + []; +float_lower_limit(Config) when is_list(Config) -> + + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ float_to_list( + ?RDBMS:float_min()) + ++ "')"), + {selected,[_ColName],[{MinFloat}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + true = ?RDBMS:float_min() == MinFloat, + + case ?RDBMS of + oracle -> + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), + + {updated, _} = + odbc:sql_query(Ref, + "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_underflow() ++ ")"), + + SelectResult = ?RDBMS:float_zero_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table); + _ -> + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_underflow() ++ ")") + end, + ok. + + +%%------------------------------------------------------------------------- +float_upper_limit(doc) -> + [""]; +float_upper_limit(suite) -> + []; +float_upper_limit(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ float_to_list( + ?RDBMS:float_max()) + ++ "')"), + + + {selected,[_ColName],[{MaxFloat}]} + = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + + true = ?RDBMS:float_max() == MaxFloat, + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_overflow() ++ ")"), + ok. + +%%------------------------------------------------------------------------- +float_zero(doc) -> + ["Test the float value zero."]; +float_zero(suite) -> + []; +float_zero(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES('0')"), + + SelectResult = ?RDBMS:float_zero_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ok. +%%------------------------------------------------------------------------- +real_zero(doc) -> + ["Test the real value zero."]; +real_zero(suite) -> + []; +real_zero(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + case ?RDBMS of + oracle -> + {skip, "Not supported in Oracle"}; + _ -> + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_real_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES('0')"), + + SelectResult = ?RDBMS:real_zero_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ok + end. +%%------------------------------------------------------------------------- +dec_and_num(doc) -> + ["Tests decimal and numeric datatypes."]; +dec_and_num(suite) -> + [dec_long, dec_double, dec_bignum, num_long, num_double, num_bignum]. +%%------------------------------------------------------------------------ +dec_long(doc) -> + [""]; +dec_long(suit) -> + []; +dec_long(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (9,0))"), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields, [{2}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. +%%------------------------------------------------------------------------ +dec_double(doc) -> + [""]; +dec_double(suit) -> + []; +dec_double(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (10,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields, [{2.00000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields1, [{2.00000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields1), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15, 1))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields2, [{1.60000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields2), + ok. + +%%------------------------------------------------------------------------ +dec_bignum(doc) -> + [""]; +dec_bignum(suit) -> + []; +dec_bignum(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields, [{"2"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,1))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields1, [{"1.6"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields1), + ok. +%%------------------------------------------------------------------------ +num_long(doc) -> + [""]; +num_long(suit) -> + []; +num_long(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (9,0))"), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.5)"), + + {selected, Fields, [{2}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + ok. +%%------------------------------------------------------------------------ +num_double(doc) -> + [""]; +num_double(suit) -> + []; +num_double(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (10,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields, [{2.0000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields1, [{2.0000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields1), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,1))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields2, [{1.6000}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields2), + ok. +%%------------------------------------------------------------------------ +num_bignum(doc) -> + [""]; +num_bignum(suit) -> + []; +num_bignum(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,0))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields, [{"2"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + %% Clean up + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,1))"), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), + + {selected, Fields1, [{"1.6"}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + ["FIELD"] = odbc_test_lib:to_upper(Fields1), + ok. + +%%------------------------------------------------------------------------ +unicode(doc) -> + ["Test unicode support"]; +unicode(suit) -> + []; +unicode(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_unicode_table()), + + Latin1Data = ["ÖÄÅÄÖÅäöå", + "testasdf", + "Row 3", + "Row 4", + "Row 5", + "Row 6", + "Row 7", + "Row 8", + "Row 9", + "Row 10", + "Row 11", + "Row 12"], + + case ?RDBMS of + sqlserver -> + w_char_support_win(Ref, Table, Latin1Data); + postgres -> + direct_utf8(Ref, Table, Latin1Data); + oracle -> + {skip, "not currently supported"} + end. + +w_char_support_win(Ref, Table, Latin1Data) -> + UnicodeIn = lists:map(fun(S) -> + unicode:characters_to_binary(S,latin1,{utf16,little}) + end, + Latin1Data), + + test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), + + {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", + [{{sql_wvarchar,50},UnicodeIn}]), + + {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), + + test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + + Result = lists:map(fun({Unicode}) -> + unicode:characters_to_list(Unicode,{utf16,little}) + end, + UnicodeOut), + Latin1Data = Result. + + +direct_utf8(Ref, Table, Latin1Data) -> + UnicodeIn = lists:map(fun(String) -> + unicode:characters_to_binary(String,latin1,utf8) + end, + Latin1Data), + + test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]), + {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", + [{{sql_varchar,50}, UnicodeIn}]), + + {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), + + test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + + Result = lists:map(fun({Char}) -> + unicode:characters_to_list(Char,utf8) + end, UnicodeOut), + + test_server:format("Result: ~p ~n", [Result]), + + Latin1Data = Result. + +%%------------------------------------------------------------------------ +timestamp(doc) -> + [""]; +timestamp(suit) -> + []; +timestamp(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_timestamp_table()), + + Data = [calendar:local_time(), + {{2009,6,17},{20,54,59}}, + {{2009,6,18},{20,54,59}}, + {{2009,6,19},{20,54,59}}, + {{2009,6,20},{20,54,59}}, + {{2009,6,21},{20,54,59}}], + + {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", + [{sql_timestamp,Data}]), + + %%% Crate list or database table rows + TimeStamps = lists:map(fun(Value) -> {Value} end, Data), + + {selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table). diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl new file mode 100644 index 0000000000..12b39be3b7 --- /dev/null +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -0,0 +1,1453 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(odbc_query_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). +-include("odbc_test.hrl"). + +%%-------------------------------------------------------------------- +%% all(Arg) -> [Doc] | [Case] | {skip, Comment} +%% Arg - doc | suite +%% Doc - string() +%% Case - atom() +%% Name of a test case function. +%% Comment - string() +%% Description: Returns documentation/test cases in this test suite +%% or a skip tuple if the platform is not supported. +%%-------------------------------------------------------------------- +all(doc) -> + ["Tests SQL queries"]; +all(suite) -> + case odbc_test_lib:odbc_check() of + ok -> all(); + Other -> {skip, Other} + end. + +all() -> + [sql_query, first, last, next, prev, select_count,select_next, + select_relative, select_absolute, create_table_twice, + delete_table_twice, duplicate_key, not_connection_owner, + no_result_set, query_error, multiple_select_result_sets, + multiple_mix_result_sets, multiple_result_sets_error, + parameterized_queries, describe_table, + delete_nonexisting_row]. + + +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initiation before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) when is_list(Config) -> + application:start(odbc), + [{tableName, odbc_test_lib:unique_table_name()}| Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + application:stop(odbc), + ok. + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(Case, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initiation before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_testcase(_Case, Config) -> + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + Dog = test_server:timetrap(?default_timeout), + Temp = lists:keydelete(connection_ref, 1, Config), + NewConfig = lists:keydelete(watchdog, 1, Temp), + [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(Case, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_Case, Config) -> + Ref = ?config(connection_ref, Config), + ok = odbc:disconnect(Ref), + %% Clean up if needed + Table = ?config(tableName, Config), + {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []), + odbc:sql_query(NewRef, "DROP TABLE " ++ Table), + odbc:disconnect(NewRef), + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- +sql_query(doc)-> + ["Test the common cases"]; +sql_query(suite) -> []; +sql_query(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + + {updated, Count} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + true = odbc_test_lib:check_row_count(1, Count), + + InsertResult = ?RDBMS:insert_result(), + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {updated, NewCount} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foo' WHERE ID = 1"), + + true = odbc_test_lib:check_row_count(1, NewCount), + + UpdateResult = ?RDBMS:update_result(), + UpdateResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {updated, NewCount1} = odbc:sql_query(Ref, "DELETE FROM " ++ Table ++ + " WHERE ID = 1"), + + true = odbc_test_lib:check_row_count(1, NewCount1), + + {selected, Fields, []} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["ID","DATA"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- +select_count(doc) -> + ["Tests select_count/[2,3]'s timeout, " + " select_count's functionality will be better tested by other tests " + " such as first."]; +select_count(sute) -> []; +select_count(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + true = odbc_test_lib:check_row_count(1, Count), + {ok, _} = + odbc:select_count(Ref, "SELECT * FROM " ++ Table, ?TIMEOUT), + {'EXIT', {function_clause, _}} = + (catch odbc:select_count(Ref, "SELECT * FROM ", -1)), + ok. +%%------------------------------------------------------------------------- +first(doc) -> + ["Tests first/[1,2]"]; +first(suite) -> []; +first(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + true = odbc_test_lib:check_row_count(1, Count), + {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + true = odbc_test_lib:check_row_count(1, NewCount), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + + FirstResult = ?RDBMS:selected_ID(1, first), + FirstResult = odbc:first(Ref), + FirstResult = odbc:first(Ref, ?TIMEOUT), + {'EXIT', {function_clause, _}} = (catch odbc:first(Ref, -1)), + ok. + +%%------------------------------------------------------------------------- +last(doc) -> + ["Tests last/[1,2]"]; +last(suite) -> []; +last(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + true = odbc_test_lib:check_row_count(1, Count), + {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + true = odbc_test_lib:check_row_count(1, NewCount), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + LastResult = ?RDBMS:selected_ID(2, last), + LastResult = odbc:last(Ref), + + LastResult = odbc:last(Ref, ?TIMEOUT), + {'EXIT', {function_clause, _}} = (catch odbc:last(Ref, -1)), + ok. + +%%------------------------------------------------------------------------- +next(doc) -> + ["Tests next/[1,2]"]; +next(suite) -> []; +next(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + true = odbc_test_lib:check_row_count(1, Count), + {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + true = odbc_test_lib:check_row_count(1, NewCount), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + NextResult = ?RDBMS:selected_ID(1, next), + NextResult = odbc:next(Ref), + NextResult2 = ?RDBMS:selected_ID(2, next), + NextResult2 = odbc:next(Ref, ?TIMEOUT), + {'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)), + ok. +%%------------------------------------------------------------------------- +prev(doc) -> + ["Tests prev/[1,2]"]; +prev(suite) -> []; +prev(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + true = odbc_test_lib:check_row_count(1, Count), + {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + true = odbc_test_lib:check_row_count(1, NewCount), + + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + odbc:last(Ref), % Position cursor last so there will be a prev + PrevResult = ?RDBMS:selected_ID(1, prev), + PrevResult = odbc:prev(Ref), + + odbc:last(Ref), % Position cursor last so there will be a prev + PrevResult = odbc:prev(Ref, ?TIMEOUT), + {'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)), + ok. +%%------------------------------------------------------------------------- +select_next(doc) -> + ["Tests select/[4,5] with CursorRelation = next "]; +select_next(suit) -> []; +select_next(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(3)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(4)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(5)"), + + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + SelectResult1 = ?RDBMS:selected_next_N(1), + SelectResult1 = odbc:select(Ref, next, 3), + + %% Test that selecting stops at the end of the result set + SelectResult2 = ?RDBMS:selected_next_N(2), + SelectResult2 = odbc:select(Ref, next, 3, ?TIMEOUT), + {'EXIT',{function_clause, _}} = + (catch odbc:select(Ref, next, 2, -1)), + + %% If you try fetching data beyond the the end of result set, + %% you get an empty list. + {selected, Fields, []} = odbc:select(Ref, next, 1), + + ["ID"] = odbc_test_lib:to_upper(Fields), + ok. + +%%------------------------------------------------------------------------- +select_relative(doc) -> + ["Tests select/[4,5] with CursorRelation = relative "]; +select_relative(suit) -> []; +select_relative(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(3)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(4)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(5)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(6)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(7)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(8)"), + + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + SelectResult1 = ?RDBMS:selected_relative_N(1), + SelectResult1 = odbc:select(Ref, {relative, 2}, 3), + + %% Test that selecting stops at the end of the result set + SelectResult2 = ?RDBMS:selected_relative_N(2), + SelectResult2 = odbc:select(Ref, {relative, 3}, 3, ?TIMEOUT), + {'EXIT',{function_clause, _}} = + (catch odbc:select(Ref, {relative, 3} , 2, -1)), + ok. + +%%------------------------------------------------------------------------- +select_absolute(doc) -> + ["Tests select/[4,5] with CursorRelation = absolute "]; +select_absolute(suit) -> []; +select_absolute(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer)"), + + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(3)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(4)"), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(5)"), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + + SelectResult1 = ?RDBMS:selected_absolute_N(1), + SelectResult1 = odbc:select(Ref, {absolute, 1}, 3), + + %% Test that selecting stops at the end of the result set + SelectResult2 = ?RDBMS:selected_absolute_N(2), + SelectResult2 = odbc:select(Ref, {absolute, 1}, 6, ?TIMEOUT), + {'EXIT',{function_clause, _}} = + (catch odbc:select(Ref, {absolute, 1}, 2, -1)), + ok. + +%%------------------------------------------------------------------------- +create_table_twice(doc) -> + ["Test what happens if you try to create the same table twice."]; +create_table_twice(suite) -> []; +create_table_twice(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + {error, Error} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + is_driver_error(Error), + ok. + +%%------------------------------------------------------------------------- +delete_table_twice(doc) -> + ["Test what happens if you try to delete the same table twice."]; +delete_table_twice(suite) -> []; +delete_table_twice(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10))"), + {updated, _} = odbc:sql_query(Ref, "DROP TABLE " ++ Table), + {error, Error} = odbc:sql_query(Ref, "DROP TABLE " ++ Table), + is_driver_error(Error), + ok. + +%------------------------------------------------------------------------- +duplicate_key(doc) -> + ["Test what happens if you try to use the same key twice"]; +duplicate_key(suit) -> []; +duplicate_key(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA char(10), PRIMARY KEY(ID))"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + {error, Error} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'foo')"), + is_driver_error(Error), + ok. + +%%------------------------------------------------------------------------- +not_connection_owner(doc) -> + ["Test what happens if a process that did not start the connection" + " tries to acess it."]; +not_connection_owner(suite) -> []; +not_connection_owner(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + spawn_link(?MODULE, not_owner, [self(), Ref, Table]), + + receive + continue -> + ok + end. + +not_owner(Pid, Ref, Table) -> + {error, process_not_owner_of_odbc_connection} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), + + {error, process_not_owner_of_odbc_connection} = + odbc:disconnect(Ref), + + Pid ! continue. + +%%------------------------------------------------------------------------- +no_result_set(doc) -> + ["Tests what happens if you try to use a function that needs an " + "associated result set when there is none."]; +no_result_set(suite) -> []; +no_result_set(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + + {error, result_set_does_not_exist} = odbc:first(Ref), + {error, result_set_does_not_exist} = odbc:last(Ref), + {error, result_set_does_not_exist} = odbc:next(Ref), + {error, result_set_does_not_exist} = odbc:prev(Ref), + {error, result_set_does_not_exist} = odbc:select(Ref, next, 1), + {error, result_set_does_not_exist} = + odbc:select(Ref, {absolute, 2}, 1), + {error, result_set_does_not_exist} = + odbc:select(Ref, {relative, 2}, 1), + ok. +%%------------------------------------------------------------------------- +query_error(doc) -> + ["Test what happens if there is an error in the query."]; +query_error(suite) -> + []; +query_error(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA char(10), PRIMARY KEY(ID))"), + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), + + {error, _} = + odbc:sql_query(Ref, "INSERT ONTO " ++ Table ++ " VALUES(1,'bar')"), + ok. + +%%------------------------------------------------------------------------- +multiple_select_result_sets(doc) -> + ["Test what happens if you have a batch of select queries."]; +multiple_select_result_sets(suite) -> + []; +multiple_select_result_sets(Config) when is_list(Config) -> + case ?RDBMS of + sqlserver -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), " + "PRIMARY KEY(ID))"), + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1,'bar')"), + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2, 'foo')"), + + MultipleResult = ?RDBMS:multiple_select(), + + MultipleResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table ++ + "; SELECT DATA FROM "++ Table ++ + " WHERE ID=2"), + ok; + _ -> + {skip, "multiple result_set not supported"} + end. + +%%------------------------------------------------------------------------- +multiple_mix_result_sets(doc) -> + ["Test what happens if you have a batch of select and other type of" + " queries."]; +multiple_mix_result_sets(suite) -> + []; +multiple_mix_result_sets(Config) when is_list(Config) -> + case ?RDBMS of + sqlserver -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), " + "PRIMARY KEY(ID))"), + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1,'bar')"), + + MultipleResult = ?RDBMS:multiple_mix(), + + MultipleResult = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2,'foo'); UPDATE " ++ Table ++ + " SET DATA = 'foobar' WHERE ID =1;SELECT " + "* FROM " + ++ Table ++ ";DELETE FROM " ++ Table ++ + " WHERE ID =1; SELECT DATA FROM " ++ Table), + ok; + _ -> + {skip, "multiple result_set not supported"} + end. +%%------------------------------------------------------------------------- +multiple_result_sets_error(doc) -> + ["Test what happens if one of the batched queries fails."]; +multiple_result_sets_error(suite) -> + []; +multiple_result_sets_error(Config) when is_list(Config) -> + case ?RDBMS of + sqlserver -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID integer, DATA varchar(10), " + "PRIMARY KEY(ID))"), + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1,'bar')"), + + {error, Error} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(1,'foo'); SELECT * FROM " ++ Table), + is_driver_error(Error), + + {error, NewError} = + odbc:sql_query(Ref, "SELECT * FROM " + ++ Table ++ ";INSERT INTO " ++ Table ++ + " VALUES(1,'foo')"), + is_driver_error(NewError), + ok; + _ -> + {skip, "multiple result_set not supported"} + end. + +%%------------------------------------------------------------------------- +parameterized_queries(doc)-> + ["Tests diffrent variants of parameterized queries."]; +parameterized_queries(suite) -> + %% Note timestamps are inserted with param_query in odbc_data_type_SUITE + %% so no need to test this again. + [param_integers, + param_insert_decimal, param_insert_numeric, + param_insert_string, + param_insert_float, param_insert_real, param_insert_double, + param_insert_mix, param_update, param_delete, param_select]. + +%%------------------------------------------------------------------------- +param_integers(doc)-> + ["Test insertion of integers by parameterized queries."]; +param_integers(suite) -> + [param_insert_tiny_int, + param_insert_small_int, param_insert_int, param_insert_integer]. +%%------------------------------------------------------------------------- +param_insert_tiny_int(doc)-> + ["Test insertion of tiny ints by parameterized queries."]; +param_insert_tiny_int(suite) -> + []; +param_insert_tiny_int(Config) when is_list(Config) -> + case ?RDBMS of + sqlserver -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD TINYINT)"), + + {updated, Count} = + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_tinyint, [1, 2]}], + ?TIMEOUT),%Make sure to test timeout clause + + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_tiny_int(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_tinyint, [1, "2"]}])), + ok; + _ -> + {skip, "Type tiniyint not supported"} + end. +%%------------------------------------------------------------------------- +param_insert_small_int(doc)-> + ["Test insertion of small ints by parameterized queries."]; +param_insert_small_int(suite) -> + []; +param_insert_small_int(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD SMALLINT)"), + + {updated, Count} = + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_smallint, [1, 2]}], + ?TIMEOUT), %% Make sure to test timeout clause + + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_small_int(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_smallint, [1, "2"]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_int(doc)-> + ["Test insertion of ints by parameterized queries."]; +param_insert_int(suite) -> + []; +param_insert_int(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD INT)"), + + Int = ?RDBMS:small_int_max() + 1, + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_integer, [1, Int]}]), + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_int(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_integer, [1, "2"]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_integer(doc)-> + ["Test insertion of integers by parameterized queries."]; +param_insert_integer(suite) -> + []; +param_insert_integer(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD INTEGER)"), + + Int = ?RDBMS:small_int_max() + 1, + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_integer, [1, Int]}]), + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_int(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_integer, [1, 2.3]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_decimal(doc)-> + ["Test insertion of decimal numbers by parameterized queries."]; +param_insert_decimal(suite) -> + []; +param_insert_decimal(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD DECIMAL (3,0))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_decimal, 3, 0}, [1, 2]}]), + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_decimal(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_decimal, 3, 0}, [1, "2"]}])), + + + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD DECIMAL (3,1))"), + + {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_decimal, 3, 1}, [0.25]}]), + true = odbc_test_lib:check_row_count(1, NewCount), + + {selected, Fields, [{Value}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fields), + + odbc_test_lib:match_float(Value, 0.3, 0.01), + + ok. + +%%------------------------------------------------------------------------- +param_insert_numeric(doc)-> + ["Test insertion of numeric numbers by parameterized queries."]; +param_insert_numeric(suite) -> + []; +param_insert_numeric(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD NUMERIC (3,0))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_numeric,3,0}, [1, 2]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_numeric(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_decimal, 3, 0}, [1, "2"]}])), + + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD NUMERIC (3,1))"), + + {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_numeric, 3, 1}, [0.25]}]), + + true = odbc_test_lib:check_row_count(1, NewCount), + + {selected, Fileds, [{Value}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + odbc_test_lib:match_float(Value, 0.3, 0.01), + ok. + +%%------------------------------------------------------------------------- +param_insert_string(doc) -> + ["Test insertion of strings by parameterized queries."]; +param_insert_string(suite) -> + [param_insert_char, param_insert_character, param_insert_char_varying, + param_insert_character_varying]. + +%%------------------------------------------------------------------------- +param_insert_char(doc)-> + ["Test insertion of fixed length string by parameterized queries."]; +param_insert_char(suite) -> + []; +param_insert_char(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD CHAR (10))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, + ["foofoofoof", "0123456789"]}]), + true = odbc_test_lib:check_row_count(2, Count), + + {selected,Fileds,[{"foofoofoof"}, {"0123456789"}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, + ["foo", "01234567890"]}]), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["1", 2.3]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_character(doc)-> + ["Test insertion of fixed length string by parameterized queries."]; +param_insert_character(suite) -> + []; +param_insert_character(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD CHARACTER (10))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, + ["foofoofoof", "0123456789"]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + {selected, Fileds, [{"foofoofoof"}, {"0123456789"}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, + ["foo", "01234567890"]}]), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["1", 2]}])), + ok. + +%%------------------------------------------------------------------------ +param_insert_char_varying(doc)-> + ["Test insertion of variable length strings by parameterized queries."]; +param_insert_char_varying(suite) -> + []; +param_insert_char_varying(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD CHAR VARYING(10))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, + ["foo", "0123456789"]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + {selected, Fileds, [{"foo"}, {"0123456789"}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, + ["foo", "01234567890"]}]), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["1", 2.3]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_character_varying(doc)-> + ["Test insertion of variable length strings by parameterized queries."]; +param_insert_character_varying(suite) -> + []; +param_insert_character_varying(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD CHARACTER VARYING(10))"), + + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, + ["foo", "0123456789"]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + {selected, Fileds, [{"foo"}, {"0123456789"}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, + ["foo", "01234567890"]}]), + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["1", 2]}])), + ok. +%%------------------------------------------------------------------------- +param_insert_float(doc)-> + ["Test insertion of floats by parameterized queries."]; +param_insert_float(suite) -> + []; +param_insert_float(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD FLOAT(5))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_float,5}, [1.3, 1.2]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + {selected, Fileds, [{Float1},{Float2}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + case (odbc_test_lib:match_float(Float1, 1.3, 0.000001) and + odbc_test_lib:match_float(Float2, 1.2, 0.000001)) of + true -> + ok; + false -> + test_server:fail(float_numbers_do_not_match) + end, + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_float, 5}, [1.0, "2"]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_real(doc)-> + ["Test insertion of real numbers by parameterized queries."]; +param_insert_real(suite) -> + []; +param_insert_real(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD REAL)"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_real, [1.3, 1.2]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + %_InsertResult = ?RDBMS:param_select_real(), + + {selected, Fileds, [{Real1},{Real2}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + case (odbc_test_lib:match_float(Real1, 1.3, 0.000001) and + odbc_test_lib:match_float(Real2, 1.2, 0.000001)) of + true -> + ok; + false -> + test_server:fail(real_numbers_do_not_match) + end, + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_real,[1.0, "2"]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_double(doc)-> + ["Test insertion of doubles by parameterized queries."]; +param_insert_double(suite) -> + []; +param_insert_double(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (FIELD DOUBLE PRECISION)"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_double, [1.3, 1.2]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + {selected, Fileds, [{Double1},{Double2}]} = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + + ["FIELD"] = odbc_test_lib:to_upper(Fileds), + + case (odbc_test_lib:match_float(Double1, 1.3, 0.000001) and + odbc_test_lib:match_float(Double2, 1.2, 0.000001)) of + true -> + ok; + false -> + test_server:fail(double_numbers_do_not_match) + end, + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_double, [1.0, "2"]}])), + ok. + +%%------------------------------------------------------------------------- +param_insert_mix(doc)-> + ["Test insertion of a mixture of datatypes by parameterized queries."]; +param_insert_mix(suite) -> + []; +param_insert_mix(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID INTEGER, DATA CHARACTER VARYING(10)," + " PRIMARY KEY(ID))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(ID, DATA) VALUES(?, ?)", + [{sql_integer, [1, 2]}, + {{sql_varchar, 10}, ["foo", "bar"]}]), + + true = odbc_test_lib:check_row_count(2, Count), + + InsertResult = ?RDBMS:param_select_mix(), + + InsertResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + ok. +%%------------------------------------------------------------------------- +param_update(doc)-> + ["Test parameterized update query."]; +param_update(suite) -> + []; +param_update(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID INTEGER, DATA CHARACTER VARYING(10)," + " PRIMARY KEY(ID))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(ID, DATA) VALUES(?, ?)", + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, + ["foo", "bar", "baz"]}]), + + true = odbc_test_lib:check_row_count(3, Count), + + {updated, NewCount} = odbc:param_query(Ref, "UPDATE " ++ Table ++ + " SET DATA = 'foobar' WHERE ID = ?", + [{sql_integer, [1, 2]}]), + + true = odbc_test_lib:check_row_count(2, NewCount), + + UpdateResult = ?RDBMS:param_update(), + + UpdateResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + ok. + +%%------------------------------------------------------------------------- +delete_nonexisting_row(doc) -> % OTP-5759 + ["Make a delete...where with false conditions (0 rows deleted). ", + "This used to give an error message (see ticket OTP-5759)."]; +delete_nonexisting_row(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table + ++ " (ID INTEGER, DATA CHARACTER VARYING(10))"), + {updated, Count} = + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(ID, DATA) VALUES(?, ?)", + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "baz"]}]), + + true = odbc_test_lib:check_row_count(3, Count), + + {updated, NewCount} = + odbc:sql_query(Ref, "DELETE FROM " ++ Table ++ " WHERE ID = 8"), + + true = odbc_test_lib:check_row_count(0, NewCount), + + {updated, _} = + odbc:sql_query(Ref, "DROP TABLE "++ Table), + + ok. + +%%------------------------------------------------------------------------- +param_delete(doc) -> + ["Test parameterized delete query."]; +param_delete(suite) -> + []; +param_delete(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID INTEGER, DATA CHARACTER VARYING(10)," + " PRIMARY KEY(ID))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(ID, DATA) VALUES(?, ?)", + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, + ["foo", "bar", "baz"]}]), + true = odbc_test_lib:check_row_count(3, Count), + + {updated, NewCount} = odbc:param_query(Ref, "DELETE FROM " ++ Table ++ + " WHERE ID = ?", + [{sql_integer, [1, 2]}]), + + true = odbc_test_lib:check_row_count(2, NewCount), + + UpdateResult = ?RDBMS:param_delete(), + + UpdateResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + ok. + + +%%------------------------------------------------------------------------- +param_select(doc) -> + ["Test parameterized select query."]; +param_select(suite) -> + []; +param_select(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (ID INTEGER, DATA CHARACTER VARYING(10)," + " PRIMARY KEY(ID))"), + + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(ID, DATA) VALUES(?, ?)", + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, + ["foo", "bar", "foo"]}]), + + true = odbc_test_lib:check_row_count(3, Count), + + SelectResult = ?RDBMS:param_select(), + + SelectResult = odbc:param_query(Ref, "SELECT * FROM " ++ Table ++ + " WHERE DATA = ?", + [{{sql_varchar, 10}, ["foo"]}]), + ok. + +%%------------------------------------------------------------------------- +describe_table(doc) -> + ["Test describe_table/[2,3]"]; +describe_table(suite) -> + [describe_integer, describe_string, describe_floating, describe_dec_num, + describe_no_such_table]. + +%%------------------------------------------------------------------------- +describe_integer(doc) -> + ["Test describe_table/[2,3] for integer columns."]; +describe_integer(suite) -> + []; +describe_integer(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (int1 SMALLINT, int2 INT, int3 INTEGER)"), + + Decs = ?RDBMS:describe_integer(), + %% Make sure to test timeout clause + Decs = odbc:describe_table(Ref, Table, ?TIMEOUT), + ok. + +%%------------------------------------------------------------------------- +describe_string(doc) -> + ["Test describe_table/[2,3] for string columns."]; +describe_string(suite) -> + []; +describe_string(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (str1 char(10), str2 character(10), " + "str3 CHAR VARYING(10), str4 " + "CHARACTER VARYING(10))"), + + Decs = ?RDBMS:describe_string(), + + Decs = odbc:describe_table(Ref, Table), + ok. + +%%------------------------------------------------------------------------- +describe_floating(doc) -> + ["Test describe_table/[2,3] for floting columns."]; +describe_floating(suite) -> + []; +describe_floating(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (f FLOAT(5), r REAL, " + "d DOUBLE PRECISION)"), + + Decs = ?RDBMS:describe_floating(), + + Decs = odbc:describe_table(Ref, Table), + ok. + +%%------------------------------------------------------------------------- +describe_dec_num(doc) -> + ["Test describe_table/[2,3] for decimal and numerical columns"]; +describe_dec_num(suite) -> + []; +describe_dec_num(Config) when is_list(Config) -> + + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ + " (dec DECIMAL(9,3), num NUMERIC(9,2))"), + + Decs = ?RDBMS:describe_dec_num(), + + Decs = odbc:describe_table(Ref, Table), + ok. + + +%%------------------------------------------------------------------------- +describe_timestamp(doc) -> + ["Test describe_table/[2,3] for tinmestap columns"]; +describe_timestamp(suite) -> + []; +describe_timestamp(Config) when is_list(Config) -> + + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_timestamp_table()), + + Decs = ?RDBMS:describe_timestamp(), + + Decs = odbc:describe_table(Ref, Table), + ok. + +%%------------------------------------------------------------------------- +describe_no_such_table(doc) -> + ["Test what happens if you try to describe a table that does not exist."]; +describe_no_such_table(suite) -> + []; +describe_no_such_table(Config) when is_list(Config) -> + + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {error, _ } = odbc:describe_table(Ref, Table), + ok. + +%%------------------------------------------------------------------------- +%% Internal functions +%%------------------------------------------------------------------------- + +is_driver_error(Error) -> + case is_list(Error) of + true -> + test_server:format("Driver error ~p~n", [Error]), + ok; + false -> + test_server:fail(Error) + end. diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl new file mode 100644 index 0000000000..2cca8e4546 --- /dev/null +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -0,0 +1,147 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(odbc_start_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). +-include("odbc_test.hrl"). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + case code:which(odbc) of + non_existing -> + {skip, "No ODBC built"}; + _ -> + [{tableName, odbc_test_lib:unique_table_name()} | Config] + end. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config0) -> + test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all(doc) -> + ["Test start/stop of odbc"]; + +all(suite) -> + case odbc_test_lib:odbc_check() of + ok -> all(); + Other -> {skip, Other} + end. + +all() -> + [start]. + + +%% Test cases starts here. +%%-------------------------------------------------------------------- + +start(doc) -> + ["Test start/stop of odbc"]; +start(suite) -> + []; +start(Config) when is_list(Config) -> + {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), []), + odbc:start(), + case odbc:connect(?RDBMS:connection_string(), []) of + {ok, Ref0} -> + ok = odbc:disconnect(Ref0), + odbc:stop(), + {error,odbc_not_started} = + odbc:connect(?RDBMS:connection_string(), []), + start_odbc(transient), + start_odbc(permanent); + {error, odbc_not_started} -> + test_server:fail(start_failed); + Error -> + test_server:format("Connection failed: ~p~n", [Error]), + {skip, "ODBC is not properly setup"} + end. + +start_odbc(Type) -> + ok = odbc:start(Type), + case odbc:connect(?RDBMS:connection_string(), []) of + {ok, Ref} -> + ok = odbc:disconnect(Ref), + odbc:stop(); + {error, odbc_not_started} -> + test_server:fail(start_failed) + end. diff --git a/lib/odbc/test/odbc_test.hrl b/lib/odbc/test/odbc_test.hrl new file mode 100644 index 0000000000..87f50043db --- /dev/null +++ b/lib/odbc/test/odbc_test.hrl @@ -0,0 +1,37 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + + +% Default timetrap timeout (set in init_per_testcase). +% This should be set relatively high (10-15 times the expected +% max testcasetime). +-define(default_timeout, ?t:minutes(10)). + +-define(RDBMS, case os:type() of + {unix, sunos} -> + postgres; + {unix,linux} -> + postgres; + {win32, _} -> + sqlserver + end). + +-define(TIMEOUT, 100000). + + diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl new file mode 100644 index 0000000000..92e895eb87 --- /dev/null +++ b/lib/odbc/test/odbc_test_lib.erl @@ -0,0 +1,77 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(odbc_test_lib). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("odbc_test.hrl"). +-include("test_server.hrl"). + +unique_table_name() -> + lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ; + (X, Acc) -> [X |Acc] end, + [], atom_to_list(node()))). + +match_float(Float, Match, Delta) -> + (Float < Match + Delta) and (Float > Match - Delta). + +odbc_check() -> + case erlang:system_info(wordsize) of + 4 -> + case test_server:os_type() of + {unix, sunos} -> + ok; + {unix, linux} -> + ok; + {win32, _} -> + ok; + Other -> + lists:flatten( + io_lib:format("Platform not supported: ~w", + [Other])) + end; + Other -> + case test_server:os_type() of + {unix, linux} -> + ok; + Platform -> + lists:flatten( + io_lib:format("Word on platform ~w size" + " ~w not supported", [Other, + Platform])) + end + end. + +check_row_count(Count, Count) -> + test_server:format("Correct row count Count: ~p~n", [Count]), + true; +check_row_count(_, undefined) -> + test_server:format("Undefined row count ~n", []), + true; +check_row_count(Expected, Count) -> + test_server:format("Incorrect row count Expected ~p Got ~p~n", + [Expected, Count]), + false. + +to_upper(List) -> + lists:map(fun(Str) -> string:to_upper(Str) end, List). diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl new file mode 100644 index 0000000000..ebf6dbb6bf --- /dev/null +++ b/lib/odbc/test/oracle.erl @@ -0,0 +1,246 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(oracle). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +%------------------------------------------------------------------------- +connection_string() -> + "DSN=Oracle8;UID=odbctest". + +%------------------------------------------------------------------------- +insert_result() -> + {selected,["ID","DATA"],[{"1","bar"}]}. + +update_result() -> + {selected,["ID","DATA"],[{"1","foo"}]}. + +selected_ID(N, next) -> + {selected,["ID"],[{integer_to_list(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 oracle 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. %% Should be 255 acording to manual but empirical tests say 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 varchar2(" ++ integer_to_list(Size) ++ "))". + +%------------------------------------------------------------------------- +text_min() -> + 1. +text_max() -> + 2147483646. % 2147483647. %% 2^31 - 1 + +create_text_table() -> + " (FIELD long)". %Oracle long is variable length char data + +%------------------------------------------------------------------------- +create_unicode_table() -> + " (FIELD nvarchar(50))". + +%------------------------------------------------------------------------- +create_timestamp_table() -> + " (FIELD DATETIME)". + +%------------------------------------------------------------------------- +tiny_int_min() -> + -999. +tiny_int_max() -> + 999. + +create_tiny_int_table() -> + " (FIELD number(3, 0))". + +tiny_int_min_selected() -> + {selected,["FIELD"],[{-999}]}. + +tiny_int_max_selected() -> + {selected,["FIELD"], [{999}]}. + +%------------------------------------------------------------------------- +small_int_min() -> + -99999. +small_int_max() -> + 99999. + +create_small_int_table() -> + " (FIELD number(5, 0))". + +small_int_min_selected() -> + {selected,["FIELD"],[{-99999}]}. + +small_int_max_selected() -> + {selected,["FIELD"], [{99999}]}. + +%------------------------------------------------------------------------- +int_min() -> + -999999999. +int_max() -> + 999999999. + +create_int_table() -> + " (FIELD number(9, 0))". + +int_min_selected() -> + {selected,["FIELD"],[{-999999999}]}. + +int_max_selected() -> + {selected,["FIELD"], [{999999999}]}. + +%------------------------------------------------------------------------- +big_int_min() -> + -99999999999999999999999999999999999999. + +big_int_max() -> + 99999999999999999999999999999999999999. + +create_big_int_table() -> + " (FIELD number(38,0))". + +big_int_min_selected() -> + {selected,["FIELD"], [{"-99999999999999999999999999999999999999"}]}. + +big_int_max_selected() -> + {selected,["FIELD"], [{"99999999999999999999999999999999999999"}]}. + +%------------------------------------------------------------------------- +float_min() -> + 1.40129846432481707e-45. + +float_max() -> + 3.40282346638528860e+38. + +create_float_table() -> + " (FIELD float(32))". + +float_underflow() -> + "'4.94065645841246544e-324'". +float_overflow() -> + "'1.79769313486231570e+308'". + +float_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"}, {integer_to_list(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"],[{"1", "foobar"}, {"2", "foobar"}, {"3", "baz"}]}. + +param_delete() -> + {selected,["ID","DATA"],[{"3", "baz"}]}. + +param_select() -> + {selected,["ID","DATA"],[{"1", "foo"},{"3", "foo"}]}. + +%------------------------------------------------------------------------- +describe_integer() -> + {ok,[{"INT1",{sql_decimal,38,0}},{"INT2",{sql_decimal,38,0}}, + {"INT3",{sql_decimal,38,0}}]}. + +describe_string() -> + {ok,[{"STR1",{sql_char,10}}, + {"STR2",{sql_char,10}}, + {"STR3",{sql_varchar,10}}, + {"STR4",{sql_varchar,10}}]}. + +describe_floating() -> + {ok,[{"F",sql_double},{"R",sql_double},{"D",sql_double}]}. +describe_dec_num() -> + {ok,[{"DEC",{sql_decimal,9,3}},{"NUM",{sql_decimal,9,2}}]}. diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl new file mode 100644 index 0000000000..169ca26e43 --- /dev/null +++ b/lib/odbc/test/postgres.erl @@ -0,0 +1,294 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(postgres). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +%------------------------------------------------------------------------- +connection_string() -> + case test_server:os_type() of + {unix, sunos} -> + "DSN=Postgres;UID=odbctest"; + {unix, linux} -> + Size = erlang:system_info(wordsize), + 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(_) -> + "DSN=PostgresLinux64;UID=odbctest". + +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_unicode_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() -> + 1.79e-307. +float_max() -> + 1.79e+308. + +create_float_table() -> + " (FIELD float)". + +float_underflow() -> + "1.80e-308". +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,[{"int1",sql_smallint}, + {"int2",sql_integer}, + {"int3",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,15}}]}. +describe_dec_num() -> + {ok,[{"dec",{sql_numeric,9,3}},{"num",{sql_numeric,9,2}}]}. + +describe_timestamp() -> + {ok, [{"field", sql_timestamp}]}. diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl new file mode 100644 index 0000000000..e3fe30e0bc --- /dev/null +++ b/lib/odbc/test/sqlserver.erl @@ -0,0 +1,298 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% +%% 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 online 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. +%% +%% %CopyrightEnd% +%% + +%% + +-module(sqlserver). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +%------------------------------------------------------------------------- +connection_string() -> + "DSN=sql-server;UID=odbctest;PWD=gurka". + +%------------------------------------------------------------------------- +insert_result() -> + {selected,["ID","DATA"],[{1,"bar"}]}. + +update_result() -> + {selected,["ID","DATA"],[{1,"foo"}]}. + +selected_ID(N, _) -> + {selected,["ID"],[{N}]}. + +selected_next_N(1)-> + {selected,["ID"], + [{1}, + {2}, + {3}]}; + +selected_next_N(2)-> + {selected,["ID"], + [{4}, + {5}]}. + +selected_relative_N(1)-> + {selected,["ID"], + [{2}, + {3}, + {4}]}; + +selected_relative_N(2)-> + {selected,["ID"], + [{7}, + {8}]}. + +selected_absolute_N(1)-> + {selected,["ID"], + [{1}, + {2}, + {3}]}; + +selected_absolute_N(2)-> + {selected,["ID"], + [{1}, + {2}, + {3}, + {4}, + {5}]}. + +selected_list_rows() -> + {selected,["ID", "DATA"],[[1, "bar"],[2,"foo"]]}. + +first_list_rows() -> + {selected,["ID", "DATA"],[[1, "bar"]]}. +last_list_rows() -> + {selected,["ID", "DATA"],[[2, "foo"]]}. +prev_list_rows() -> + {selected,["ID", "DATA"],[[1, "bar"]]}. +next_list_rows() -> + {selected,["ID", "DATA"],[[2, "foo"]]}. + +multiple_select()-> + [{selected,["ID", "DATA"],[{1, "bar"},{2, "foo"}]}, + {selected,["DATA"],[{"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() -> + 8000. + +create_fixed_char_table(Size) -> + " (FIELD char(" ++ integer_to_list(Size) ++ "))". + +%------------------------------------------------------------------------- +var_char_min() -> + 1. +var_char_max() -> + 8000. + +create_var_char_table(Size) -> + " (FIELD varchar(" ++ integer_to_list(Size) ++ "))". +%------------------------------------------------------------------------- +text_min() -> + 1. +text_max() -> + 2147483647. %% 2^31 - 1 + +create_text_table() -> + " (FIELD text)". + +%------------------------------------------------------------------------- +create_unicode_table() -> + " (FIELD nvarchar(50))". + +%------------------------------------------------------------------------- +create_timestamp_table() -> + " (FIELD DATETIME)". + +%------------------------------------------------------------------------- +tiny_int_min() -> + 0. +tiny_int_max() -> + 255. + +create_tiny_int_table() -> + " (FIELD tinyint)". + +tiny_int_min_selected() -> + {selected,["FIELD"],[{tiny_int_min()}]}. + +tiny_int_max_selected() -> + {selected,["FIELD"], [{tiny_int_max()}]}. + +%------------------------------------------------------------------------- +small_int_min() -> + -32768. % -2^15 +small_int_max() -> + 32767. % 2^15-1 + +create_small_int_table() -> + " (FIELD smallint)". + +small_int_min_selected() -> + {selected,["FIELD"],[{small_int_min()}]}. + +small_int_max_selected() -> + {selected,["FIELD"], [{small_int_max()}]}. + +%------------------------------------------------------------------------- +int_min() -> + -2147483648. % -2^31 +int_max() -> + 2147483647. % 2^31-1 + +create_int_table() -> + " (FIELD int)". + +int_min_selected() -> + {selected,["FIELD"],[{int_min()}]}. + +int_max_selected() -> + {selected,["FIELD"], [{int_max()}]}. + +%------------------------------------------------------------------------- +big_int_min() -> + -9223372036854775808. % -2^63 +big_int_max() -> + 9223372036854775807. % 2^63-1 + +create_big_int_table() -> + " (FIELD bigint)". + +big_int_min_selected() -> + {selected,["FIELD"],[{integer_to_list(big_int_min())}]}. + +big_int_max_selected() -> + {selected,["FIELD"], [{integer_to_list(big_int_max())}]}. + +%------------------------------------------------------------------------- +bit_false() -> + 0. +bit_true() -> + 1. + +create_bit_table() -> + " (FIELD bit)". + +bit_false_selected() -> + {selected,["FIELD"],[{false}]}. + +bit_true_selected() -> + {selected,["FIELD"], [{true}]}. +%------------------------------------------------------------------------- +float_min() -> + -1.79e+308. +float_max() -> + 1.79e+308. + +float_underflow() -> + "'-1.80e+308'". + +float_overflow() -> + "'-1.80e+308'". + +create_float_table() -> + " (FIELD float)". + +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_tiny_int() -> + {selected,["FIELD"],[{1}, {2}]}. + +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"],[{1, "foobar"}, {2, "foobar"}, {3, "baz"}]}. + +param_delete() -> + {selected,["ID","DATA"],[{3, "baz"}]}. + +param_select() -> + {selected,["ID","DATA"],[{1, "foo"},{3, "foo"}]}. + +%------------------------------------------------------------------------- + +describe_integer() -> + {ok,[{"int1", sql_smallint},{"int2", sql_integer}, + {"int3", 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, 53}}]}. + +describe_dec_num() -> + {ok,[{"dec",{sql_decimal,9,3}},{"num",{sql_numeric,9,2}}]}. + +describe_timestamp() -> + {ok, [{"field", sql_timestamp}]}. -- cgit v1.2.3