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(rlm_sql_iodbc_conn_t *conn)
58 DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
60 if (conn->stmt_handle) {
61 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
64 if (conn->dbc_handle) {
65 SQLDisconnect(conn->dbc_handle);
66 SQLFreeConnect(conn->dbc_handle);
69 if (conn->env_handle) {
70 SQLFreeEnv(conn->env_handle);
76 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
79 rlm_sql_iodbc_conn_t *conn;
82 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
83 talloc_set_destructor(conn, _sql_socket_destructor);
85 rcode = SQLAllocEnv(&conn->env_handle);
86 if (!SQL_SUCCEEDED(rcode)) {
87 ERROR("rlm_sql_iodbc: SQLAllocEnv failed: %s", sql_error(handle, config));
91 rcode = SQLAllocConnect(conn->env_handle,
93 if (!SQL_SUCCEEDED(rcode)) {
94 ERROR("rlm_sql_iodbc: SQLAllocConnect failed: %s", sql_error(handle, config));
99 * The iodbc API doesn't qualify arguments as const even when they should be.
102 SQLCHAR *server, *login, *password;
104 memcpy(&server, &config->sql_server, sizeof(server));
105 memcpy(&login, &config->sql_login, sizeof(login));
106 memcpy(&password, &config->sql_password, sizeof(password));
108 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
110 if (!SQL_SUCCEEDED(rcode)) {
111 ERROR("rlm_sql_iodbc: SQLConnectfailed: %s", sql_error(handle, config));
118 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
120 rlm_sql_iodbc_conn_t *conn = handle->conn;
123 rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt_handle);
124 if (!SQL_SUCCEEDED(rcode)) {
125 ERROR("rlm_sql_iodbc: SQLAllocStmt failed: %s", sql_error(handle, config));
129 if (!conn->dbc_handle) {
130 ERROR("rlm_sql_iodbc: Socket not connected");
137 memcpy(&statement, &query, sizeof(statement));
138 rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
141 if (!SQL_SUCCEEDED(rcode)) {
142 ERROR("rlm_sql_iodbc: Query failed %s", sql_error(handle, config));
149 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
155 rlm_sql_iodbc_conn_t *conn = handle->conn;
157 if(sql_query(handle, config, query) < 0) {
161 numfields = sql_num_fields(handle, config);
163 row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
164 memset(row, 0, (sizeof(char *) * (numfields)));
165 row[numfields] = NULL;
167 for(i=1; i<=numfields; i++) {
168 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
172 * Allocate space for each column
174 row[i - 1] = rad_malloc((size_t) len);
177 * This makes me feel dirty, but, according to Microsoft, it works.
178 * Any ODBC datatype can be converted to a 'char *' according to
181 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
183 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
191 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
196 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
200 rlm_sql_iodbc_conn_t *conn = handle->conn;
202 SQLNumResultCols(conn->stmt_handle, &count);
207 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
210 * I presume this function is used to determine the number of
211 * rows in a result set *before* fetching them. I don't think
212 * this is possible in ODBC 2.x, but I'd be happy to be proven
213 * wrong. If you know how to do this, email me at jeff@apex.net
218 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
221 rlm_sql_iodbc_conn_t *conn = handle->conn;
225 if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
228 /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
230 handle->row = conn->row;
234 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
237 rlm_sql_iodbc_conn_t *conn = handle->conn;
239 for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]);
243 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
248 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
250 SQLINTEGER errornum = 0;
251 SQLSMALLINT length = 0;
252 SQLCHAR state[256] = "";
253 rlm_sql_iodbc_conn_t *conn = handle->conn;
255 conn->error[0] = '\0';
257 SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
258 state, &errornum, conn->error, IODBC_MAX_ERROR_LEN, &length);
259 return (char const *) &conn->error;
262 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
264 return sql_free_result(handle, config);
267 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
269 return sql_free_result(handle, config);
272 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
275 rlm_sql_iodbc_conn_t *conn = handle->conn;
277 SQLRowCount(conn->stmt_handle, &count);
281 /* Exported to rlm_sql */
282 extern rlm_sql_module_t rlm_sql_iodbc;
283 rlm_sql_module_t rlm_sql_iodbc = {
284 .name = "rlm_sql_iodbc",
285 .sql_socket_init = sql_socket_init,
286 .sql_query = sql_query,
287 .sql_select_query = sql_select_query,
288 .sql_store_result = sql_store_result,
289 .sql_num_fields = sql_num_fields,
290 .sql_num_rows = sql_num_rows,
291 .sql_affected_rows = sql_affected_rows,
292 .sql_fetch_row = sql_fetch_row,
293 .sql_free_result = sql_free_result,
294 .sql_error = sql_error,
295 .sql_finish_query = sql_finish_query,
296 .sql_finish_select_query = sql_finish_select_query