[m-dev.] for review: handle exceptions in the ODBC interface

Simon Taylor stayl at cs.mu.OZ.AU
Tue Mar 21 14:55:00 AEDT 2000



Estimated hours taken: 2

Handle exceptions thrown by the closure passed to odbc__transaction.

extras/odbc/odbc.m:
	Abort the transaction if the closure passed to odbc__transaction
	throws an exception, and rethrow the exception to the caller.

extras/odbc/odbc_test.m:
	Add a transaction which throws an exception to the test.

extras/odbc/Mmakefile:
	Minor changes to the set-up for iODBC 2.50.3.


Index: Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/extras/odbc/Mmakefile,v
retrieving revision 1.2
diff -u -u -r1.2 Mmakefile
--- Mmakefile	1997/10/07 12:09:07	1.2
+++ Mmakefile	2000/03/21 03:42:38
@@ -22,7 +22,7 @@
 #MODBC_DB=MODBC_SQL_SERVER
 
 # Pathname for iODBC (only for MODBC_IODBC)
-IODBC_DIR=/home/mercury1/stayl/iodbc/iODBC-2.12
+IODBC_DIR=/home/aditi_db1/stayl/libiodbc-2.50.3
 
 # Pathname for the ODBC SDK (only for MODBC_MS)
 ODBC_SDK_DIR=/odbcsdk
@@ -34,9 +34,9 @@
 	MLLIBS=-lodbc32
 else 
 	ODBC_LIB_DIR=$(IODBC_DIR)/lib
-	ODBC_INCL_DIR=$(IODBC_DIR)
+	ODBC_INCL_DIR=$(IODBC_DIR)/include
 	# note: on a DEC Alpha using OSF1 remove the -ldl.
-	MLLIBS=-L$(ODBC_LIB_DIR) -liodbc -ldl
+	MLLIBS=-L$(ODBC_LIB_DIR) -R$(ODBC_LIB_DIR) -liodbc -ldl
 endif
 
 MAIN_TARGET=odbc_test
Index: odbc.m
===================================================================
RCS file: /home/mercury1/repository/mercury/extras/odbc/odbc.m,v
retrieving revision 1.9
diff -u -u -r1.9 odbc.m
--- odbc.m	1999/11/01 06:20:58	1.9
+++ odbc.m	2000/03/21 03:46:51
@@ -23,9 +23,11 @@
 %	<http://www.cs.mu.OZ.AU/publications/tr_db/mu_96_45_cover.ps.gz>
 %	and <http://www.cs.mu.OZ.AU/publications/tr_db/mu_96_45.ps.gz>.
 %
-% This has been tested with MySQL 3.20.19 and iODBC 2.12 under Solaris 2.5,
-% and with Microsoft SQL Server 6.5 under Windows NT 4.0 with the GNU-Win32 
-% tools beta 17.1.
+% This has been tested using the following platforms:
+% - MySQL 3.20.19 and iODBC 2.12 under Solaris 2.5
+% - MySQL 3.22.32 and iODBC 2.50.3 under Solaris 2.6
+% - Microsoft SQL Server 6.5 under Windows NT 4.0 with the
+%	GNU-Win32 tools beta 17.1
 %
 % Notes:
 %
@@ -76,8 +78,20 @@
 
 :- type odbc__state.
 
-	% Perform the closure atomically on the given database connection.
-	% On error, the transaction is rolled back.
+	% odbc__transaction(Source, UserName, Password, Transaction, Result).
+	%
+	% Open a connection to `Source' using the given `UserName'
+	% and `Password', perform `Transaction' within a transaction
+	% using that connection, then close the connection.
+	%
+	% `Result' is `ok(Results) - Messages' if the transaction
+	% succeeds or `error - Messages' if the transaction is aborted.
+	% Whether updates are rolled back if the transaction aborts depends
+	% on the database. MySQL will not roll back updates.
+	%
+	% If `Transaction' throws an exception, odbc__transaction will
+	% attempt to roll back the transaction, and will then rethrow
+	% the exception to the caller.
 :- pred odbc__transaction(odbc__data_source, odbc__user_name, odbc__password,
 		odbc__transaction(T), odbc__result(T), io__state, io__state).
 :- mode odbc__transaction(in, in, in, odbc__transaction, out, di, uo) is det.
@@ -265,7 +279,7 @@
 %-----------------------------------------------------------------------------%
 :- implementation.
 
-:- import_module assoc_list, int, require, std_util, string.
+:- import_module assoc_list, exception, int, require, std_util, string.
 
 %-----------------------------------------------------------------------------%
 
