#if defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__) #define ON_WINDOWS #include #include #endif #include #include #include #include #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); }