Commit cc565c95 cc565c9504c89f68b35c654bebb383856af15747 by Sergey Poznyakoff

ODBC SQL driver

1 parent 91ea068b
Showing 1 changed file with 308 additions and 0 deletions
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <mailutils/mailutils.h>
23 #include <mailutils/sql.h>
24
25 #include <sql.h>
26 #include <sqlext.h>
27 #include <sqltypes.h>
28
29 struct mu_odbc_data
30 {
31 SQLHENV env; /* Environment */
32 SQLHDBC dbc; /* DBC */
33 /* Result data: */
34 SQLHSTMT stmt; /* Statement being executed */
35 list_t result; /* List of returned field values */
36 /* Error reporting: */
37 struct odbc_err_buffer
38 {
39 SQLSMALLINT handle_type; /* Type of the handle */
40 SQLHANDLE handle; /* Handle that caused the error */
41 char *what; /* Name of the function that failed */
42 SQLCHAR *msg; /* Error message buffer */
43 char *text; /* Error text buffer */
44 } err;
45 };
46
47 static void
48 mu_odbc_diag(struct mu_odbc_data *dp,
49 SQLSMALLINT handle_type, SQLHANDLE handle, char *what)
50 {
51 dp->err.what = what;
52 dp->err.handle_type = handle_type;
53 dp->err.handle = handle;
54 }
55
56 /* ************************************************************************* */
57 /* Interface routines */
58
59 static int
60 init (mu_sql_connection_t conn)
61 {
62 struct mu_odbc_data *dp = calloc (1, sizeof (*dp));
63 if (!dp)
64 return ENOMEM;
65 conn->data = dp;
66 return 0;
67 }
68
69 static int
70 destroy (mu_sql_connection_t conn)
71 {
72 struct mu_odbc_data *dp = conn->data;
73 free (dp->err.msg);
74 free (dp->err.text);
75 if (dp->stmt)
76 SQLFreeHandle (SQL_HANDLE_STMT, dp->stmt);
77 free (dp);
78 conn->data = NULL;
79 return 0;
80 }
81
82 static int
83 connect (mu_sql_connection_t conn)
84 {
85 struct mu_odbc_data *dp = conn->data;
86 long rc;
87
88 rc = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &dp->env);
89 if (rc != SQL_SUCCESS)
90 {
91 mu_odbc_diag (dp, SQL_HANDLE_ENV, dp->env, "SQLAllocHandle");
92 return MU_ERR_SQL;
93 }
94
95 rc = SQLSetEnvAttr (dp->env, SQL_ATTR_ODBC_VERSION,
96 (void*)SQL_OV_ODBC3, 0);
97 if (rc != SQL_SUCCESS)
98 {
99 mu_odbc_diag (dp, SQL_HANDLE_ENV, dp->dbc, "SQLSetEnvAttr");
100 return MU_ERR_SQL;
101 }
102
103 rc = SQLAllocHandle (SQL_HANDLE_DBC, dp->env, &dp->dbc);
104 if (rc != SQL_SUCCESS)
105 {
106 mu_odbc_diag (dp, SQL_HANDLE_DBC, dp->dbc, "SQLAllocHandle");
107 return MU_ERR_SQL;
108 }
109
110 rc = SQLConnect(dp->dbc,
111 (SQLCHAR*)conn->dbname, SQL_NTS,
112 (SQLCHAR*)conn->login, SQL_NTS,
113 (SQLCHAR*)conn->password, SQL_NTS);
114 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
115 {
116 mu_odbc_diag (dp, SQL_HANDLE_DBC, dp->dbc, "SQLConnect");
117 return MU_ERR_SQL;
118 }
119
120 return 0;
121 }
122
123 static int
124 disconnect (mu_sql_connection_t conn)
125 {
126 struct mu_odbc_data *dp = conn->data;
127 SQLDisconnect (dp->dbc);
128 SQLFreeHandle (SQL_HANDLE_ENV, dp->env);
129 return 0;
130 }
131
132 static int
133 query (mu_sql_connection_t conn, char *query)
134 {
135 struct mu_odbc_data *dp = conn->data;
136 long rc;
137
138 if (dp->stmt)
139 SQLFreeHandle (SQL_HANDLE_STMT, dp->stmt);
140
141 rc = SQLAllocHandle (SQL_HANDLE_STMT, dp->dbc, &dp->stmt);
142 if (rc != SQL_SUCCESS)
143 {
144 mu_odbc_diag (dp, SQL_HANDLE_DBC, dp->dbc, "SQLAllocHandle");
145 return MU_ERR_SQL;
146 }
147
148 /* FIXME: In some implementations only default (forward only) cursors
149 may be available. Do we need a sequential access method after all?
150 FIXME2: On SQL_SUCCESS_WITH_INFO no info is output */
151 rc = SQLSetStmtAttr (dp->stmt,
152 SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_DYNAMIC,
153 0);
154 if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
155 {
156 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLSetStmtAttr");
157 return MU_ERR_SQL;
158 }
159
160 rc = SQLExecDirect (dp->stmt, query, SQL_NTS);
161 if (rc != SQL_SUCCESS)
162 {
163 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLExecDirect");
164 return MU_ERR_SQL;
165 }
166
167 return 0;
168 }
169
170 static int
171 store_result (mu_sql_connection_t conn)
172 {
173 struct mu_odbc_data *dp = conn->data;
174 list_create (&dp->result);
175 return 0;
176 }
177
178 static int
179 free_char_data (void *item, void *data ARG_UNUSED)
180 {
181 free (item);
182 return 0;
183 }
184
185 static int
186 release_result (mu_sql_connection_t conn)
187 {
188 struct mu_odbc_data *dp = conn->data;
189 list_do (dp->result, free_char_data, NULL);
190 list_destroy (&dp->result);
191 return 0;
192 }
193
194 static int
195 num_columns (mu_sql_connection_t conn, size_t *np)
196 {
197 struct mu_odbc_data *dp = conn->data;
198 SQLSMALLINT count;
199
200 if (SQLNumResultCols (dp->stmt, &count) != SQL_SUCCESS)
201 {
202 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLNumResultCount");
203 return MU_ERR_SQL;
204 }
205 *np = count;
206 return 0;
207 }
208
209 static int
210 num_tuples (mu_sql_connection_t conn, size_t *np)
211 {
212 struct mu_odbc_data *dp = conn->data;
213 SQLINTEGER count;
214
215 if (SQLRowCount (dp->stmt, &count) != SQL_SUCCESS)
216 {
217 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLRowCount");
218 return MU_ERR_SQL;
219 }
220 *np = count;
221 return 0;
222 }
223
224 static int
225 get_column (mu_sql_connection_t conn, size_t nrow, size_t ncol, char **pdata)
226 {
227 struct mu_odbc_data *dp = conn->data;
228 char buffer[1024];
229 SQLINTEGER size;
230
231 if (SQLFetchScroll (dp->stmt, SQL_FETCH_ABSOLUTE, nrow + 1) != SQL_SUCCESS)
232 {
233 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLFetchScroll");
234 return MU_ERR_SQL;
235 }
236
237 if (SQLGetData (dp->stmt, ncol + 1, SQL_C_CHAR,
238 buffer, sizeof buffer, &size) != SQL_SUCCESS)
239 {
240 mu_odbc_diag (dp, SQL_HANDLE_STMT, dp->stmt, "SQLGetData");
241 return MU_ERR_SQL;
242 }
243
244 *pdata = strdup (buffer);
245 list_append (dp->result, *pdata);
246 return 0;
247 }
248
249 #define DEFAULT_ERROR_BUFFER_SIZE 1024
250
251 static const char *
252 errstr (mu_sql_connection_t conn)
253 {
254 struct mu_odbc_data *dp = conn->data;
255 char state[16];
256 char nbuf[64];
257 SQLINTEGER nerror;
258 SQLSMALLINT msglen;
259 size_t length;
260
261 if (!dp->err.what)
262 return mu_strerror (0);
263
264 if (!dp->err.msg)
265 {
266 dp->err.msg = malloc (DEFAULT_ERROR_BUFFER_SIZE);
267 if (!dp->err.msg)
268 return mu_strerror (ENOMEM);
269 }
270
271 SQLGetDiagRec (dp->err.handle_type,
272 dp->err.handle,
273 1,
274 state,
275 &nerror,
276 dp->err.msg, DEFAULT_ERROR_BUFFER_SIZE, &msglen);
277
278 snprintf (nbuf, sizeof nbuf, "%d", (int) nerror);
279 length = strlen (dp->err.what) + 1
280 + strlen (state) + 1
281 + strlen (nbuf) + 1
282 + strlen (dp->err.msg) + 1;
283 if (dp->err.text)
284 free (dp->err.text);
285 dp->err.text = malloc (length);
286 if (!dp->err.text)
287 return dp->err.msg;
288
289 snprintf (dp->err.text, length, "%s %s %s %s", dp->err.what, state, nbuf,
290 dp->err.msg);
291 return dp->err.text;
292 }
293
294 MU_DECL_SQL_DISPATCH_T(odbc) = {
295 "odbc",
296 0,
297 init,
298 destroy,
299 connect,
300 disconnect,
301 query,
302 store_result,
303 release_result,
304 num_tuples,
305 num_columns,
306 get_column,
307 errstr,
308 };