@@ -398,7 +412,8 @@
 static Word	odbc_message_list;
 
 static void odbc_transaction_c_code(Word type_info, Word Connection, 
-			Word Closure, Word *Results, Word *Status, 
+			Word Closure, Word *Results, Word *GotMercuryException,
+			Word *Exception, Word *Status, 
 			Word *Msgs, Word IO0, Word *IO);
 static bool odbc_check(SQLHENV, SQLHDBC, SQLHSTMT, SQLRETURN);
 
@@ -419,11 +434,22 @@
 
 		% Do the transaction.
 		odbc__transaction_2(Connection, Closure, Data, 
-			Status, RevMessages),
+			GotMercuryException, Exception, Status, RevMessages),
 		{ list__reverse(RevMessages, TransMessages) },
 
 		odbc__close_connection(Connection,
 			CloseStatus - CloseMessages),
+
+		%
+		% Pass on any exception that was found while
+		% processing the transaction.
+		%
+		( { GotMercuryException = 1 } ->
+			{ rethrow(exception(Exception)) }
+		;
+			[]
+		),
+
 		{ list__condense(
 			[ConnectMessages, TransMessages, CloseMessages], 
 			Messages) },
@@ -439,15 +465,15 @@
 
 :- pred odbc__transaction_2(odbc__connection,
 		pred(T, odbc__state, odbc__state), T, 
-		int, list(odbc__message), io__state, io__state).  
+		int, univ, int, list(odbc__message), io__state, io__state).  
 :- mode odbc__transaction_2(in, pred(out, di, uo) is det, 
-		out, out, out, di, uo) is det.
+		out, out, out, out, out, di, uo) is det.
 
 :- pragma c_code(
 		odbc__transaction_2(Connection::in, 
 			Closure::pred(out, di, uo) is det,
-			Results::out, Status::out, Msgs::out,
-			IO0::di, IO::uo),
+			Results::out, GotMercuryException::out, Exception::out,
+			Status::out, Msgs::out, IO0::di, IO::uo),
 		may_call_mercury,
 "
 	/*
@@ -460,7 +486,8 @@
 	*/
 	save_transient_registers();
 	odbc_transaction_c_code(TypeInfo_for_T, Connection, Closure, 
-			&Results, &Status, &Msgs, IO0, &IO);
+			&Results, &GotMercuryException, &Exception, 
+			&Status, &Msgs, IO0, &IO);
 	restore_transient_registers();
 ").
 
