@@ -24,25 +24,25 @@ VALUE opt_escape_regex, opt_escape_dblquote;
24
24
25
25
// Lib Backend (Helpers)
26
26
27
- VALUE rb_tinytds_raise_error (DBPROCESS * dbproc , int is_message , int cancel , const char * error , const char * source , int severity , int dberr , int oserr ) {
27
+ VALUE rb_tinytds_raise_error (DBPROCESS * dbproc , tinytds_errordata error ) {
28
28
VALUE e ;
29
29
GET_CLIENT_USERDATA (dbproc );
30
- if (cancel && !dbdead (dbproc ) && userdata && !userdata -> closed ) {
30
+ if (error . cancel && !dbdead (dbproc ) && userdata && !userdata -> closed ) {
31
31
userdata -> dbsqlok_sent = 1 ;
32
32
dbsqlok (dbproc );
33
33
userdata -> dbcancel_sent = 1 ;
34
34
dbcancel (dbproc );
35
35
}
36
- e = rb_exc_new2 (cTinyTdsError , error );
37
- rb_funcall (e , intern_source_eql , 1 , rb_str_new2 (source ));
38
- if (severity )
39
- rb_funcall (e , intern_severity_eql , 1 , INT2FIX (severity ));
40
- if (dberr )
41
- rb_funcall (e , intern_db_error_number_eql , 1 , INT2FIX (dberr ));
42
- if (oserr )
43
- rb_funcall (e , intern_os_error_number_eql , 1 , INT2FIX (oserr ));
44
-
45
- if (severity <= 10 && is_message ) {
36
+ e = rb_exc_new2 (cTinyTdsError , error . error );
37
+ rb_funcall (e , intern_source_eql , 1 , rb_str_new2 (error . source ));
38
+ if (error . severity )
39
+ rb_funcall (e , intern_severity_eql , 1 , INT2FIX (error . severity ));
40
+ if (error . dberr )
41
+ rb_funcall (e , intern_db_error_number_eql , 1 , INT2FIX (error . dberr ));
42
+ if (error . oserr )
43
+ rb_funcall (e , intern_os_error_number_eql , 1 , INT2FIX (error . oserr ));
44
+
45
+ if (error . severity <= 10 && error . is_message ) {
46
46
VALUE message_handler = userdata && userdata -> message_handler ? userdata -> message_handler : Qnil ;
47
47
if (message_handler && message_handler != Qnil && rb_respond_to (message_handler , intern_call ) != 0 ) {
48
48
rb_funcall (message_handler , intern_call , 1 , e );
@@ -57,6 +57,16 @@ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, cons
57
57
58
58
59
59
// Lib Backend (Memory Management & Handlers)
60
+ static void push_userdata_error (tinytds_client_userdata * userdata , tinytds_errordata error ) {
61
+ // reallocate memory for the array as needed
62
+ if (userdata -> nonblocking_errors_size == userdata -> nonblocking_errors_length ) {
63
+ userdata -> nonblocking_errors_size *= 2 ;
64
+ userdata -> nonblocking_errors = realloc (userdata -> nonblocking_errors , userdata -> nonblocking_errors_size * sizeof (tinytds_errordata ));
65
+ }
66
+
67
+ userdata -> nonblocking_errors [userdata -> nonblocking_errors_length ] = error ;
68
+ userdata -> nonblocking_errors_length ++ ;
69
+ }
60
70
61
71
int tinytds_err_handler (DBPROCESS * dbproc , int severity , int dberr , int oserr , char * dberrstr , char * oserrstr ) {
62
72
static const char * source = "error" ;
@@ -105,6 +115,16 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
105
115
break ;
106
116
}
107
117
118
+ tinytds_errordata error_data = {
119
+ .is_message = 0 ,
120
+ .cancel = cancel ,
121
+ .severity = severity ,
122
+ .dberr = dberr ,
123
+ .oserr = oserr
124
+ };
125
+ strncpy (error_data .error , dberrstr , ERROR_MSG_SIZE );
126
+ strncpy (error_data .source , source , ERROR_MSG_SIZE );
127
+
108
128
/*
109
129
When in non-blocking mode we need to store the exception data to throw it
110
130
once the blocking call returns, otherwise we will segfault ruby since part
@@ -116,27 +136,9 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
116
136
dbcancel (dbproc );
117
137
userdata -> dbcancel_sent = 1 ;
118
138
}
119
-
120
- /*
121
- If we've already captured an error message, don't overwrite it. This is
122
- here because FreeTDS sends a generic "General SQL Server error" message
123
- that will overwrite the real message. This is not normally a problem
124
- because a ruby exception is normally thrown and we bail before the
125
- generic message can be sent.
126
- */
127
- if (!userdata -> nonblocking_error .is_set ) {
128
- userdata -> nonblocking_error .is_message = 0 ;
129
- userdata -> nonblocking_error .cancel = cancel ;
130
- strncpy (userdata -> nonblocking_error .error , dberrstr , ERROR_MSG_SIZE );
131
- strncpy (userdata -> nonblocking_error .source , source , ERROR_MSG_SIZE );
132
- userdata -> nonblocking_error .severity = severity ;
133
- userdata -> nonblocking_error .dberr = dberr ;
134
- userdata -> nonblocking_error .oserr = oserr ;
135
- userdata -> nonblocking_error .is_set = 1 ;
136
- }
137
-
139
+ push_userdata_error (userdata , error_data );
138
140
} else {
139
- rb_tinytds_raise_error (dbproc , 0 , cancel , dberrstr , source , severity , dberr , oserr );
141
+ rb_tinytds_raise_error (dbproc , error_data );
140
142
}
141
143
142
144
return return_value ;
@@ -148,25 +150,31 @@ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severi
148
150
149
151
int is_message_an_error = severity > 10 ? 1 : 0 ;
150
152
153
+ tinytds_errordata error_data = {
154
+ .is_message = !is_message_an_error ,
155
+ .cancel = is_message_an_error ,
156
+ .severity = severity ,
157
+ .dberr = msgno ,
158
+ .oserr = msgstate
159
+ };
160
+ strncpy (error_data .error , msgtext , ERROR_MSG_SIZE );
161
+ strncpy (error_data .source , source , ERROR_MSG_SIZE );
162
+
151
163
// See tinytds_err_handler() for info about why we do this
152
164
if (userdata && userdata -> nonblocking ) {
153
- if (!userdata -> nonblocking_error .is_set ) {
154
- userdata -> nonblocking_error .is_message = !is_message_an_error ;
155
- userdata -> nonblocking_error .cancel = is_message_an_error ;
156
- strncpy (userdata -> nonblocking_error .error , msgtext , ERROR_MSG_SIZE );
157
- strncpy (userdata -> nonblocking_error .source , source , ERROR_MSG_SIZE );
158
- userdata -> nonblocking_error .severity = severity ;
159
- userdata -> nonblocking_error .dberr = msgno ;
160
- userdata -> nonblocking_error .oserr = msgstate ;
161
- userdata -> nonblocking_error .is_set = 1 ;
162
- }
165
+ /*
166
+ In the case of non-blocking command batch execution we can receive multiple messages
167
+ (including errors). We keep track of those here so they can be processed once the
168
+ non-blocking call returns.
169
+ */
170
+ push_userdata_error (userdata , error_data );
163
171
164
172
if (is_message_an_error && !dbdead (dbproc ) && !userdata -> closed ) {
165
173
dbcancel (dbproc );
166
174
userdata -> dbcancel_sent = 1 ;
167
175
}
168
176
} else {
169
- rb_tinytds_raise_error (dbproc , ! is_message_an_error , is_message_an_error , msgtext , source , severity , msgno , msgstate );
177
+ rb_tinytds_raise_error (dbproc , error_data );
170
178
}
171
179
return 0 ;
172
180
}
@@ -204,7 +212,10 @@ static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata)
204
212
userdata -> dbsqlok_sent = 0 ;
205
213
userdata -> dbcancel_sent = 0 ;
206
214
userdata -> nonblocking = 0 ;
207
- userdata -> nonblocking_error .is_set = 0 ;
215
+ // the following is mainly done for consistency since the values are reset accordingly in nogvl_setup/cleanup.
216
+ // the nonblocking_errors array does not need to be freed here. That is done as part of nogvl_cleanup.
217
+ userdata -> nonblocking_errors_length = 0 ;
218
+ userdata -> nonblocking_errors_size = 0 ;
208
219
}
209
220
210
221
static void rb_tinytds_client_mark (void * ptr ) {
0 commit comments