forked from hdbc/hdbc-odbc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhdbc-odbc-helper.c
162 lines (142 loc) · 5.17 KB
/
hdbc-odbc-helper.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#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) {
odbc_atomic_increment(&parentobj->refcount);
}
#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;
if (!res->encapobj)
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;
if (!res->encapobj)
return SQL_SUCCESS;
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 && res->encapobj) {
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);
}