packages feed

HDBC-odbc-2.4.0.0: hdbc-odbc-helper.c

#if defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__)
#define ON_WINDOWS
#include <windows.h>
#include <winnt.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <stdio.h>
#include <stdlib.h>
#include "hdbc-odbc-helper.h"

SQLLEN nullDataHDBC = SQL_NULL_DATA;

int sqlSucceeded(SQLRETURN ret) {
  return SQL_SUCCEEDED(ret);
}

#ifdef ON_WINDOWS
#define odbc_sync_val_compare_and_swap(ptr, oldval, newval) InterlockedCompareExchange((volatile LONG*)(ptr), (newval), (oldval))
#define odbc_atomic_increment(ptr) InterlockedIncrement((volatile LONG*)(ptr))
#define odbc_atomic_decrement(ptr) InterlockedDecrement((volatile LONG*)(ptr))
#else
#define odbc_sync_val_compare_and_swap(ptr, oldval, newval) __sync_val_compare_and_swap((ptr), (oldval), (newval))
#define odbc_atomic_increment(ptr) __sync_add_and_fetch((ptr), 1)
#define odbc_atomic_decrement(ptr) __sync_sub_and_fetch((ptr), 1)
#endif

/* Things can't finalize more than once.
We'd like to let people call them from the app.
Yet we'd also like to be able to have a ForeignPtr finalize them.

So, here's a little wrapper for things. */

void dbc_conditional_finalizer(finalizeonce *conn, int refCount);

finalizeonce *wrapobjodbc(void *obj, finalizeonce *parentobj) {
  // parentobj might not get finalized during running this function
  // from other thread because of the way it is called from Haskell
  // side.
  finalizeonce *newobj;
  newobj = malloc(sizeof(finalizeonce));
  if (newobj == NULL) {
    fprintf(stderr, "\nHDBC: could not allocate wrapper!\n");
    return NULL;
  }
  newobj->isfinalized = 0;
  newobj -> refcount = 1;
  newobj->encapobj = obj;
  newobj->extrainfo = NULL;
  newobj->parent = parentobj;
  if (parentobj != NULL) {
    __sync_add_and_fetch(&parentobj->refcount, 1);
  }
#ifdef HDBC_DEBUG
  fprintf(stderr, "\nWrapped %p at %p\n", obj, newobj);
#endif
  return newobj;
}

finalizeonce *wrapobjodbc_extra(void *obj, void *extra, finalizeonce *parentobj) {
  finalizeonce *newobj = wrapobjodbc(obj, parentobj);
  if (newobj != NULL)
    newobj->extrainfo = extra;
  return newobj;
}

void sqlFreeHandleSth_app(finalizeonce *res) {
#ifdef HDBC_DEBUG
  fprintf(stderr, "\nApp cleanup of sth %p requested: %d\n",
          res->encapobj, res->isfinalized);
#endif
  int isFinalized = odbc_sync_val_compare_and_swap(&res->isfinalized, 0, 1);
  if (isFinalized)
    return;
  // Microsoft SQL Server driver might deadlock if calling SQLCloseCursor on a statement that is in the
  // process of fetching data via network. So we cancel it first.
  SQLCancel((SQLHSTMT) (res->encapobj));
  SQLCloseCursor((SQLHSTMT) (res->encapobj));
  SQLFreeHandle(SQL_HANDLE_STMT, (SQLHANDLE) (res->encapobj));
  res->encapobj = NULL;
}

void sqlFreeHandleSth_finalizer(finalizeonce *res) {
#ifdef HDBC_DEBUG
  fprintf(stderr, "\nFinalizer cleanup of sth %p requested: %d\n",
          res->encapobj, res->isfinalized);
#endif
  sqlFreeHandleSth_app(res);

  /* Not really important since this is never a parent */
  odbc_atomic_decrement(&res->refcount);

  int parentRefCount = odbc_atomic_decrement(&res->parent->refcount);
  dbc_conditional_finalizer(res->parent, parentRefCount);
  free(res);
}

SQLRETURN sqlFreeHandleDbc_app(finalizeonce *res) {
  SQLRETURN retval;
#ifdef HDBC_DEBUG
  fprintf(stderr, "\nApp cleanup of dbc %p requested: %d\n",
          res->encapobj, res->isfinalized);
#endif
  int isFinalized = odbc_sync_val_compare_and_swap(&res->isfinalized, 0, 1);
  if (isFinalized)
    return 0;
  retval = SQLDisconnect((SQLHDBC) (res->encapobj));
  if (SQL_SUCCEEDED(retval)) {
    SQLFreeHandle(SQL_HANDLE_DBC, (SQLHANDLE) (res->encapobj));
    SQLFreeHandle(SQL_HANDLE_ENV, (SQLHANDLE) (res->extrainfo));
    res->encapobj = NULL;
  }
  return retval;
}

void sqlFreeHandleDbc_finalizer(finalizeonce *res) {
#ifdef HDBC_DEBUG
  fprintf(stderr, "\nFinalizer cleanup of dbc %p requested: %d\n",
          res->encapobj, res->isfinalized);
#endif
  int refCount = odbc_atomic_decrement(&res->refcount);
  dbc_conditional_finalizer(res, refCount);
}

void dbc_conditional_finalizer(finalizeonce *res, int refcount) {
  if (refcount < 1) {
  /* Don't use sqlFreeHandleDbc_app here, because we want to clear it out
     regardless of the success or failues of SQLDisconnect. */
    int isFinalized = odbc_sync_val_compare_and_swap(&res->isfinalized, 0, 1);
    if (!isFinalized) {
      SQLDisconnect((SQLHDBC) (res->encapobj));
      SQLFreeHandle(SQL_HANDLE_DBC, (SQLHANDLE) (res->encapobj));
      SQLFreeHandle(SQL_HANDLE_ENV, (SQLHANDLE) (res->extrainfo));
      res->encapobj = NULL;
    }
    free(res);
  }
}

void *getSqlOvOdbc3(void) {
  return (void *)SQL_OV_ODBC3;
}

SQLRETURN disableAutoCommit(SQLHDBC conn) {
  return SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT,
                           (SQLPOINTER) SQL_AUTOCOMMIT_OFF,
                           SQL_IS_UINTEGER);
}

SQLRETURN simpleSqlTables(SQLHSTMT stmt) {
  return SQLTables(stmt, NULL, 0, NULL, 0, "%", 1, "TABLE", 5);
}

SQLRETURN simpleSqlColumns(SQLHSTMT stmt, SQLCHAR *tablename,
                           SQLSMALLINT tnlen) {
  return SQLColumns(stmt, NULL, 0, NULL, 0, tablename, tnlen, "%", 1);
}