Define return codes for SQL drivers
[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(void *c)
57 {
58         rlm_sql_iodbc_conn_t *conn = c;
59         
60         DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
61         
62         if (conn->stmt_handle) {
63                 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
64         }
65         
66         if (conn->dbc_handle) {
67                 SQLDisconnect(conn->dbc_handle);
68                 SQLFreeConnect(conn->dbc_handle);
69         }
70         
71         if (conn->env_handle) {
72                 SQLFreeEnv(conn->env_handle);
73         }
74         
75         return 0;
76 }
77
78 /*************************************************************************
79  *
80  *      Function: sql_socket_init
81  *
82  *      Purpose: Establish connection to the db
83  *
84  *************************************************************************/
85 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
86
87         rlm_sql_iodbc_conn_t *conn;
88         SQLRETURN rcode;
89
90         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
91         talloc_set_destructor((void *) conn, sql_socket_destructor);
92
93         rcode = SQLAllocEnv(&conn->env_handle);
94         if (!SQL_SUCCEEDED(rcode)) {
95                 ERROR("sql_create_socket: SQLAllocEnv failed:  %s",
96                                 sql_error(handle, config));
97                 return -1;
98         }
99
100         rcode = SQLAllocConnect(conn->env_handle,
101                                 &conn->dbc_handle);
102         if (!SQL_SUCCEEDED(rcode)) {
103                 ERROR("sql_create_socket: SQLAllocConnect failed:  %s",
104                                 sql_error(handle, config));
105                 return -1;
106         }
107
108         /*
109          *      The iodbc API doesn't qualify arguments as const even when they should be.
110          */
111         {
112                 SQLCHAR *server, *login, *password;
113                 
114                 memcpy(&server, &config->sql_server, sizeof(server));
115                 memcpy(&login, &config->sql_login, sizeof(login));
116                 memcpy(&password, &config->sql_password, sizeof(password));
117                 
118                 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
119         }
120         if (!SQL_SUCCEEDED(rcode)) {
121                 ERROR("sql_create_socket: SQLConnectfailed:  %s",
122                                 sql_error(handle, config));
123                 return -1;
124         }
125
126         return 0;
127 }
128
129 /*************************************************************************
130  *
131  *      Function: sql_query
132  *
133  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
134  *             the database.
135  *
136  *************************************************************************/
137 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
138
139         rlm_sql_iodbc_conn_t *conn = handle->conn;
140         SQLRETURN rcode;
141
142         rcode = SQLAllocStmt(conn->dbc_handle,
143                              &conn->stmt_handle);
144         if (!SQL_SUCCEEDED(rcode)) {
145                 ERROR("sql_create_socket: SQLAllocStmt failed:  %s",
146                                 sql_error(handle, config));
147                 return -1;
148         }
149
150         if (!conn->dbc_handle) {
151                 ERROR("sql_query:  Socket not connected");
152                 return -1;
153         }
154
155         {
156                 SQLCHAR *statement;
157                 
158                 memcpy(&statement, &query, sizeof(statement));
159                 rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
160         }
161         
162         if (!SQL_SUCCEEDED(rcode)) {
163                 ERROR("sql_query: failed:  %s",
164                                 sql_error(handle, config));
165                 return -1;
166         }
167
168         return 0;
169 }
170
171
172 /*************************************************************************
173  *
174  *      Function: sql_select_query
175  *
176  *      Purpose: Issue a select query to the database
177  *
178  *************************************************************************/
179 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
180
181         int numfields = 0;
182         int i = 0;
183         char **row = NULL;
184         long len = 0;
185         rlm_sql_iodbc_conn_t *conn = handle->conn;
186
187         if(sql_query(handle, config, query) < 0) {
188                 return -1;
189         }
190
191         numfields = sql_num_fields(handle, config);
192
193         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
194         memset(row, 0, (sizeof(char *) * (numfields)));
195         row[numfields] = NULL;
196
197         for(i=1; i<=numfields; i++) {
198                 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
199                 len++;
200
201                 /*
202                  * Allocate space for each column
203                  */
204                 row[i - 1] = rad_malloc((size_t) len);
205
206                 /*
207                  * This makes me feel dirty, but, according to Microsoft, it works.
208                  * Any ODBC datatype can be converted to a 'char *' according to
209                  * the following:
210                  *
211                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
212                  */
213                 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
214         }
215
216         conn->row = row;
217
218         return 0;
219 }
220
221
222 /*************************************************************************
223  *
224  *      Function: sql_store_result
225  *
226  *      Purpose: database specific store_result function. Returns a result
227  *             set for the query.
228  *
229  *************************************************************************/
230 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
231
232         return 0;
233 }
234
235
236 /*************************************************************************
237  *
238  *      Function: sql_num_fields
239  *
240  *      Purpose: database specific num_fields function. Returns number
241  *             of columns from query
242  *
243  *************************************************************************/
244 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
245
246         SQLSMALLINT count=0;
247         rlm_sql_iodbc_conn_t *conn = handle->conn;
248
249         SQLNumResultCols(conn->stmt_handle, &count);
250
251         return (int)count;
252 }
253
254 /*************************************************************************
255  *
256  *      Function: sql_num_rows
257  *
258  *      Purpose: database specific num_rows. Returns number of rows in
259  *             query
260  *
261  *************************************************************************/
262 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
263         /*
264          * I presume this function is used to determine the number of
265          * rows in a result set *before* fetching them.  I don't think
266          * this is possible in ODBC 2.x, but I'd be happy to be proven
267          * wrong.  If you know how to do this, email me at jeff@apex.net
268          */
269         return 0;
270 }
271
272
273 /*************************************************************************
274  *
275  *      Function: sql_fetch_row
276  *
277  *      Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
278  *             with all the data for the query in 'handle->row'. Returns
279  *               0 on success, -1 on failure, RLM_SQL_RECONNECT if 'database is down'
280  *
281  *************************************************************************/
282 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
283
284         SQLRETURN rc;
285         rlm_sql_iodbc_conn_t *conn = handle->conn;
286
287         handle->row = NULL;
288
289         if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
290                 return 0;
291         }
292         /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
293
294         handle->row = conn->row;
295         return 0;
296 }
297
298
299
300 /*************************************************************************
301  *
302  *      Function: sql_free_result
303  *
304  *      Purpose: database specific free_result. Frees memory allocated
305  *             for a result set
306  *
307  *************************************************************************/
308 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
309
310         int i=0;
311         rlm_sql_iodbc_conn_t *conn = handle->conn;
312
313         for(i=0; i<sql_num_fields(handle, config); i++) {
314                 free(conn->row[i]);
315         }
316         free(conn->row);
317         conn->row=NULL;
318
319         SQLFreeStmt( conn->stmt_handle, SQL_DROP );
320
321         return 0;
322 }
323
324
325 /*************************************************************************
326  *
327  *      Function: sql_error
328  *
329  *      Purpose: database specific error. Returns error associated with
330  *             connection
331  *
332  *************************************************************************/
333 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
334
335         SQLINTEGER errornum = 0;
336         SQLSMALLINT length = 0;
337         SQLCHAR state[256] = "";
338         rlm_sql_iodbc_conn_t *conn = handle->conn;
339
340         conn->error[0] = '\0';
341         
342         SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
343                 state, &errornum, conn->error, IODBC_MAX_ERROR_LEN, &length);
344         return (char const *) &conn->error;
345 }
346
347 /*************************************************************************
348  *
349  *      Function: sql_finish_query
350  *
351  *      Purpose: End the query, such as freeing memory
352  *
353  *************************************************************************/
354 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
355
356         return sql_free_result(handle, config);
357 }
358
359 /*************************************************************************
360  *
361  *      Function: sql_finish_select_query
362  *
363  *      Purpose: End the select query, such as freeing memory or result
364  *
365  *************************************************************************/
366 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
367         return sql_free_result(handle, config);
368 }
369
370 /*************************************************************************
371  *
372  *      Function: sql_affected_rows
373  *
374  *      Purpose: Return the number of rows affected by the query (update,
375  *             or insert)
376  *
377  *************************************************************************/
378 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
379
380         long count;
381         rlm_sql_iodbc_conn_t *conn = handle->conn;
382
383         SQLRowCount(conn->stmt_handle, &count);
384         return (int)count;
385 }
386
387 /* Exported to rlm_sql */
388 rlm_sql_module_t rlm_sql_iodbc = {
389         "rlm_sql_iodbc",
390         NULL,
391         sql_socket_init,
392         sql_query,
393         sql_select_query,
394         sql_store_result,
395         sql_num_fields,
396         sql_num_rows,
397         sql_fetch_row,
398         sql_free_result,
399         sql_error,
400         sql_finish_query,
401         sql_finish_select_query,
402         sql_affected_rows
403 };