Skip to content

Commit 3c5ea6f

Browse files
committed
First commit
0 parents  commit 3c5ea6f

File tree

9 files changed

+771
-0
lines changed

9 files changed

+771
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.stack-work

LICENSE

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Copyright FP Complete (c) 2017
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials provided
14+
with the distribution.
15+
16+
* Neither the name of FP Complete nor the names of other
17+
contributors may be used to endorse or promote products derived
18+
from this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# odbc
2+
3+
Safe wrapper to the ODBC C library.

Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

cbits/odbc.c

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
#include <sql.h>
2+
#include <sqlext.h>
3+
#include <sqltypes.h>
4+
#include <odbcss.h>
5+
#include <stdlib.h>
6+
#include <stdio.h>
7+
#include <string.h>
8+
#include <sql.h>
9+
#include <sqlext.h>
10+
#include <sqltypes.h>
11+
#include <odbcss.h>
12+
13+
#define FALSE 0
14+
#define TRUE 1
15+
#define MAXBUFLEN 256
16+
#define MAXNAME 256
17+
18+
void odbc_ProcessLogMessages(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, char *logstring, int ConnInd);
19+
20+
////////////////////////////////////////////////////////////////////////////////
21+
// Alloc/dealloc env
22+
23+
SQLHENV *odbc_SQLAllocEnv(){
24+
SQLHENV *henv = malloc(sizeof *henv);
25+
*henv = SQL_NULL_HENV;
26+
RETCODE retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, henv);
27+
if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
28+
free(henv);
29+
return NULL;
30+
} else {
31+
return henv;
32+
}
33+
}
34+
35+
void odbc_SQLFreeEnv(SQLHENV *henv){
36+
if(henv != NULL && *henv != SQL_NULL_HENV) {
37+
SQLFreeHandle(SQL_HANDLE_ENV, *henv);
38+
free(henv);
39+
}
40+
}
41+
42+
RETCODE odbc_SetEnvAttr(SQLHENV *henv){
43+
return
44+
SQLSetEnvAttr(*henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
45+
}
46+
47+
////////////////////////////////////////////////////////////////////////////////
48+
// Alloc/dealloc dbc
49+
50+
SQLHDBC *odbc_SQLAllocDbc(SQLHENV *henv){
51+
SQLHDBC *hdbc = malloc(sizeof *hdbc);
52+
*hdbc = SQL_NULL_HDBC;
53+
RETCODE retcode = SQLAllocHandle (SQL_HANDLE_DBC, *henv, hdbc);
54+
if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
55+
free(hdbc);
56+
return NULL;
57+
} else {
58+
return hdbc;
59+
}
60+
}
61+
62+
void odbc_SQLFreeDbc(SQLHDBC *hdbc){
63+
if(hdbc != NULL && *hdbc != SQL_NULL_HDBC) {
64+
SQLFreeHandle(SQL_HANDLE_DBC, *hdbc);
65+
free(hdbc);
66+
}
67+
}
68+
69+
////////////////////////////////////////////////////////////////////////////////
70+
// Connect/disconnect
71+
72+
RETCODE odbc_SQLDriverConnect(SQLHDBC *hdbc, SQLCHAR *connString){
73+
SQLSMALLINT ignored = 0;
74+
RETCODE r = SQLDriverConnect(
75+
*hdbc,
76+
NULL,
77+
connString,
78+
SQL_NTS,
79+
NULL,
80+
0,
81+
&ignored,
82+
SQL_DRIVER_NOPROMPT);
83+
if (r == SQL_ERROR)
84+
odbc_ProcessLogMessages(SQL_HANDLE_DBC, *hdbc, "odbc_SQLDriverConnect", FALSE);
85+
return r;
86+
}
87+
88+
void odbc_SQLDisconnect(SQLHDBC *hdbc){
89+
SQLDisconnect(*hdbc);
90+
}
91+
92+
////////////////////////////////////////////////////////////////////////////////
93+
// Alloc/dealloc statement
94+
95+
SQLHSTMT *odbc_SQLAllocStmt(SQLHDBC *hdbc){
96+
SQLHSTMT *hstmt = malloc(sizeof *hstmt);
97+
*hstmt = SQL_NULL_HSTMT;
98+
RETCODE retcode = SQLAllocHandle (SQL_HANDLE_STMT, *hdbc, hstmt);
99+
if ((retcode != SQL_SUCCESS_WITH_INFO) && (retcode != SQL_SUCCESS)) {
100+
free(hstmt);
101+
return NULL;
102+
} else {
103+
return hstmt;
104+
}
105+
}
106+
107+
void odbc_SQLFreeStmt(SQLHSTMT *hstmt){
108+
if(hstmt != NULL && *hstmt != SQL_NULL_HSTMT) {
109+
SQLFreeHandle(SQL_HANDLE_STMT, *hstmt);
110+
free(hstmt);
111+
}
112+
}
113+
114+
////////////////////////////////////////////////////////////////////////////////
115+
// Prepare
116+
117+
SQLRETURN odbc_SQLPrepare(
118+
SQLHSTMT * hstmt,
119+
SQLCHAR * text
120+
){
121+
RETCODE r = SQLPrepare(*hstmt, text, (SQLINTEGER)strlen((void*)text));
122+
if (r == SQL_ERROR)
123+
odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLPrepare", FALSE);
124+
return r;
125+
}
126+
127+
////////////////////////////////////////////////////////////////////////////////
128+
// Execute
129+
130+
RETCODE odbc_SQLExecDirect(SQLHSTMT *hstmt, UCHAR *stmt){
131+
RETCODE r = SQLExecDirect(*hstmt, stmt, SQL_NTS);
132+
if (r == SQL_ERROR) odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLExecDirect", FALSE);
133+
return r;
134+
}
135+
136+
RETCODE odbc_SQLExec(SQLHSTMT *hstmt){
137+
RETCODE r = SQLExecute(*hstmt);
138+
if (r == SQL_ERROR) odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLExec", FALSE);
139+
return r;
140+
}
141+
142+
////////////////////////////////////////////////////////////////////////////////
143+
// Fetch row
144+
145+
RETCODE odbc_SQLFetch(SQLHSTMT *hstmt){
146+
RETCODE r = SQLFetch(*hstmt);
147+
if (r == SQL_ERROR) odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLFetch", FALSE);
148+
return r;
149+
}
150+
151+
RETCODE odbc_SQLMoreResults(SQLHSTMT *hstmt){
152+
RETCODE r = SQLMoreResults(*hstmt);
153+
if (r == SQL_ERROR) odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLMoreResults", FALSE);
154+
return r;
155+
}
156+
157+
////////////////////////////////////////////////////////////////////////////////
158+
// Get fields
159+
160+
RETCODE odbc_SQLGetData(SQLHSTMT *hstmt,
161+
SQLUSMALLINT col,
162+
SQLSMALLINT targetType,
163+
SQLPOINTER buffer,
164+
SQLLEN bufferLen,
165+
SQLLEN * resultLen){
166+
RETCODE r = SQLGetData(*hstmt, col, targetType, buffer, bufferLen, resultLen);
167+
if (r == SQL_ERROR) odbc_ProcessLogMessages(SQL_HANDLE_STMT, *hstmt, "odbc_SQLGetData", FALSE);
168+
return r;
169+
}
170+
171+
SQLRETURN odbc_SQLDescribeCol(
172+
SQLHSTMT *StatementHandle,
173+
SQLUSMALLINT ColumnNumber,
174+
SQLCHAR * ColumnName,
175+
SQLSMALLINT BufferLength,
176+
SQLSMALLINT * NameLengthPtr,
177+
SQLSMALLINT * DataTypePtr,
178+
SQLULEN * ColumnSizePtr,
179+
SQLSMALLINT * DecimalDigitsPtr,
180+
SQLSMALLINT * NullablePtr){
181+
return SQLDescribeCol(
182+
*StatementHandle,
183+
ColumnNumber,
184+
ColumnName,
185+
BufferLength,
186+
NameLengthPtr,
187+
DataTypePtr,
188+
ColumnSizePtr,
189+
DecimalDigitsPtr,
190+
NullablePtr);
191+
}
192+
193+
////////////////////////////////////////////////////////////////////////////////
194+
// Get columns
195+
196+
RETCODE odbc_SQLNumResultCols(SQLHSTMT *hstmt, SQLSMALLINT *cols){
197+
return SQLNumResultCols(*hstmt, cols);
198+
}
199+
200+
////////////////////////////////////////////////////////////////////////////////
201+
// Logs
202+
203+
void odbc_ProcessLogMessages(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, char *logstring, int ConnInd) {
204+
RETCODE plm_retcode = SQL_SUCCESS;
205+
UCHAR plm_szSqlState[MAXBUFLEN] = "", plm_szErrorMsg[MAXBUFLEN] = "";
206+
SDWORD plm_pfNativeError = 0L;
207+
SWORD plm_pcbErrorMsg = 0;
208+
SQLSMALLINT plm_cRecNmbr = 1;
209+
SDWORD plm_SS_MsgState = 0/* , plm_SS_Severity = 0 */;
210+
SQLINTEGER plm_Rownumber = 0;
211+
USHORT plm_SS_Line;
212+
/* SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname; */
213+
/* SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME]; */
214+
215+
if (logstring)
216+
puts(logstring);
217+
218+
while (plm_retcode != SQL_NO_DATA_FOUND) {
219+
plm_retcode = SQLGetDiagRec(plm_handle_type, plm_handle, plm_cRecNmbr,
220+
plm_szSqlState, &plm_pfNativeError, plm_szErrorMsg,
221+
MAXBUFLEN - 1, &plm_pcbErrorMsg);
222+
223+
// Note that if the application has not yet made a successful connection,
224+
// the SQLGetDiagField information has not yet been cached by ODBC Driver Manager and
225+
// these calls to SQLGetDiagField will fail.
226+
if (plm_retcode != SQL_NO_DATA_FOUND) {
227+
if (ConnInd) {
228+
plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
229+
SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
230+
SQL_IS_INTEGER, NULL);
231+
232+
plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
233+
SQL_DIAG_SS_LINE, &plm_SS_Line, SQL_IS_INTEGER, NULL);
234+
235+
plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr,
236+
SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
237+
SQL_IS_INTEGER, NULL);
238+
239+
/* plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, */
240+
/* SQL_DIAG_SS_SEVERITY, &plm_SS_Severity, */
241+
/* SQL_IS_INTEGER, NULL); */
242+
243+
/* plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, */
244+
/* SQL_DIAG_SS_PROCNAME, &plm_SS_Procname, */
245+
/* sizeof(plm_SS_Procname), &plm_cbSS_Procname); */
246+
247+
/* plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, */
248+
/* SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname, */
249+
/* sizeof(plm_SS_Srvname), &plm_cbSS_Srvname); */
250+
}
251+
252+
printf("szSqlState = %s\n", plm_szSqlState);
253+
printf("pfNativeError = %d\n", plm_pfNativeError);
254+
printf("szErrorMsg = %s\n", plm_szErrorMsg);
255+
printf("pcbErrorMsg = %d\n\n", plm_pcbErrorMsg);
256+
257+
if (ConnInd) {
258+
printf("ODBCRowNumber = %d\n", plm_Rownumber);
259+
printf("SSrvrLine = %d\n", plm_Rownumber);
260+
printf("SSrvrMsgState = %d\n", plm_SS_MsgState);
261+
/* printf("SSrvrSeverity = %d\n", plm_SS_Severity); */
262+
/* printf("SSrvrProcname = %s\n", plm_SS_Procname); */
263+
/* printf("SSrvrSrvname = %s\n\n", plm_SS_Srvname); */
264+
}
265+
}
266+
267+
plm_cRecNmbr++; // Increment to next diagnostic record.
268+
}
269+
}

cbits/odbc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <sql.h>
2+
#include <sqlext.h>
3+
#include <sqltypes.h>
4+
#include <odbcss.h>
5+
6+
SQLHDBC odbc_SQLAllocHandle();

odbc.cabal

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: odbc
2+
version: 0.1.0.0
3+
license: BSD3
4+
license-file: LICENSE
5+
build-type: Simple
6+
extra-source-files: README.md
7+
cabal-version: >=1.10
8+
9+
library
10+
hs-source-dirs: src
11+
ghc-options: -Wall -O2
12+
exposed-modules: ODBC
13+
default-language: Haskell2010
14+
extra-libraries: odbc
15+
c-sources: cbits/odbc.c
16+
includes: cbits/odbc.h
17+
cc-options: -Wall -Wconversion
18+
include-dirs: cbits
19+
build-depends:
20+
base >= 4.7 && < 5,
21+
async,
22+
bytestring,
23+
text

0 commit comments

Comments
 (0)