Fixup talloc destructors
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_iodbc / rlm_sql_iodbc.c
1 /*
2  * sql_iodbc.c  iODBC support for FreeRadius
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Jeff Carneal <jeff@apex.net>
22  */
23
24 RCSID("$Id$")
25 USES_APPLE_DEPRECATED_API
26
27 #include <freeradius-devel/radiusd.h>
28
29 #include <sys/stat.h>
30
31 #include <isql.h>
32 #include <isqlext.h>
33 #include <sqltypes.h>
34
35 #include "rlm_sql.h"
36
37 #define IODBC_MAX_ERROR_LEN 256
38
39 typedef struct rlm_sql_iodbc_conn {
40         HENV    env_handle;
41         HDBC    dbc_handle;
42         HSTMT   stmt_handle;
43         int     id;
44
45         rlm_sql_row_t row;
46
47         struct sql_socket *next;
48
49         SQLCHAR error[IODBC_MAX_ERROR_LEN];
50         void    *conn;
51 } rlm_sql_iodbc_conn_t;
52
53 static char const *sql_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
54 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
55
56 static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
57 {
58         DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
59
60         if (conn->stmt_handle) {
61                 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
62         }
63
64         if (conn->dbc_handle) {
65                 SQLDisconnect(conn->dbc_handle);
66                 SQLFreeConnect(conn->dbc_handle);
67         }
68
69         if (conn->env_handle) {
70                 SQLFreeEnv(conn->env_handle);
71         }
72
73         return 0;
74 }
75
76 /*************************************************************************
77  *
78  *      Function: sql_socket_init
79  *
80  *      Purpose: Establish connection to the db
81  *
82  *************************************************************************/
83 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
84
85         rlm_sql_iodbc_conn_t *conn;
86         SQLRETURN rcode;
87
88         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
89         talloc_set_destructor(conn, _sql_socket_destructor);
90
91         rcode = SQLAllocEnv(&conn->env_handle);
92         if (!SQL_SUCCEEDED(rcode)) {
93                 ERROR("sql_create_socket: SQLAllocEnv failed:  %s",
94                                 sql_error(handle, config));
95                 return -1;
96         }
97
98         rcode = SQLAllocConnect(conn->env_handle,
99                                 &conn->dbc_handle);
100         if (!SQL_SUCCEEDED(rcode)) {
101                 ERROR("sql_create_socket: SQLAllocConnect failed:  %s",
102                                 sql_error(handle, config));
103                 return -1;
104         }
105
106         /*
107          *      The iodbc API doesn't qualify arguments as const even when they should be.
108          */
109         {
110                 SQLCHAR *server, *login, *password;
111
112                 memcpy(&server, &config->sql_server, sizeof(server));
113                 memcpy(&login, &config->sql_login, sizeof(login));
114                 memcpy(&password, &config->sql_password, sizeof(password));
115
116                 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
117         }
118         if (!SQL_SUCCEEDED(rcode)) {
119                 ERROR("sql_create_socket: SQLConnectfailed:  %s",
120                                 sql_error(handle, config));
121                 return -1;
122         }
123
124         return 0;
125 }
126
127 /*************************************************************************
128  *
129  *      Function: sql_query
130  *
131  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
132  *             the database.
133  *
134  *************************************************************************/
135 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
136
137         rlm_sql_iodbc_conn_t *conn = handle->conn;
138         SQLRETURN rcode;
139
140         rcode = SQLAllocStmt(conn->dbc_handle,
141                              &conn->stmt_handle);
142         if (!SQL_SUCCEEDED(rcode)) {
143                 ERROR("sql_create_socket: SQLAllocStmt failed:  %s",
144                                 sql_error(handle, config));
145                 return -1;
146         }
147
148         if (!conn->dbc_handle) {
149                 ERROR("sql_query:  Socket not connected");
150                 return -1;
151         }
152
153         {
154                 SQLCHAR *statement;
155
156                 memcpy(&statement, &query, sizeof(statement));
157                 rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
158         }
159
160         if (!SQL_SUCCEEDED(rcode)) {
161                 ERROR("sql_query: failed:  %s",
162                                 sql_error(handle, config));
163                 return -1;
164         }
165
166         return 0;
167 }
168
169
170 /*************************************************************************
171  *
172  *      Function: sql_select_query
173  *
174  *      Purpose: Issue a select query to the database
175  *
176  *************************************************************************/
177 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
178
179         int numfields = 0;
180         int i = 0;
181         char **row = NULL;
182         long len = 0;
183         rlm_sql_iodbc_conn_t *conn = handle->conn;
184
185         if(sql_query(handle, config, query) < 0) {
186                 return -1;
187         }
188
189         numfields = sql_num_fields(handle, config);
190
191         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
192         memset(row, 0, (sizeof(char *) * (numfields)));
193         row[numfields] = NULL;
194
195         for(i=1; i<=numfields; i++) {
196                 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
197                 len++;
198
199                 /*
200                  * Allocate space for each column
201                  */
202                 row[i - 1] = rad_malloc((size_t) len);
203
204                 /*
205                  * This makes me feel dirty, but, according to Microsoft, it works.
206                  * Any ODBC datatype can be converted to a 'char *' according to
207                  * the following:
208                  *
209                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
210                  */
211                 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
212         }
213
214         conn->row = row;
215
216         return 0;
217 }
218
219
220 /*************************************************************************
221  *
222  *      Function: sql_store_result
223  *
224  *      Purpose: database specific store_result function. Returns a result
225  *             set for the query.
226  *
227  *************************************************************************/
228 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
229
230         return 0;
231 }
232
233
234 /*************************************************************************
235  *
236  *      Function: sql_num_fields
237  *
238  *      Purpose: database specific num_fields function. Returns number
239  *             of columns from query
240  *
241  *************************************************************************/
242 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
243
244         SQLSMALLINT count=0;
245         rlm_sql_iodbc_conn_t *conn = handle->conn;
246
247         SQLNumResultCols(conn->stmt_handle, &count);
248
249         return (int)count;
250 }
251
252 /*************************************************************************
253  *
254  *      Function: sql_num_rows
255  *
256  *      Purpose: database specific num_rows. Returns number of rows in
257  *             query
258  *
259  *************************************************************************/
260 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
261         /*
262          * I presume this function is used to determine the number of
263          * rows in a result set *before* fetching them.  I don't think
264          * this is possible in ODBC 2.x, but I'd be happy to be proven
265          * wrong.  If you know how to do this, email me at jeff@apex.net
266          */
267         return 0;
268 }
269
270
271 /*************************************************************************
272  *
273  *      Function: sql_fetch_row
274  *
275  *      Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
276  *             with all the data for the query in 'handle->row'. Returns
277  *               0 on success, -1 on failure, RLM_SQL_RECONNECT if 'database is down'
278  *
279  *************************************************************************/
280 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
281
282         SQLRETURN rc;
283         rlm_sql_iodbc_conn_t *conn = handle->conn;
284
285         handle->row = NULL;
286
287         if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
288                 return 0;
289         }
290         /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
291
292         handle->row = conn->row;
293         return 0;
294 }
295
296
297
298 /*************************************************************************
299  *
300  *      Function: sql_free_result
301  *
302  *      Purpose: database specific free_result. Frees memory allocated
303  *             for a result set
304  *
305  *************************************************************************/
306 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
307
308         int i=0;
309         rlm_sql_iodbc_conn_t *conn = handle->conn;
310
311         for(i=0; i<sql_num_fields(handle, config); i++) {
312                 free(conn->row[i]);
313         }
314         free(conn->row);
315         conn->row=NULL;
316
317         SQLFreeStmt( conn->stmt_handle, SQL_DROP );
318
319         return 0;
320 }
321
322
323 /*************************************************************************
324  *
325  *      Function: sql_error
326  *
327  *      Purpose: database specific error. Returns error associated with
328  *             connection
329  *
330  *************************************************************************/
331 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
332
333         SQLINTEGER errornum = 0;
334         SQLSMALLINT length = 0;
335         SQLCHAR state[256] = "";
336         rlm_sql_iodbc_conn_t *conn = handle->conn;
337
338         conn->error[0] = '\0';
339
340         SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
341                 state, &errornum, conn->error, IODBC_MAX_ERROR_LEN, &length);
342         return (char const *) &conn->error;
343 }
344
345 /*************************************************************************
346  *
347  *      Function: sql_finish_query
348  *
349  *      Purpose: End the query, such as freeing memory
350  *
351  *************************************************************************/
352 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
353
354         return sql_free_result(handle, config);
355 }
356
357 /*************************************************************************
358  *
359  *      Function: sql_finish_select_query
360  *
361  *      Purpose: End the select query, such as freeing memory or result
362  *
363  *************************************************************************/
364 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
365         return sql_free_result(handle, config);
366 }
367
368 /*************************************************************************
369  *
370  *      Function: sql_affected_rows
371  *
372  *      Purpose: Return the number of rows affected by the query (update,
373  *             or insert)
374  *
375  *************************************************************************/
376 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
377
378         long count;
379         rlm_sql_iodbc_conn_t *conn = handle->conn;
380
381         SQLRowCount(conn->stmt_handle, &count);
382         return (int)count;
383 }
384
385 /* Exported to rlm_sql */
386 rlm_sql_module_t rlm_sql_iodbc = {
387         "rlm_sql_iodbc",
388         NULL,
389         sql_socket_init,
390         sql_query,
391         sql_select_query,
392         sql_store_result,
393         sql_num_fields,
394         sql_num_rows,
395         sql_fetch_row,
396         sql_free_result,
397         sql_error,
398         sql_finish_query,
399         sql_finish_select_query,
400         sql_affected_rows
401 };