0

We create and cache ODBC connections created using SQLDriverConnect; we've found circumstances where the connection becomes lost... prepared statements stop working, etc.

I didn't see an obvious function to test a connection/statement is valid, or to reset/re-create a connection, which seems like it would be a fairly common pattern. Can anyone suggest how this is best implemented?

This answer is along the same lines, is that the correct solution here too? If so is there any neat way to 're-boot' an existing statement to use the new connection? And if a connection is discovered to be dead does it still need to be freed?

Community
  • 1
  • 1
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

1 Answers1

0

In my mind there has always been some confusion over whether SQL_ATTR_CONNECTION_DEAD works to test if a connection is still alive before you make a call which requires it to be alive. This is based on the usage of many ODBC drivers, including the ones I wrote and I suspect they may all implement it differently.

In the post you refer to Nick's answer (SQL_ATTR_CONNECTION_DEAD) does not work with many drivers I have access to:

#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
#include <stdlib.h>

static void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type);

main() {
    SQLHENV henv;
    SQLHDBC hdbc;
    SQLHSTMT hstmt;
    SQLCHAR outstr[1024];
    SQLSMALLINT         outstr_len;
    SQLRETURN ret;
    SQLINTEGER dead;

    SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
    SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    ret = SQLDriverConnect(hdbc, (void *)0, "DSN=xx;UID=xx;PWD=xx", SQL_NTS,
                           outstr, sizeof(outstr), &outstr_len,
                           SQL_DRIVER_COMPLETE);
    if (!SQL_SUCCEEDED(ret)) {
        extract_error("SQLDriverConnect", hdbc, SQL_HANDLE_DBC);
        exit(1);
    }

    SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    ret = SQLPrepare(hstmt, "select 123", SQL_NTS);
    if (!SQL_SUCCEEDED(ret)) {
        extract_error("SQLPrepare", hstmt, SQL_HANDLE_STMT);
        exit(1);
    }

    ret = SQLExecute(hstmt);
    if (!SQL_SUCCEEDED(ret)) {
        extract_error("SQLExecute", hstmt, SQL_HANDLE_STMT);
        exit(1);
    }

    sleep(120);

    SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);
    printf ("dead=%ld\n", dead);

    ret = SQLExecute(hstmt);
    if (!SQL_SUCCEEDED(ret)) {
        extract_error("SQLExecute", hstmt, SQL_HANDLE_STMT);
    }
    SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_DEAD, &dead, 0, NULL);
    printf ("dead=%ld\n", dead);


}


static void extract_error(
    char *fn,
    SQLHANDLE handle,
    SQLSMALLINT type)
{
    SQLINTEGER i = 0, native;
    SQLCHAR state[ 7 ];
    SQLCHAR text[256];
    SQLSMALLINT len;
    int ret;

    fprintf(stderr,
            "\n"
            "The driver reported the following diagnostics whilst running "
            "%s\n\n",
            fn);

    do
    {
        ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
                            sizeof(text), &len );
        if (SQL_SUCCEEDED(ret))
            printf( "%s:%ld:%ld:%s\n", state, i, native, text );
    }
    while( ret == SQL_SUCCESS );
}

which outputs with a number of drivers:

dead=0
# At this point I took the server down
The driver reported the following diagnostics whilst running SQLExecute
08S01:1:0:[SQL Server Driver 10.0][SQL Server]Communication link failure: short write
dead=0

The other option specified in the post you refer to is SQL_COPT_SS_CONNECTION_DEAD but that is specific to MS SQL Server.

Since I presume you don't intend for any statement handles to fail ordinarily can't you just run the stmt and if it fails assume the connection is dead and reconnect and reprepare?

If a connection does die you do still need to call SQLFreeHandle for the various handles.

bohica
  • 5,932
  • 3
  • 23
  • 28