2 * sql_unixodbc.c unixODBC rlm_sql driver
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 * Copyright 2000,2006 The FreeRADIUS server project
19 * Copyright 2000 Dmitri Ageev <d_ageev@ortcc.ru>
24 #include <freeradius-devel/radiusd.h>
29 typedef struct rlm_sql_unixodbc_conn {
35 } rlm_sql_unixodbc_conn_t;
38 USES_APPLE_DEPRECATED_API
42 /* Forward declarations */
43 static char const *sql_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
44 static int sql_state(long err_handle, rlm_sql_handle_t *handle, rlm_sql_config_t *config);
45 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
46 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
47 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
49 static int _sql_socket_destructor(rlm_sql_unixodbc_conn_t *conn)
51 DEBUG2("rlm_sql_unixodbc: Socket destructor called, closing socket");
53 if (conn->statement) {
54 SQLFreeStmt(conn->statement, SQL_DROP);
55 conn->statement = NULL;
59 SQLDisconnect(conn->dbc);
60 SQLFreeConnect(conn->dbc);
65 SQLFreeEnv(conn->env);
72 /*************************************************************************
74 * Function: sql_socket_init
76 * Purpose: Establish connection to the db
78 *************************************************************************/
79 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
80 rlm_sql_unixodbc_conn_t *conn;
83 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_unixodbc_conn_t));
84 talloc_set_destructor(conn, _sql_socket_destructor);
86 /* 1. Allocate environment handle and register version */
87 err_handle = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->env);
88 if (sql_state(err_handle, handle, config)) {
89 ERROR("rlm_sql_unixodbc: Can't allocate environment handle");
93 err_handle = SQLSetEnvAttr(conn->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
94 if (sql_state(err_handle, handle, config)) {
95 ERROR("rlm_sql_unixodbc: Can't register ODBC version");
99 /* 2. Allocate connection handle */
100 err_handle = SQLAllocHandle(SQL_HANDLE_DBC, conn->env, &conn->dbc);
101 if (sql_state(err_handle, handle, config)) {
102 ERROR("rlm_sql_unixodbc: Can't allocate connection handle");
106 /* 3. Connect to the datasource */
108 SQLCHAR *odbc_server, *odbc_login, *odbc_password;
110 memcpy(&odbc_server, &config->sql_server, sizeof(odbc_server));
111 memcpy(&odbc_login, &config->sql_login, sizeof(odbc_login));
112 memcpy(&odbc_password, &config->sql_password, sizeof(odbc_password));
113 err_handle = SQLConnect(conn->dbc,
114 odbc_server, strlen(config->sql_server),
115 odbc_login, strlen(config->sql_login),
116 odbc_password, strlen(config->sql_password));
119 if (sql_state(err_handle, handle, config)) {
120 ERROR("rlm_sql_unixodbc: Connection failed");
124 /* 4. Allocate the statement */
125 err_handle = SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &conn->statement);
126 if (sql_state(err_handle, handle, config)) {
127 ERROR("rlm_sql_unixodbc: Can't allocate the statement");
134 /*************************************************************************
136 * Function: sql_query
138 * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
141 *************************************************************************/
142 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
143 rlm_sql_unixodbc_conn_t *conn = handle->conn;
147 /* Executing query */
151 memcpy(&odbc_query, &query, sizeof(odbc_query));
152 err_handle = SQLExecDirect(conn->statement, odbc_query, strlen(query));
154 if ((state = sql_state(err_handle, handle, config))) {
155 if(state == RLM_SQL_RECONNECT) {
156 DEBUG("rlm_sql_unixodbc: rlm_sql will attempt to reconnect");
164 /*************************************************************************
166 * Function: sql_select_query
168 * Purpose: Issue a select query to the database
170 *************************************************************************/
171 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query) {
172 rlm_sql_unixodbc_conn_t *conn = handle->conn;
178 /* Only state = 0 means success */
179 if ((state = sql_query(handle, config, query))) {
183 colcount = sql_num_fields(handle, config);
188 /* Reserving memory for result */
189 conn->row = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */
191 for (i = 1; i <= colcount; i++) {
192 SQLColAttributes(conn->statement, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
193 conn->row[i - 1] = talloc_array(conn->row, char, ++len);
194 SQLBindCol(conn->statement, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, NULL);
201 /*************************************************************************
203 * Function: sql_store_result
205 * Purpose: database specific store_result function. Returns a result
208 *************************************************************************/
209 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
215 /*************************************************************************
217 * Function: sql_num_fields
219 * Purpose: database specific num_fields function. Returns number
220 * of columns from query
222 *************************************************************************/
223 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
224 rlm_sql_unixodbc_conn_t *conn = handle->conn;
226 SQLSMALLINT num_fields = 0;
228 err_handle = SQLNumResultCols(conn->statement,&num_fields);
229 if (sql_state(err_handle, handle, config)) {
237 /*************************************************************************
239 * Function: sql_num_rows
241 * Purpose: database specific num_rows. Returns number of rows in
244 *************************************************************************/
245 static int sql_num_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
246 return sql_affected_rows(handle, config);
250 /*************************************************************************
252 * Function: sql_fetch_row
254 * Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
255 * with all the data for the query in 'handle->row'. Returns
256 * 0 on success, -1 on failure, RLM_SQL_RECONNECT if 'database is down'.
258 *************************************************************************/
259 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
260 rlm_sql_unixodbc_conn_t *conn = handle->conn;
266 err_handle = SQLFetch(conn->statement);
267 if(err_handle == SQL_NO_DATA_FOUND) {
271 if ((state = sql_state(err_handle, handle, config))) {
272 if(state == RLM_SQL_RECONNECT) {
273 DEBUG("rlm_sql_unixodbc: rlm_sql will attempt to reconnect");
279 handle->row = conn->row;
284 /*************************************************************************
286 * Function: sql_finish_select_query
288 * Purpose: End the select query, such as freeing memory or result
290 *************************************************************************/
291 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config) {
292 rlm_sql_unixodbc_conn_t *conn = handle->conn;
294 sql_free_result(handle, config);
297 * SQL_CLOSE - The cursor (if any) associated with the statement
298 * handle (StatementHandle) is closed and all pending results are
299 * discarded. The application can reopen the cursor by calling
300 * SQLExecute() with the same or different values in the
301 * application variables (if any) that are bound to StatementHandle.
302 * If no cursor has been associated with the statement handle,
303 * this option has no effect (no warning or error is generated).
305 * So, this call does NOT free the statement at all, it merely
306 * resets it for the next call. This is terrible terrible naming.
308 SQLFreeStmt(conn->statement, SQL_CLOSE);
313 /*************************************************************************
315 * Function: sql_finish_query
317 * Purpose: End the query, such as freeing memory
319 *************************************************************************/
320 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
321 rlm_sql_unixodbc_conn_t *conn = handle->conn;
323 SQLFreeStmt(conn->statement, SQL_CLOSE);
328 /*************************************************************************
330 * Function: sql_free_result
332 * Purpose: database specific free_result. Frees memory allocated
335 *************************************************************************/
336 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
337 rlm_sql_unixodbc_conn_t *conn = handle->conn;
339 TALLOC_FREE(conn->row);
344 /*************************************************************************
346 * Function: sql_error
348 * Purpose: database specific error. Returns error associated with
351 *************************************************************************/
352 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
355 SQLINTEGER errornum = 0;
356 SQLSMALLINT length = 255;
357 static char result[1024]; /* NOT thread-safe! */
359 rlm_sql_unixodbc_conn_t *conn = handle->conn;
361 error[0] = state[0] = '\0';
363 SQLError(conn->env, conn->dbc, conn->statement, state, &errornum,
364 error, 256, &length);
366 sprintf(result, "%s %s", state, error);
367 result[sizeof(result) - 1] = '\0'; /* catch idiot thread issues */
371 /*************************************************************************
373 * Function: sql_state
375 * Purpose: Returns 0 for success, RLM_SQL_RECONNECT if the error was
376 * connection related or -1 for other errors
378 *************************************************************************/
379 static sql_rcode_t sql_state(long err_handle, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
382 SQLINTEGER errornum = 0;
383 SQLSMALLINT length = 255;
386 rlm_sql_unixodbc_conn_t *conn = handle->conn;
388 if(SQL_SUCCEEDED(err_handle)) {
389 return 0; /* on success, just return 0 */
392 error[0] = state[0] = '\0';
394 SQLError(conn->env, conn->dbc, conn->statement, state, &errornum,
395 error, 256, &length);
397 if(state[0] == '0') {
399 /* SQLSTATE 01 class contains info and warning messages */
401 INFO("rlm_sql_unixodbc: %s %s", state, error);
403 case '0': /* SQLSTATE 00 class means success */
407 /* SQLSTATE 08 class describes various connection errors */
409 ERROR("rlm_sql_unixodbc: SQL down %s %s", state, error);
410 res = RLM_SQL_RECONNECT;
413 /* any other SQLSTATE means error */
415 ERROR("rlm_sql_unixodbc: %s %s", state, error);
424 /*************************************************************************
426 * Function: sql_affected_rows
428 * Purpose: Return the number of rows affected by the query (update,
431 *************************************************************************/
432 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
433 rlm_sql_unixodbc_conn_t *conn = handle->conn;
435 SQLLEN affected_rows;
437 err_handle = SQLRowCount(conn->statement, &affected_rows);
438 if (sql_state(err_handle, handle, config)) {
442 return affected_rows;
446 /* Exported to rlm_sql */
447 rlm_sql_module_t rlm_sql_unixodbc = {
460 sql_finish_select_query,