Skip to Content.
Sympa Menu

freetds - Re: [freetds] SQLCancel() and error diagnostic: Expecting HY008 SQLSTATE

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: Sebastien FLAESCH <sf AT 4js.com>
  • To: FreeTDS Development Group <freetds AT lists.ibiblio.org>
  • Subject: Re: [freetds] SQLCancel() and error diagnostic: Expecting HY008 SQLSTATE
  • Date: Mon, 14 Jan 2008 09:41:12 +0100

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
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






Archive powered by MHonArc 2.6.24.

Top of Page