@@ -469,8 +496,8 @@
 "
 static void 
 odbc_transaction_c_code(Word TypeInfo_for_T, Word Connection, 
-		Word Closure, Word *Results, Word *Status, 
-		Word *Msgs, Word IO0, Word *IO)
+		Word Closure, Word *Results, Word *GotMercuryException,
+		Word *Exception, Word *Status, Word *Msgs, Word IO0, Word *IO)
 {
 	Word DB0 = (Word) 0;
 	Word DB = (Word) 0;
@@ -496,19 +523,32 @@
 	** MODBC_odbc__do_transaction() must be declared volatile.
 	*/
 
-	MODBC_odbc__do_transaction(TypeInfo_for_T, Closure, Results, DB0, &DB);
+	MODBC_odbc__do_transaction(TypeInfo_for_T, Closure,
+		GotMercuryException, Results, Exception, DB0, &DB);
 
 	/*
 	** MR_longjmp() cannot be called after here.
 	*/
 
-	rc = SQLTransact(odbc_env_handle, odbc_connection, SQL_COMMIT);
+	if (*GotMercuryException == 0) {
 
-	if (! odbc_check(odbc_env_handle, odbc_connection, 
-			SQL_NULL_HSTMT, rc)) {
-		goto transaction_error;
+		rc = SQLTransact(odbc_env_handle, odbc_connection, SQL_COMMIT);
+
+		if (! odbc_check(odbc_env_handle, odbc_connection, 
+				SQL_NULL_HSTMT, rc)) {
+			goto transaction_error;
+		}
+
+	} else {
+
+		/*
+		** There was a Mercury exception -- abort the transaction.
+		*/
+		DEBUG(printf(
+			""Mercury exception in transaction: aborting\\n""));
+		SQLTransact(odbc_env_handle, odbc_connection, SQL_ROLLBACK);
 	}
-	
+		
 	*Status = SQL_SUCCESS;
 
 	goto transaction_done;
@@ -522,6 +562,8 @@
 
 	*Status = odbc_ret_code;
 
+	*GotMercuryException = 0;
+
 	rc = SQLTransact(odbc_env_handle, odbc_connection, SQL_ROLLBACK);
 
 	odbc_check(odbc_env_handle, odbc_connection, SQL_NULL_HSTMT, rc);
@@ -543,15 +585,40 @@
 %-----------------------------------------------------------------------------%
 
 	% Call the transaction closure.
-:- pred odbc__do_transaction(odbc__transaction(T), T,
+:- pred odbc__do_transaction(odbc__transaction(T), int, T, univ,
 		odbc__state, odbc__state).
-:- mode odbc__do_transaction(odbc__transaction, out, di, uo) is det.
+:- mode odbc__do_transaction(odbc__transaction,
+		out, out, out, di, uo) is cc_multi.
 
-:- pragma export(odbc__do_transaction(odbc__transaction, out, di, uo), 
+:- pragma export(odbc__do_transaction(odbc__transaction,
+		out, out, out, di, uo), 
 		"MODBC_odbc__do_transaction").
+
+odbc__do_transaction(Closure, GotException, Results,
+		Exception, State0, State) :-
+	try((pred(TryResult::out) is det :-
+		unsafe_promise_unique(State0, State1),
+		Closure(Result, State1, ResultState),
+		TryResult = Result - ResultState
+	), ExceptResult),
+	(
+		ExceptResult = succeeded(Results - State2),
+		unsafe_promise_unique(State2, State),
+		make_dummy_value(Exception),
+		GotException = 0
+	;
+		ExceptResult = exception(Exception),
+		make_dummy_value(Results),
+		unsafe_promise_unique(State0, State),
+		GotException = 1
+	).
 
-odbc__do_transaction(Closure, Results) -->
-	call(Closure, Results).
+	% Produce a value which is never looked at, for returning
+	% discriminated unions to C.
+:- pred make_dummy_value(T::out) is det.
+:- pragma c_code(make_dummy_value(T::out),
+		[will_not_call_mercury, thread_safe],
+		"T = 0;").
 
 %-----------------------------------------------------------------------------%
 
@@ -958,6 +1025,7 @@
 static SQLRETURN odbc_do_cleanup_statement(MODBC_Statement *stat);
 static size_t sql_type_to_size(SWORD sql_type, UDWORD cbColDef, 
 		SWORD ibScale, SWORD fNullable);
+static MODBC_AttrType sql_type_to_attribute_type(SWORD sql_type);
 static SWORD attribute_type_to_sql_c_type(MODBC_AttrType AttrType);
 static bool is_variable_length_sql_type(SWORD);
 void odbc_do_get_data(MODBC_Statement *stat, int column_id);
Index: odbc_test.m
===================================================================
RCS file: /home/mercury1/repository/mercury/extras/odbc/odbc_test.m,v
retrieving revision 1.2
diff -u -u -r1.2 odbc_test.m
--- odbc_test.m	1997/10/04 23:13:32	1.2
+++ odbc_test.m	2000/03/21 03:26:55
@@ -12,12 +12,12 @@
 
 :- import_module io.
 
-:- pred main(io__state::di, io__state::uo) is det.
+:- pred main(io__state::di, io__state::uo) is cc_multi.
 
 %-----------------------------------------------------------------------------%
 :- implementation.
 
-:- import_module list, string, std_util.
+:- import_module exception, list, string, std_util.
 :- import_module odbc.
 
 main -->
@@ -50,6 +50,7 @@
 	),
 	io__write_list(TableMessages, "\n", io__write),
 	io__nl,
+
 	odbc__transaction("test", "", "", test_trans, 
 		TransResult - TransMessages),
 	( 
@@ -66,13 +67,40 @@
 		io__write_string("error in transaction:\n")
 	),
 	io__write_list(TransMessages, "\n", io__write),
-	io__nl.
+	io__nl,
+
+	try_io(odbc__transaction("test", "", "", test_trans_2),
+		ExceptionResult),
+	(
+		{ ExceptionResult = succeeded(Results2) },
+		io__set_exit_status(1),
+		io__write_string("Error: expected exception, got results:"),
+		io__write(Results2),
+		io__nl
+	;
+		{ ExceptionResult = exception(Exception) },
+		{ det_univ_to_type(Exception, ExceptionString) },
+		io__write_string("Got exception: "),
+		io__write_string(ExceptionString),
+		io__nl
+	).
 
 :- pred test_trans(list(odbc__row)::out,
 		odbc__state::di, odbc__state::uo) is det.
 
 test_trans(Results) -->
 	odbc__solutions("select * from test", Results).
+
+:- pred test_trans_2(list(odbc__row)::out,
+		odbc__state::di, odbc__state::uo) is det.
+
+test_trans_2(Results) -->
+	odbc__solutions("select * from test", Results),
+	( { semidet_succeed } ->
+		{ throw("exception in test_trans_2") }
+	;	
+		[]
+	).
 
 :- pred output_results(list(odbc__row)::in,
 		io__state::di, io__state::uo) is det.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list