[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