rails-sqlserver/tiny_tds — pull request #133 by wbond (2013-08-20 18:01:15+00:00)
Title: Fixed error handling to prevent crashing ruby in 0.6.x

Observed errors included:

- Segmentation fault
- cfp consistency error
- object allocation during garbage collection phase

These errors manifested themselves most consistently when a SQL error
occurred while using threads, but would also happen occasionally without
threads being involved.

With TinyTds 0.6.0 changes were made to make the C code properly release
the Global VM Lock (GVL) so that other threads could run while FreeTDS
was waiting for results from the server (blocked on I/O). While this
lock is released it is not possible to safely call the ruby C API.

The way that FreeTDS deals with error messages is to define a handler to
process the message when it is raised. The issue is that the error
handler that TinyTds uses invokes the ruby C API to throw an exception.
This accidental use of the ruby C API while TinyTds did not have the GVL
caused various catastrophic conditions in the ruby VM. These conditions
manifested themselves as the various error messages listed above.

To fix these errors, the userdata struct is used to track if the client
is currently "nonblocking" and thus does not have the GVL. In those
situations, any errors are saved into a new tinytds_errordata struct
that saves all necessary information. Once the GVL is re-obtained,
TinyTds uses the information in the tinytds_errordata struct to throw
an exception.

To further complicate the issue, FreeTDS will send multiple messages/
errors if the error is severe enough. Normally, when exceptions are
raised immediately, the developer will properly receive the first,
detailed, message. However, when TinyTds does not have the GVL and the
error is stored for later use, FreeTDS has the opportunity to send more
messages, causing the first one to be overwritten with something more
generic. Because of this, as soon as the first message is captured, all
further messages are ignored until TinyTds::Client.execute() is called
again.