3737#include <caml/signals.h>
3838
3939#include <sqlite3.h>
40+ #include <pthread.h>
4041
4142#if __GNUC__ >= 3
4243# define inline inline __attribute__ ((always_inline))
@@ -105,6 +106,46 @@ typedef struct stmt_wrap {
105106} stmt_wrap ;
106107
107108
109+ /* Handling of exceptions in user-defined SQL-functions */
110+
111+ /* For propagating exceptions from user-defined SQL-functions */
112+ static pthread_key_t user_exception_key ;
113+
114+ typedef struct user_exception { value exn ; } user_exception ;
115+
116+ static inline void create_user_exception (value exn )
117+ {
118+ user_exception * user_exn = malloc (sizeof (user_exception ));
119+ user_exn -> exn = exn ;
120+ caml_register_global_root (& user_exn -> exn );
121+ pthread_setspecific (user_exception_key , user_exn );
122+ }
123+
124+ static inline void destroy_user_exception (void * user_exc_ )
125+ {
126+ user_exception * user_exn = user_exc_ ;
127+ caml_remove_global_root (& user_exn -> exn );
128+ free (user_exn );
129+ }
130+
131+ static inline void maybe_raise_user_exception (int rc )
132+ {
133+ if (rc == SQLITE_ERROR ) {
134+ user_exception * user_exn = pthread_getspecific (user_exception_key );
135+
136+ if (user_exn != NULL ) {
137+ CAMLparam0 ();
138+ CAMLlocal1 (v_exn );
139+ v_exn = user_exn -> exn ;
140+ destroy_user_exception (user_exn );
141+ pthread_setspecific (user_exception_key , NULL );
142+ caml_raise (v_exn );
143+ CAMLnoreturn ;
144+ }
145+ }
146+ }
147+
148+
108149/* Macros to access the wrapper structures stored in the custom blocks */
109150
110151#define Sqlite3_val (x ) (*((db_wrap **) (Data_custom_val(x))))
@@ -120,11 +161,11 @@ static value *caml_sqlite3_RangeError = NULL;
120161static inline void raise_with_two_args (value v_tag , value v_arg1 , value v_arg2 )
121162{
122163 CAMLparam3 (v_tag , v_arg1 , v_arg2 );
123- value v_exc = caml_alloc_small (3 , 0 );
124- Field (v_exc , 0 ) = v_tag ;
125- Field (v_exc , 1 ) = v_arg1 ;
126- Field (v_exc , 2 ) = v_arg2 ;
127- caml_raise (v_exc );
164+ value v_exn = caml_alloc_small (3 , 0 );
165+ Field (v_exn , 0 ) = v_tag ;
166+ Field (v_exn , 1 ) = v_arg1 ;
167+ Field (v_exn , 2 ) = v_arg2 ;
168+ caml_raise (v_exn );
128169 CAMLnoreturn ;
129170}
130171
@@ -215,6 +256,7 @@ CAMLprim value caml_sqlite3_init(value __unused v_unit)
215256 caml_sqlite3_InternalError = caml_named_value ("Sqlite3.InternalError" );
216257 caml_sqlite3_Error = caml_named_value ("Sqlite3.Error" );
217258 caml_sqlite3_RangeError = caml_named_value ("Sqlite3.RangeError" );
259+ pthread_key_create (& user_exception_key , destroy_user_exception );
218260 return Val_unit ;
219261}
220262
@@ -521,6 +563,7 @@ CAMLprim value caml_sqlite3_exec(value v_db, value v_maybe_cb, value v_sql)
521563 caml_leave_blocking_section ();
522564
523565 if (rc == SQLITE_ABORT ) caml_raise (* cbx .exn );
566+ maybe_raise_user_exception (rc );
524567
525568 CAMLreturn (Val_rc (rc ));
526569}
@@ -570,6 +613,7 @@ CAMLprim value caml_sqlite3_exec_no_headers(value v_db, value v_cb, value v_sql)
570613 caml_leave_blocking_section ();
571614
572615 if (rc == SQLITE_ABORT ) caml_raise (* cbx .exn );
616+ maybe_raise_user_exception (rc );
573617
574618 CAMLreturn (Val_rc (rc ));
575619}
@@ -633,6 +677,8 @@ CAMLprim value caml_sqlite3_exec_not_null(value v_db, value v_cb, value v_sql)
633677 if (* cbx .exn != 0 ) caml_raise (* cbx .exn );
634678 else raise_sqlite3_Error ("Null element in row" );
635679 }
680+ maybe_raise_user_exception (rc );
681+
636682 CAMLreturn (Val_rc (rc ));
637683}
638684
@@ -692,6 +738,8 @@ CAMLprim value caml_sqlite3_exec_not_null_no_headers(
692738 if (* cbx .exn != 0 ) caml_raise (* cbx .exn );
693739 else raise_sqlite3_Error ("Null element in row" );
694740 }
741+ maybe_raise_user_exception (rc );
742+
695743 CAMLreturn (Val_rc (rc ));
696744}
697745
@@ -847,7 +895,6 @@ CAMLprim value caml_sqlite3_bind(value v_stmt, value v_index, value v_data)
847895 String_val (v_field ),
848896 caml_string_length (v_field ),
849897 SQLITE_TRANSIENT ));
850- case 4 : return Val_rc (SQLITE_ERROR );
851898 }
852899 }
853900 return Val_rc (SQLITE_ERROR );
@@ -1002,14 +1049,15 @@ static inline value caml_sqlite3_wrap_values(int argc, sqlite3_value **args)
10021049 }
10031050}
10041051
1005- static inline void exception_result (sqlite3_context * ctx )
1052+ static inline void exception_result (sqlite3_context * ctx , value v_exn )
10061053{
10071054 sqlite3_result_error (ctx , "OCaml callback raised an exception" , -1 );
1055+ create_user_exception (v_exn );
10081056}
10091057
10101058static inline void set_sqlite3_result (sqlite3_context * ctx , value v_res )
10111059{
1012- if (Is_exception_result (v_res )) exception_result (ctx );
1060+ if (Is_exception_result (v_res )) exception_result (ctx , v_res );
10131061 else if (Is_long (v_res )) sqlite3_result_null (ctx );
10141062 else {
10151063 value v = Field (v_res , 0 );
@@ -1024,9 +1072,6 @@ static inline void set_sqlite3_result(sqlite3_context *ctx, value v_res)
10241072 sqlite3_result_blob (
10251073 ctx , String_val (v ), caml_string_length (v ), SQLITE_TRANSIENT );
10261074 break ;
1027- case 4 :
1028- sqlite3_result_error (ctx , String_val (v ), caml_string_length (v ));
1029- break ;
10301075 default :
10311076 sqlite3_result_error (ctx , "unknown value returned by callback" , -1 );
10321077 }
@@ -1064,7 +1109,7 @@ static inline void caml_sqlite3_user_function_step(
10641109 }
10651110 v_args = caml_sqlite3_wrap_values (argc , argv );
10661111 v_res = caml_callback2_exn (Field (data -> v_fun , 2 ), agg_ctx -> v_acc , v_args );
1067- if (Is_exception_result (v_res )) exception_result (ctx );
1112+ if (Is_exception_result (v_res )) exception_result (ctx , v_res );
10681113 else agg_ctx -> v_acc = v_res ;
10691114 caml_enter_blocking_section ();
10701115}
0 commit comments