[freetds] SQLCancel() and error diagnostic: Expecting HY008 SQLSTATE
Sebastien FLAESCH
sf at 4js.com
Mon Jan 14 04:31:50 EST 2008
Sorry little typo in original mail, on course the code examples are UNIX specific, not WIN32.
See below.
Sorry, will take another coffee.
Seb
Sebastien FLAESCH wrote:
> James K. Lowden wrote:
>> Frediano Ziglio wrote:
>>> Also it
>>> seems that in some different systems signal are handled in a slightly
>>> different way. That is
>>> - query is sent, state is set to TDS_READING
>>> - select/poll stops
>>> - signal is caught, SQLCancel is called
>> Who catches the signal? I hope it's the application.
>
> Yes:
>
> static void sigint_handler(int s)
> {
> int rcode;
> printf(">>>> SQLCancel() ...\n");
> rcode = SQLCancel(m_hstmt1);
> printf(">>>> ... SQLCancel done rcode = %d\n", rcode);
> CHECK_RCODE(SQL_HANDLE_STMT,m_hstmt1,"SQLCancel failed");
> signal(SIGINT, sigint_handler);
> }
>
>> I don't know the proper treatment of signals in an ODBC application, but
>> I'm pretty sure SQLCancel shouldn't be called in a signal handler. The
>> handler doesn't have enough information to know whether that's the right
>> thing to do.
>>
>> I think all the signal handler can do is record the fact that a signal was
>> caught in a more-or-less global variable. When the called function
>> returns an error -- e.g. a timeout on reads -- the app can then decide
>> whether or not to call SQLCancel.
>
> Actually this is what we do in our Genero VM. We just set a global variable
> in the SIGINT handler, and check for it during the pseudo-code interpreter
> loop...
>
> However, our VM is a special case... As we interpret pseudo-code, we *can*
> test for a interrupt flag variable between opcode interpretation.
>
> But what about normal/simple TUI ODBC applications? I can't see any another
> way as calling SQLCancel() from the signal handler... How would you implement
> SQL interruption in a text-based SQL command interpreter?
>
> From the ODBC programmer point of view, the steps are:
>
> 1) Call SQLExecute() ... wait for response.
> 2) Get a SIGINT signal... handler gets called and calls SQLCancel()
> 3) SQLExecute() should stop with SQL Error.
> 4) Application code must handle the error.
>
> For sure GUI applications typically handle user interruption by catching
> a special GUI event (click on cancel button), and can then call SQLCancel()
> outside a signal handler flow... be this is about a non-GUI application.
>
>>> - cancel token is sent, in_cancel get 1, current_statement is reset
>> By whom? It's too soon....
>>
>>> - select/poll stop with EINTR error
>>> - interrupt is call (which do nothing)
>> The driver could steer here. We could add a property to the FreeTDS ODBC
>> driver that would let the app tell the driver how to treat EINTR on reads.
>>
>>
>> Default behavior probably should be to return TDS_INT_CANCEL if sock_errno
>> == TDSSOCK_EINTR.
>>
>> Writes are a different matter. There's no cancel packet to send. The
>> library should ignore the signal and let the application handle it.
>>
>
> I did not look at the code in detail, but you probably known that UNIX
> system calls like read() or recv() can be interrupted (EINTR) and need
> to be re-executed until all bytes have been processed...
>
> Example of code we have (note this is a short version with WIN32 specific
... *without* the WIN32 specific code!!! It's UNIX only.
> code - there are as you can imagine some differences when using MSVCRT):
>
> static int uic_Reader_read(uic_Reader * r, char *dst, int size)
> {
> int n, rv = 0;
>
> while(size) {
> if ((n = recv(r->c->sockfd, dst, size, 0)) < 0) {
> if (errno != EINTR) {
> /* Handle TCP error */
> }
> return rv ? rv : -1;
> } else {
> if (n == 0) { /* EOF */
> return rv;
> }
> rv += n;
> size -= n;
> dst += n;
> }
> }
> return rv;
> }
>
> or:
>
> static int my_write(int fd, const char *aSrc, int aSize)
> {
> int status = 0;
> int size, n;
> char *src;
>
> status = 0;
> src = (char *) aSrc;
> size = aSize;
> while (size) {
> n = write(fd, src, size);
> if (n == -1) {
> if (errno == EINTR) {
> n = 0;
> } else {
> status = errno;
> break;
> }
> }
> size -= n;
> src += n;
> }
> return status;
> }
>
>
>>> - tds_select returns 0
>>> - tdserror(TDSETIME) is called
>>> - odbc_errmsg_handler close connection cause current_statement is
>>> NULL...
>> In sum, treat EINTR like an early timeout:
>>
>> 1. Don't catch the signal. Let the app do it.
>> 2. Let odbc_errmsg_handler return TDS_INT_TIMEOUT to tdserror(); let
>> tdserror() send the cancel packet.
>> 3. Return control to the application with a timeout error.
>>
>> When control returns to the application, he'll have a failed call, a
>> connection that's probably OK but might be dead, and evidence that a
>> signal was caught.
>>
>> There's an expression in English, "to put two and two together", meaning
>> to come to the obvious conclusion. The application puts two and two
>> together, decides the signal caused the timeout, sees the state of the
>> connection, and decides what to do.
>
> BTW this remembers me that Oracle's OCI client handle SIGINT signals in
> the client driver... Is seems that Oracle registers a signal handler after
> a connect. So to get the control back, we need to register again our
> signal handler... (Oracle probably do that to take control and allow
> interruption of SQL queries by default)...
>
> This is to me unexpected: The application should handle signals such as
> SIGINT, and do the DB client API calls (if available) to cancel a query.
>
> This works for us with:
>
> - Postgresql using PQcancel()
> - DB2 CLI using SQLCancel()
> - ANTS DS using SQLCancel()
> - Informix using sqlbreak() on Unix, and callback method on MS Windows.
> - Oracle using OCIBreak() (but need to register SIGINT handle after connection)
>
> Bye
> Seb
>
>> HTH.
>>
>> --jkl
>> _______________________________________________
>> FreeTDS mailing list
>> FreeTDS at lists.ibiblio.org
>> http://lists.ibiblio.org/mailman/listinfo/freetds
>>
>
> _______________________________________________
> FreeTDS mailing list
> FreeTDS at lists.ibiblio.org
> http://lists.ibiblio.org/mailman/listinfo/freetds
>
More information about the FreeTDS
mailing list