2 * sql_iodbc.c iODBC support for FreeRadius
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.
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.
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
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * Copyright 2000 Jeff Carneal <jeff@apex.net>
25 USES_APPLE_DEPRECATED_API
27 #include <freeradius-devel/radiusd.h>
37 #define IODBC_MAX_ERROR_LEN 256
39 typedef struct rlm_sql_iodbc_conn {
47 struct sql_socket *next;
49 SQLCHAR error[IODBC_MAX_ERROR_LEN];
51 } rlm_sql_iodbc_conn_t;
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);
56 static int sql_socket_destructor(void *c)
58 rlm_sql_iodbc_conn_t *conn = c;
60 DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
62 if (conn->stmt_handle) {
63 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
66 if (conn->dbc_handle) {
67 SQLDisconnect(conn->dbc_handle);
68 SQLFreeConnect(conn->dbc_handle);
71 if (conn->env_handle) {
72 SQLFreeEnv(conn->env_handle);
78 /*************************************************************************
80 * Function: sql_socket_init
82 * Purpose: Establish connection to the db
84 *************************************************************************/
85 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
87 rlm_sql_iodbc_conn_t *conn;
90 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
91 talloc_set_destructor((void *) conn, sql_socket_destructor);
93 rcode = SQLAllocEnv(&conn->env_handle);
94 if (!SQL_SUCCEEDED(rcode)) {
95 ERROR("sql_create_socket: SQLAllocEnv failed: %s",
96 sql_error(handle, config));
100 rcode = SQLAllocConnect(conn->env_handle,
102 if (!SQL_SUCCEEDED(rcode)) {
103 ERROR("sql_create_socket: SQLAllocConnect failed: %s",
104 sql_error(handle, config));
109 * The iodbc API doesn't qualify arguments as const even when they should be.
112 SQLCHAR *server, *login, *password;
114 memcpy(&server, &config->sql_server, sizeof(server));
115 memcpy(&login, &config->sql_login, sizeof(login));
116 memcpy(&password, &config->sql_password, sizeof(password));
118 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
120 if (!SQL_SUCCEEDED(rcode)) {
121 ERROR("sql_create_socket: SQLConnectfailed: %s",
122 sql_error(handle, config));
129 /*************************************************************************
131 * Function: sql_query
133 * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
136 *************************************************************************/
137 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
139 rlm_sql_iodbc_conn_t *conn = handle->conn;
142 rcode = SQLAllocStmt(conn->dbc_handle,
144 if (!SQL_SUCCEEDED(rcode)) {
145 ERROR("sql_create_socket: SQLAllocStmt failed: %s",
146 sql_error(handle, config));
150 if (!conn->dbc_handle) {
151 ERROR("sql_query: Socket not connected");
158 memcpy(&statement, &query, sizeof(statement));
159 rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
162 if (!SQL_SUCCEEDED(rcode)) {
163 ERROR("sql_query: failed: %s",
164 sql_error(handle, config));
172 /*************************************************************************
174 * Function: sql_select_query
176 * Purpose: Issue a select query to the database
178 *************************************************************************/
179 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
185 rlm_sql_iodbc_conn_t *conn = handle->conn;
187 if(sql_query(handle, config, query) < 0) {
191 numfields = sql_num_fields(handle, config);
193 row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
194 memset(row, 0, (sizeof(char *) * (numfields)));
195 row[numfields] = NULL;
197 for(i=1; i<=numfields; i++) {
198 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
202 * Allocate space for each column
204 row[i - 1] = rad_malloc((size_t) len);
207 * This makes me feel dirty, but, according to Microsoft, it works.
208 * Any ODBC datatype can be converted to a 'char *' according to
211 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
213 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
222 /*************************************************************************
224 * Function: sql_store_result
226 * Purpose: database specific store_result function. Returns a result
229 *************************************************************************/
230 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
236 /*************************************************************************
238 * Function: sql_num_fields
240 * Purpose: database specific num_fields function. Returns number
241 * of columns from query
243 *************************************************************************/
244 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
247 rlm_sql_iodbc_conn_t *conn = handle->conn;
249 SQLNumResultCols(conn->stmt_handle, &count);
254 /*************************************************************************
256 * Function: sql_num_rows
258 * Purpose: database specific num_rows. Returns number of rows in
261 *************************************************************************/
262 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
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
273 /*************************************************************************
275 * Function: sql_fetch_row
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'
281 *************************************************************************/
282 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
285 rlm_sql_iodbc_conn_t *conn = handle->conn;
289 if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
292 /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
294 handle->row = conn->row;
300 /*************************************************************************
302 * Function: sql_free_result
304 * Purpose: database specific free_result. Frees memory allocated
307 *************************************************************************/
308 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
311 rlm_sql_iodbc_conn_t *conn = handle->conn;
313 for(i=0; i<sql_num_fields(handle, config); i++) {
319 SQLFreeStmt( conn->stmt_handle, SQL_DROP );
325 /*************************************************************************
327 * Function: sql_error
329 * Purpose: database specific error. Returns error associated with
332 *************************************************************************/
333 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
335 SQLINTEGER errornum = 0;
336 SQLSMALLINT length = 0;
337 SQLCHAR state[256] = "";
338 rlm_sql_iodbc_conn_t *conn = handle->conn;
340 conn->error[0] = '\0';
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;
347 /*************************************************************************
349 * Function: sql_finish_query
351 * Purpose: End the query, such as freeing memory
353 *************************************************************************/
354 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
356 return sql_free_result(handle, config);
359 /*************************************************************************
361 * Function: sql_finish_select_query
363 * Purpose: End the select query, such as freeing memory or result
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);
370 /*************************************************************************
372 * Function: sql_affected_rows
374 * Purpose: Return the number of rows affected by the query (update,
377 *************************************************************************/
378 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
381 rlm_sql_iodbc_conn_t *conn = handle->conn;
383 SQLRowCount(conn->stmt_handle, &count);
387 /* Exported to rlm_sql */
388 rlm_sql_module_t rlm_sql_iodbc = {
401 sql_finish_select_query,