1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2002-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: %% 21: 22: -module(odbc_connect_SUITE). 23: 24: %% Note: This directive should only be used in test suites. 25: -compile(export_all). 26: 27: -include_lib("common_test/include/ct.hrl"). 28: -include("test_server_line.hrl"). 29: -include("odbc_test.hrl"). 30: 31: -define(MAX_SEQ_TIMEOUTS, 10). 32: 33: %%-------------------------------------------------------------------- 34: %% all(Arg) -> [Doc] | [Case] | {skip, Comment} 35: %% Arg - doc | suite 36: %% Doc - string() 37: %% Case - atom() 38: %% Name of a test case function. 39: %% Comment - string() 40: %% Description: Returns documentation/test cases in this test suite 41: %% or a skip tuple if the platform is not supported. 42: %%-------------------------------------------------------------------- 43: 44: suite() -> [{ct_hooks,[ts_install_cth]}]. 45: 46: all() -> 47: case odbc_test_lib:odbc_check() of 48: ok -> 49: [not_exist_db, commit, rollback, not_explicit_commit, 50: no_c_node, port_dies, control_process_dies, 51: {group, client_dies}, connect_timeout, timeout, 52: many_timeouts, timeout_reset, disconnect_on_timeout, 53: connection_closed, disable_scrollable_cursors, 54: return_rows_as_lists, api_missuse, extended_errors]; 55: Other -> {skip, Other} 56: end. 57: 58: groups() -> 59: [{client_dies, [], 60: [client_dies_normal, client_dies_timeout, 61: client_dies_error]}]. 62: 63: init_per_group(_GroupName, Config) -> 64: Config. 65: 66: end_per_group(_GroupName, Config) -> 67: Config. 68: 69: 70: %%-------------------------------------------------------------------- 71: %% Function: init_per_suite(Config) -> Config 72: %% Config - [tuple()] 73: %% A list of key/value pairs, holding the test case configuration. 74: %% Description: Initiation before the whole suite 75: %% 76: %% Note: This function is free to add any key/value pairs to the Config 77: %% variable, but should NOT alter/remove any existing entries. 78: %%-------------------------------------------------------------------- 79: init_per_suite(Config) when is_list(Config) -> 80: file:write_file(filename:join([proplists:get_value(priv_dir,Config), 81: "..","..","..","ignore_core_files"]),""), 82: case odbc_test_lib:skip() of 83: true -> 84: {skip, "ODBC not supported"}; 85: false -> 86: case (catch odbc:start()) of 87: ok -> 88: case catch odbc:connect(?RDBMS:connection_string(), 89: [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of 90: {ok, Ref} -> 91: odbc:disconnect(Ref), 92: [{tableName, odbc_test_lib:unique_table_name()} | Config]; 93: _ -> 94: {skip, "ODBC is not properly setup"} 95: end; 96: _ -> 97: {skip,"ODBC not startable"} 98: end 99: end. 100: 101: %%-------------------------------------------------------------------- 102: %% Function: end_per_suite(Config) -> _ 103: %% Config - [tuple()] 104: %% A list of key/value pairs, holding the test case configuration. 105: %% Description: Cleanup after the whole suite 106: %%-------------------------------------------------------------------- 107: end_per_suite(_Config) -> 108: application:stop(odbc). 109: 110: %%-------------------------------------------------------------------- 111: %% Function: init_per_testcase(Case, Config) -> Config 112: %% Case - atom() 113: %% Name of the test case that is about to be run. 114: %% Config - [tuple()] 115: %% A list of key/value pairs, holding the test case configuration. 116: %% 117: %% Description: Initiation before each test case 118: %% 119: %% Note: This function is free to add any key/value pairs to the Config 120: %% variable, but should NOT alter/remove any existing entries. 121: %%-------------------------------------------------------------------- 122: init_per_testcase(_TestCase, Config) -> 123: test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]), 124: Dog = test_server:timetrap(?default_timeout), 125: Temp = lists:keydelete(connection_ref, 1, Config), 126: NewConfig = lists:keydelete(watchdog, 1, Temp), 127: [{watchdog, Dog} | NewConfig]. 128: 129: %%-------------------------------------------------------------------- 130: %% Function: end_per_testcase(Case, Config) -> _ 131: %% Case - atom() 132: %% Name of the test case that is about to be run. 133: %% Config - [tuple()] 134: %% A list of key/value pairs, holding the test case configuration. 135: %% Description: Cleanup after each test case 136: %%-------------------------------------------------------------------- 137: end_per_testcase(_TestCase, Config) -> 138: Table = ?config(tableName, Config), 139: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 140: Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), 141: io:format("Drop table: ~p ~p~n", [Table, Result]), 142: odbc:disconnect(Ref), 143: Dog = ?config(watchdog, Config), 144: test_server:timetrap_cancel(Dog). 145: 146: %%------------------------------------------------------------------------- 147: %% Test cases starts here. 148: %%------------------------------------------------------------------------- 149: commit(doc)-> 150: ["Test the use of explicit commit"]; 151: commit(suite) -> []; 152: commit(Config) -> 153: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 154: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 155: 156: Table = ?config(tableName, Config), 157: TransStr = transaction_support_str(?RDBMS), 158: 159: {updated, _} = 160: odbc:sql_query(Ref, 161: "CREATE TABLE " ++ Table ++ 162: " (ID integer, DATA varchar(10))" ++ TransStr), 163: 164: {updated, 1} = 165: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1,'bar')"), 166: 167: {updated, 1} = 168: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 169: " SET DATA = 'foo' WHERE ID = 1"), 170: 171: ok = odbc:commit(Ref, commit), 172: UpdateResult = ?RDBMS:update_result(), 173: UpdateResult = 174: odbc:sql_query(Ref, "SELECT * FROM " ++ Table), 175: 176: {updated, 1} = 177: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 178: " SET DATA = 'bar' WHERE ID = 1"), 179: ok = odbc:commit(Ref, commit, ?TIMEOUT), 180: InsertResult = ?RDBMS:insert_result(), 181: InsertResult = 182: odbc:sql_query(Ref, "SELECT * FROM " ++ Table), 183: 184: {'EXIT', {function_clause, _}} = 185: (catch odbc:commit(Ref, commit, -1)), 186: 187: ok = odbc:disconnect(Ref). 188: %%------------------------------------------------------------------------- 189: 190: rollback(doc)-> 191: ["Test the use of explicit rollback"]; 192: rollback(suite) -> []; 193: rollback(Config) -> 194: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 195: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 196: 197: Table = ?config(tableName, Config), 198: 199: TransStr = transaction_support_str(?RDBMS), 200: 201: {updated, _} = 202: odbc:sql_query(Ref, 203: "CREATE TABLE " ++ Table ++ 204: " (ID integer, DATA varchar(10))" ++ TransStr), 205: {updated, 1} = 206: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 207: ok = odbc:commit(Ref, commit), 208: 209: {updated, 1} = 210: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 211: " SET DATA = 'foo' WHERE ID = 1"), 212: ok = odbc:commit(Ref, rollback), 213: InsertResult = ?RDBMS:insert_result(), 214: InsertResult = 215: odbc:sql_query(Ref, "SELECT * FROM " ++ Table), 216: {updated, 1} = 217: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 218: " SET DATA = 'foo' WHERE ID = 1"), 219: ok = odbc:commit(Ref, rollback, ?TIMEOUT), 220: InsertResult = ?RDBMS:insert_result(), 221: InsertResult = 222: odbc:sql_query(Ref, "SELECT * FROM " ++ Table), 223: 224: {'EXIT', {function_clause, _}} = 225: (catch odbc:commit(Ref, rollback, -1)), 226: 227: ok = odbc:disconnect(Ref). 228: 229: %%------------------------------------------------------------------------- 230: not_explicit_commit(doc) -> 231: ["Test what happens if you try using commit on a auto_commit connection."]; 232: not_explicit_commit(suite) -> []; 233: not_explicit_commit(_Config) -> 234: {ok, Ref} = 235: odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++ 236: odbc_test_lib:platform_options()), 237: {error, _} = odbc:commit(Ref, commit), 238: ok = odbc:disconnect(Ref). 239: 240: %%------------------------------------------------------------------------- 241: not_exist_db(doc) -> 242: ["Tests valid data format but invalid data in the connection parameters."]; 243: not_exist_db(suite) -> []; 244: not_exist_db(_Config) -> 245: {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", 246: odbc_test_lib:platform_options()), 247: %% So that the odbc control server can be stoped "in the correct way" 248: test_server:sleep(100). 249: 250: %%------------------------------------------------------------------------- 251: no_c_node(doc) -> 252: "Test what happens if the port-program can not be found"; 253: no_c_node(suite) -> []; 254: no_c_node(_Config) -> 255: process_flag(trap_exit, true), 256: Dir = filename:nativename(filename:join(code:priv_dir(odbc), 257: "bin")), 258: FileName1 = filename:nativename(os:find_executable("odbcserver", 259: Dir)), 260: FileName2 = filename:nativename(filename:join(Dir, "odbcsrv")), 261: ok = file:rename(FileName1, FileName2), 262: Result = 263: case catch odbc:connect(?RDBMS:connection_string(), 264: odbc_test_lib:platform_options()) of 265: {error, port_program_executable_not_found} -> 266: ok; 267: Else -> 268: Else 269: end, 270: 271: ok = file:rename(FileName2, FileName1), 272: ok = Result. 273: %%------------------------------------------------------------------------ 274: 275: port_dies(doc) -> 276: "Tests what happens if the port program dies"; 277: port_dies(suite) -> []; 278: port_dies(_Config) -> 279: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 280: {status, _} = process_info(Ref, status), 281: process_flag(trap_exit, true), 282: NamedPorts = [{P, erlang:port_info(P, name)} || P <- erlang:ports()], 283: case [P || {P, {name, Name}} <- NamedPorts, is_odbcserver(Name)] of 284: [Port] -> 285: exit(Port, kill), 286: %% Wait for exit_status from port 5000 ms (will not get a exit 287: %% status in this case), then wait a little longer to make sure 288: %% the port and the controlprocess has had time to terminate. 289: test_server:sleep(10000), 290: undefined = process_info(Ref, status); 291: [] -> 292: ct:fail([erlang:port_info(P, name) || P <- erlang:ports()]) 293: end. 294: 295: 296: %%------------------------------------------------------------------------- 297: control_process_dies(doc) -> 298: "Tests what happens if the Erlang control process dies"; 299: control_process_dies(suite) -> []; 300: control_process_dies(_Config) -> 301: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 302: process_flag(trap_exit, true), 303: NamedPorts = [{P, erlang:port_info(P, name)} || P <- erlang:ports()], 304: case [P || {P, {name, Name}} <- NamedPorts, is_odbcserver(Name)] of 305: [Port] -> 306: {connected, Ref} = erlang:port_info(Port, connected), 307: exit(Ref, kill), 308: test_server:sleep(500), 309: undefined = erlang:port_info(Port, connected); 310: %% Check for c-program still running, how? 311: [] -> 312: ct:fail([erlang:port_info(P, name) || P <- erlang:ports()]) 313: end. 314: 315: %%------------------------------------------------------------------------- 316: client_dies_normal(doc) -> 317: ["Client dies with reason normal."]; 318: client_dies_normal(suite) -> []; 319: client_dies_normal(Config) when is_list(Config) -> 320: Pid = spawn(?MODULE, client_normal, [self()]), 321: 322: MonitorReference = 323: receive 324: {dbRef, Ref} -> 325: MRef = erlang:monitor(process, Ref), 326: Pid ! continue, 327: MRef 328: end, 329: 330: receive 331: {'DOWN', MonitorReference, _Type, _Object, _Info} -> 332: ok 333: after 5000 -> 334: test_server:fail(control_process_not_stopped) 335: end. 336: 337: client_normal(Pid) -> 338: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 339: Pid ! {dbRef, Ref}, 340: receive 341: continue -> 342: ok 343: end, 344: exit(self(), normal). 345: 346: 347: %%------------------------------------------------------------------------- 348: client_dies_timeout(doc) -> 349: ["Client dies with reason timeout."]; 350: client_dies_timeout(suite) -> []; 351: client_dies_timeout(Config) when is_list(Config) -> 352: Pid = spawn(?MODULE, client_timeout, [self()]), 353: 354: MonitorReference = 355: receive 356: {dbRef, Ref} -> 357: MRef = erlang:monitor(process, Ref), 358: Pid ! continue, 359: MRef 360: end, 361: 362: receive 363: {'DOWN', MonitorReference, _Type, _Object, _Info} -> 364: ok 365: after 5000 -> 366: test_server:fail(control_process_not_stopped) 367: end. 368: 369: client_timeout(Pid) -> 370: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 371: Pid ! {dbRef, Ref}, 372: receive 373: continue -> 374: ok 375: end, 376: exit(self(), timeout). 377: 378: 379: %%------------------------------------------------------------------------- 380: client_dies_error(doc) -> 381: ["Client dies with reason error."]; 382: client_dies_error(suite) -> []; 383: client_dies_error(Config) when is_list(Config) -> 384: Pid = spawn(?MODULE, client_error, [self()]), 385: 386: MonitorReference = 387: receive 388: {dbRef, Ref} -> 389: MRef = erlang:monitor(process, Ref), 390: Pid ! continue, 391: MRef 392: end, 393: 394: receive 395: {'DOWN', MonitorReference, _Type, _Object, _Info} -> 396: ok 397: after 5000 -> 398: test_server:fail(control_process_not_stopped) 399: end. 400: 401: client_error(Pid) -> 402: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 403: Pid ! {dbRef, Ref}, 404: receive 405: continue -> 406: ok 407: end, 408: exit(self(), error). 409: 410: 411: %%------------------------------------------------------------------------- 412: connect_timeout(doc) -> 413: ["Test the timeout for the connect function."]; 414: connect_timeout(suite) -> []; 415: connect_timeout(Config) when is_list(Config) -> 416: {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(), 417: [{timeout, 0}] ++ 418: odbc_test_lib:platform_options())), 419: %% Need to return ok here "{'EXIT',timeout} return value" will 420: %% be interpreted as that the testcase has timed out. 421: ok. 422: %%------------------------------------------------------------------------- 423: timeout(doc) -> 424: ["Test that timeouts don't cause unwanted behavior sush as receiving" 425: " an anwser to a previously tiemed out query."]; 426: timeout(suite) -> []; 427: timeout(Config) when is_list(Config) -> 428: 429: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 430: [{auto_commit, off}]), 431: Table = ?config(tableName, Config), 432: 433: TransStr = transaction_support_str(?RDBMS), 434: 435: {updated, _} = 436: odbc:sql_query(Ref, 437: "CREATE TABLE " ++ Table ++ 438: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), 439: 440: {updated, 1} = 441: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 442: 443: ok = odbc:commit(Ref, commit), 444: 445: {updated, 1} = 446: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 447: " SET DATA = 'foo' WHERE ID = 1"), 448: 449: {updated, 1} = 450: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"), 451: 452: Pid = spawn_link(?MODULE, update_table_timeout, [Table, 5000, self()]), 453: 454: receive 455: timout_occurred -> 456: ok = odbc:commit(Ref, commit), 457: Pid ! continue 458: end, 459: 460: receive 461: altered -> 462: ok 463: end, 464: 465: {selected, Fields, [{"foobar"}]} = 466: odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"), 467: ["DATA"] = odbc_test_lib:to_upper(Fields), 468: 469: ok = odbc:commit(Ref, commit), 470: ok = odbc:disconnect(Ref). 471: 472: update_table_timeout(Table, TimeOut, Pid) -> 473: 474: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 475: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 476: UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", 477: 478: case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of 479: {'EXIT', timeout} -> 480: Pid ! timout_occurred; 481: {updated, 1} -> 482: test_server:fail(database_locker_failed) 483: end, 484: 485: receive 486: continue -> 487: ok 488: end, 489: 490: %% Make sure we receive the correct result and not the answer 491: %% to the previous query. 492: {selected, Fields, [{"baz"}]} = 493: odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), 494: ["DATA"] = odbc_test_lib:to_upper(Fields), 495: 496: %% Do not check {updated, 1} as some drivers will return 0 497: %% even though the update is done, which is checked by the test 498: %% case when the altered message is recived. 499: {updated, _} = odbc:sql_query(Ref, UpdateQuery, TimeOut), 500: 501: ok = odbc:commit(Ref, commit), 502: 503: Pid ! altered, 504: 505: ok = odbc:disconnect(Ref). 506: %%------------------------------------------------------------------------- 507: many_timeouts(doc) -> 508: ["Tests that many consecutive timeouts lead to that the connection " 509: "is shutdown."]; 510: many_timeouts(suite) -> []; 511: many_timeouts(Config) when is_list(Config) -> 512: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 513: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 514: 515: Table = ?config(tableName, Config), 516: TransStr = transaction_support_str(?RDBMS), 517: 518: {updated, _} = 519: odbc:sql_query(Ref, 520: "CREATE TABLE " ++ Table ++ 521: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), 522: 523: {updated, 1} = 524: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 525: 526: ok = odbc:commit(Ref, commit), 527: 528: {updated, 1} = 529: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 530: " SET DATA = 'foo' WHERE ID = 1"), 531: 532: _Pid = spawn_link(?MODULE, update_table_many_timeouts, 533: [Table, 5000, self()]), 534: 535: receive 536: many_timeouts_occurred -> 537: ok 538: end, 539: 540: ok = odbc:commit(Ref, commit), 541: ok = odbc:disconnect(Ref). 542: 543: 544: update_table_many_timeouts(Table, TimeOut, Pid) -> 545: 546: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 547: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 548: UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", 549: 550: ok = loop_many_timouts(Ref, UpdateQuery, TimeOut), 551: 552: Pid ! many_timeouts_occurred, 553: 554: ok = odbc:disconnect(Ref). 555: 556: 557: loop_many_timouts(Ref, UpdateQuery, TimeOut) -> 558: case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of 559: {'EXIT',timeout} -> 560: loop_many_timouts(Ref, UpdateQuery, TimeOut); 561: {updated, 1} -> 562: test_server:fail(database_locker_failed); 563: {error, connection_closed} -> 564: ok 565: end. 566: %%------------------------------------------------------------------------- 567: timeout_reset(doc) -> 568: ["Check that the number of consecutive timouts is reset to 0 when " 569: "a successful call to the database is made."]; 570: timeout_reset(suite) -> []; 571: timeout_reset(Config) when is_list(Config) -> 572: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 573: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 574: Table = ?config(tableName, Config), 575: TransStr = transaction_support_str(?RDBMS), 576: 577: {updated, _} = 578: odbc:sql_query(Ref, 579: "CREATE TABLE " ++ Table ++ 580: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), 581: 582: {updated, 1} = 583: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 584: 585: ok = odbc:commit(Ref, commit), 586: 587: {updated, 1} = 588: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 589: " SET DATA = 'foo' WHERE ID = 1"), 590: 591: {updated, 1} = 592: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"), 593: 594: 595: Pid = spawn_link(?MODULE, update_table_timeout_reset, 596: [Table, 5000, self()]), 597: 598: receive 599: many_timeouts_occurred -> 600: ok 601: end, 602: 603: ok = odbc:commit(Ref, commit), 604: Pid ! continue, 605: 606: receive 607: altered -> 608: ok 609: end, 610: 611: {selected, Fields, [{"foobar"}]} = 612: odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"), 613: ["DATA"] = odbc_test_lib:to_upper(Fields), 614: 615: ok = odbc:commit(Ref, commit), 616: ok = odbc:disconnect(Ref). 617: 618: update_table_timeout_reset(Table, TimeOut, Pid) -> 619: 620: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 621: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 622: UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", 623: 624: ok = loop_timout_reset(Ref, UpdateQuery, TimeOut, 625: ?MAX_SEQ_TIMEOUTS-1), 626: 627: Pid ! many_timeouts_occurred, 628: 629: receive 630: continue -> 631: ok 632: end, 633: 634: {selected, Fields, [{"baz"}]} = 635: odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), 636: ["DATA"] = odbc_test_lib:to_upper(Fields), 637: 638: %% Do not check {updated, 1} as some drivers will return 0 639: %% even though the update is done, which is checked by the test 640: %% case when the altered message is recived. 641: {updated, _} = odbc:sql_query(Ref, UpdateQuery, TimeOut), 642: 643: ok = odbc:commit(Ref, commit), 644: 645: Pid ! altered, 646: 647: ok = odbc:disconnect(Ref). 648: 649: loop_timout_reset(_, _, _, 0) -> 650: ok; 651: 652: loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) -> 653: case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of 654: {'EXIT',timeout} -> 655: loop_timout_reset(Ref, UpdateQuery, 656: TimeOut, NumTimeouts - 1); 657: {updated, 1} -> 658: test_server:fail(database_locker_failed); 659: {error, connection_closed} -> 660: test_server:fail(connection_closed_premature) 661: end. 662: 663: %%------------------------------------------------------------------------- 664: 665: disconnect_on_timeout(doc) -> 666: ["Check that disconnect after a time out works properly"]; 667: disconnect_on_timeout(suite) -> []; 668: disconnect_on_timeout(Config) when is_list(Config) -> 669: 670: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 671: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 672: Table = ?config(tableName, Config), 673: TransStr = transaction_support_str(?RDBMS), 674: 675: {updated, _} = 676: odbc:sql_query(Ref, 677: "CREATE TABLE " ++ Table ++ 678: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), 679: 680: {updated, 1} = 681: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 682: 683: ok = odbc:commit(Ref, commit), 684: 685: {updated, 1} = 686: odbc:sql_query(Ref, "UPDATE " ++ Table ++ 687: " SET DATA = 'foo' WHERE ID = 1"), 688: 689: 690: _Pid = spawn_link(?MODULE, update_table_disconnect_on_timeout, 691: [Table, 5000, self()]), 692: receive 693: ok -> 694: ok = odbc:commit(Ref, commit); 695: nok -> 696: test_server:fail(database_locker_failed) 697: end. 698: 699: update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> 700: 701: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 702: [{auto_commit, off}] ++ odbc_test_lib:platform_options()), 703: UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", 704: 705: case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of 706: {'EXIT', timeout} -> 707: ok = odbc:disconnect(Ref), 708: Pid ! ok; 709: {updated, 1} -> 710: Pid ! nok 711: end. 712: 713: %%------------------------------------------------------------------------- 714: connection_closed(doc) -> 715: ["Checks that you get an appropriate error message if you try to" 716: " use a connection that has been closed"]; 717: connection_closed(suite) -> []; 718: connection_closed(Config) when is_list(Config) -> 719: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 720: 721: Table = ?config(tableName, Config), 722: {updated, _} = 723: odbc:sql_query(Ref, 724: "CREATE TABLE " ++ Table ++ 725: " (ID integer, DATA char(10), PRIMARY KEY(ID))"), 726: 727: ok = odbc:disconnect(Ref), 728: 729: {error, connection_closed} = 730: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 731: {error, connection_closed} = 732: odbc:select_count(Ref, "SELECT * FROM " ++ Table), 733: {error, connection_closed} = odbc:first(Ref), 734: {error, connection_closed} = odbc:last(Ref), 735: {error, connection_closed} = odbc:next(Ref), 736: {error, connection_closed} = odbc:prev(Ref), 737: {error, connection_closed} = odbc:select(Ref, next, 3), 738: {error, connection_closed} = odbc:commit(Ref, commit). 739: 740: %%------------------------------------------------------------------------- 741: disable_scrollable_cursors(doc) -> 742: ["Test disabling of scrollable cursors."]; 743: disable_scrollable_cursors(suite) -> []; 744: disable_scrollable_cursors(Config) when is_list(Config) -> 745: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 746: [{scrollable_cursors, off}]), 747: 748: Table = ?config(tableName, Config), 749: 750: {updated, _} = 751: odbc:sql_query(Ref, 752: "CREATE TABLE " ++ Table ++ 753: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), 754: 755: {updated, _} = 756: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 757: 758: {ok, _} = odbc:select_count(Ref, "SELECT ID FROM " ++ Table), 759: 760: NextResult = ?RDBMS:selected_ID(1, next), 761: 762: test_server:format("Expected: ~p~n", [NextResult]), 763: 764: Result = odbc:next(Ref), 765: test_server:format("Got: ~p~n", [Result]), 766: NextResult = Result, 767: 768: {error, scrollable_cursors_disabled} = odbc:first(Ref), 769: {error, scrollable_cursors_disabled} = odbc:last(Ref), 770: {error, scrollable_cursors_disabled} = odbc:prev(Ref), 771: {error, scrollable_cursors_disabled} = 772: odbc:select(Ref, {relative, 2}, 5), 773: {error, scrollable_cursors_disabled} = 774: odbc:select(Ref, {absolute, 2}, 5), 775: 776: {selected, _ColNames,[]} = odbc:select(Ref, next, 1). 777: 778: %%------------------------------------------------------------------------- 779: return_rows_as_lists(doc)-> 780: ["Test the option that a row may be returned as a list instead " 781: "of a tuple. Too be somewhat backward compatible."]; 782: return_rows_as_lists(suite) -> []; 783: return_rows_as_lists(Config) when is_list(Config) -> 784: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), 785: [{tuple_row, off}] ++ odbc_test_lib:platform_options()), 786: 787: Table = ?config(tableName, Config), 788: 789: {updated, _} = 790: odbc:sql_query(Ref, 791: "CREATE TABLE " ++ Table ++ 792: " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), 793: 794: {updated, _} = 795: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), 796: 797: {updated, _} = 798: odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'foo')"), 799: 800: ListRows = ?RDBMS:selected_list_rows(), 801: ListRows = 802: odbc:sql_query(Ref, "SELECT * FROM " ++ Table), 803: 804: {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), 805: 806: case proplists:get_value(scrollable_cursors, odbc_test_lib:platform_options()) of 807: off -> 808: Next = ?RDBMS:next_list_rows(), 809: Next = odbc:next(Ref); 810: _ -> 811: First = ?RDBMS:first_list_rows(), 812: Last = ?RDBMS:last_list_rows(), 813: Prev = ?RDBMS:prev_list_rows(), 814: Next = ?RDBMS:next_list_rows(), 815: 816: Last = odbc:last(Ref), 817: Prev = odbc:prev(Ref), 818: First = odbc:first(Ref), 819: Next = odbc:next(Ref) 820: end. 821: 822: %%------------------------------------------------------------------------- 823: 824: api_missuse(doc)-> 825: ["Test that behaviour of the control process if the api is abused"]; 826: api_missuse(suite) -> []; 827: api_missuse(Config) when is_list(Config)-> 828: 829: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 830: %% Serious programming fault, connetion will be shut down 831: gen_server:call(Ref, {self(), foobar, 10}, infinity), 832: test_server:sleep(10), 833: undefined = process_info(Ref, status), 834: 835: {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), 836: odbc_test_lib:platform_options()), 837: %% Serious programming fault, connetion will be shut down 838: gen_server:cast(Ref2, {self(), foobar, 10}), 839: test_server:sleep(10), 840: undefined = process_info(Ref2, status), 841: 842: {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), 843: odbc_test_lib:platform_options()), 844: %% Could be an innocent misstake the connection lives. 845: Ref3 ! foobar, 846: test_server:sleep(10), 847: {status, _} = process_info(Ref3, status). 848: 849: transaction_support_str(mysql) -> 850: "ENGINE = InnoDB"; 851: transaction_support_str(_) -> 852: "". 853: 854: 855: %%------------------------------------------------------------------------- 856: extended_errors(doc)-> 857: ["Test the extended errors connection option: When off; the old behaviour of just an error " 858: "string is returned on error. When on, the error string is replaced by a 3 element tuple " 859: "that also exposes underlying ODBC provider error codes."]; 860: extended_errors(suite) -> []; 861: extended_errors(Config) when is_list(Config)-> 862: Table = ?config(tableName, Config), 863: {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), 864: {updated, _} = odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))"), 865: 866: % Error case WITHOUT extended errors on... 867: case odbc:sql_query(Ref, "create table " ++ Table ++" ( id integer, data varchar(10))") of 868: {error, ErrorString} when is_list(ErrorString) -> ok 869: end, 870: 871: % Now the test case with extended errors on - This should return a tuple, not a list/string now. 872: % The first element is a string that is the ODBC error string; the 2nd element is a native integer error 873: % code passed from the underlying provider driver. The last is the familiar old error string. 874: % We can't check the actual error code; as each different underlying provider will return 875: % a different value - So we just check the return types at least. 876: {ok, RefExtended} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options() ++ [{extended_errors, on}]), 877: case odbc:sql_query(RefExtended, "create table " ++ Table ++" ( id integer, data varchar(10))") of 878: {error, {ODBCCodeString, NativeCodeNum, ShortErrorString}} when is_list(ODBCCodeString), is_number(NativeCodeNum), is_list(ShortErrorString) -> ok 879: end, 880: 881: ok = odbc:disconnect(Ref), 882: ok = odbc:disconnect(RefExtended). 883: 884: 885: is_odbcserver(Name) -> 886: case re:run(Name, "odbcserver") of 887: {match, _} -> 888: true; 889: _ -> 890: false 891: end. 892: