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