2 * sql_oracle.c Oracle (OCI) routines for rlm_sql
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 David Kerry <davidk@snti.com>
24 #include <freeradius-devel/radiusd.h>
29 * There are typos in the Oracle Instaclient where the definition controlling prototype
30 * format is _STDC_ (not __STDC__).
32 * There are still cases where the oracle headers do not declare ANSI C function types
33 * but this at least cuts down the errors.
35 * -Wno-strict-prototypes does the rest.
37 #if defined(__STDC__) && __STDC__
44 typedef struct rlm_sql_oracle_conn_t {
52 int col_count; //!< Number of columns associated with the result set
54 } rlm_sql_oracle_conn_t;
56 #define MAX_DATASTR_LEN 64
59 /*************************************************************************
63 * Purpose: database specific error. Returns error associated with
66 *************************************************************************/
67 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
70 static char msgbuf[512];
72 rlm_sql_oracle_conn_t *conn = handle->conn;
74 if (!conn) return "rlm_sql_oracle: no connection to db";
78 OCIErrorGet((dvoid *) conn->error, 1, (OraText *) NULL, &errcode, (OraText *) msgbuf,
79 sizeof(msgbuf), OCI_HTYPE_ERROR);
87 /*************************************************************************
89 * Function: sql_check_error
91 * Purpose: check the error to see if the server is down
93 *************************************************************************/
94 static int sql_check_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
97 if (strstr(sql_error(handle, config), "ORA-03113") || strstr(sql_error(handle, config), "ORA-03114")) {
98 ERROR("rlm_sql_oracle: OCI_SERVER_NOT_CONNECTED");
99 return RLM_SQL_RECONNECT;
102 ERROR("rlm_sql_oracle: OCI_SERVER_NORMAL");
107 static int sql_socket_destructor(void *c)
109 rlm_sql_oracle_conn_t *conn = c;
112 OCILogoff(conn->ctx, conn->error);
116 OCIHandleFree((dvoid *)conn->query, OCI_HTYPE_STMT);
120 OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
124 OCIHandleFree((dvoid *)conn->env, OCI_HTYPE_ENV);
130 /*************************************************************************
132 * Function: sql_socket_init
134 * Purpose: Establish connection to the db
136 *************************************************************************/
137 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
140 rlm_sql_oracle_conn_t *conn;
142 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_oracle_conn_t));
143 talloc_set_destructor((void *) conn, sql_socket_destructor);
146 * Initialises the oracle environment
148 if (OCIEnvCreate(&conn->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
149 ERROR("rlm_sql_oracle: Couldn't init Oracle OCI environment (OCIEnvCreate())");
155 * Allocates an error handle
157 if (OCIHandleAlloc((dvoid *)conn->env, (dvoid **)&conn->error, OCI_HTYPE_ERROR, 0, NULL)) {
158 ERROR("rlm_sql_oracle: Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
164 * Allocate handles for select and update queries
166 if (OCIHandleAlloc((dvoid *)conn->env, (dvoid **)&conn->query, OCI_HTYPE_STMT, 0, NULL)) {
167 ERROR("rlm_sql_oracle: Couldn't init Oracle query handles: %s", sql_error(handle, config));
173 * Login to the oracle server
175 if (OCILogon(conn->env, conn->error, &conn->ctx,
176 (OraText const *)config->sql_login, strlen(config->sql_login),
177 (OraText const *)config->sql_password, strlen(config->sql_password),
178 (OraText const *)config->sql_db, strlen(config->sql_db))) {
179 ERROR("rlm_sql_oracle: Oracle logon failed: '%s'", sql_error(handle, config));
187 /*************************************************************************
189 * Function: sql_num_fields
191 * Purpose: database specific num_fields function. Returns number
192 * of columns from query
194 *************************************************************************/
195 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
198 rlm_sql_oracle_conn_t *conn = handle->conn;
200 /* get the number of columns in the select list */
201 if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&count, NULL, OCI_ATTR_PARAM_COUNT,
203 ERROR("rlm_sql_oracle: Error retrieving column count : %s", sql_error(handle, config));
211 /*************************************************************************
213 * Function: sql_query
215 * Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
218 *************************************************************************/
219 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
222 rlm_sql_oracle_conn_t *conn = handle->conn;
224 OraText *oracle_query;
226 memcpy(&oracle_query, &query, sizeof(oracle_query));
229 ERROR("rlm_sql_oracle: Socket not connected");
231 return RLM_SQL_RECONNECT;
234 if (OCIStmtPrepare(conn->query, conn->error, oracle_query, strlen(query),
235 OCI_NTV_SYNTAX, OCI_DEFAULT)) {
236 ERROR("rlm_sql_oracle: prepare failed in sql_query: %s", sql_error(handle, config));
241 status = OCIStmtExecute(conn->ctx, conn->query, conn->error, 1, 0,
242 NULL, NULL, OCI_COMMIT_ON_SUCCESS);
244 if (status == OCI_SUCCESS) {
248 if (status == OCI_ERROR) {
249 ERROR("rlm_sql_oracle: execute query failed in sql_query: %s", sql_error(handle, config));
250 return sql_check_error(handle, config);
257 /*************************************************************************
259 * Function: sql_select_query
261 * Purpose: Issue a select query to the database
263 *************************************************************************/
264 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
279 OraText *oracle_query;
281 rlm_sql_oracle_conn_t *conn = handle->conn;
283 memcpy(&oracle_query, &query, sizeof(oracle_query));
285 if (OCIStmtPrepare(conn->query, conn->error, oracle_query, strlen(query), OCI_NTV_SYNTAX,
287 ERROR("rlm_sql_oracle: prepare failed in sql_select_query: %s", sql_error(handle, config));
293 * Retrieve a single row
295 status = OCIStmtExecute(conn->ctx, conn->query, conn->error, 0, 0, NULL, NULL, OCI_DEFAULT);
296 if (status == OCI_NO_DATA) {
300 if (status != OCI_SUCCESS) {
301 ERROR("rlm_sql_oracle: query failed in sql_select_query: %s", sql_error(handle, config));
303 return sql_check_error(handle, config);
307 * We only need to do this once per result set, because
308 * the number of columns won't change.
310 if (conn->col_count == 0) {
311 conn->col_count = sql_num_fields(handle, config);
313 if (conn->col_count == 0) {
318 MEM(row = talloc_zero_array(conn, char*, conn->col_count + 1));
319 MEM(ind = talloc_zero_array(row, sb2, conn->col_count + 1));
321 for (i = 0; i < conn->col_count; i++) {
322 status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)¶m, i + 1);
323 if (status != OCI_SUCCESS) {
324 ERROR("rlm_sql_oracle: OCIParamGet() failed in sql_select_query: %s",
325 sql_error(handle, config));
330 status = OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
332 if (status != OCI_SUCCESS) {
333 ERROR("rlm_sql_oracle: OCIAttrGet() failed in sql_select_query: %s",
334 sql_error(handle, config));
339 dsize = MAX_DATASTR_LEN;
342 * Use the retrieved length of dname to allocate an output buffer, and then define the output
343 * variable (but only for char/string type columns).
347 case SQLT_AFC: /* ansii fixed char */
350 case SQLT_AFV: /* ansii var char */
352 case SQLT_VCS: /* var char */
353 case SQLT_CHR: /* char */
354 case SQLT_STR: /* string */
355 status = OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
356 OCI_ATTR_DATA_SIZE, conn->error);
357 if (status != OCI_SUCCESS) {
358 ERROR("rlm_sql_oracle: OCIAttrGet() failed in sql_select_query: %s",
359 sql_error(handle, config));
364 MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
374 MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
386 * Grab the actual row value and write it to the buffer we allocated.
388 status = OCIDefineByPos(conn->query, &define, conn->error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
389 (dvoid *)&ind[i], NULL, NULL, OCI_DEFAULT);
391 if (status != OCI_SUCCESS) {
392 ERROR("rlm_sql_oracle: OCIDefineByPos() failed in sql_select_query: %s",
393 sql_error(handle, config));
411 /*************************************************************************
413 * Function: sql_store_result
415 * Purpose: database specific store_result function. Returns a result
418 *************************************************************************/
419 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle,UNUSED rlm_sql_config_t *config)
421 /* Not needed for Oracle */
426 /*************************************************************************
428 * Function: sql_num_rows
430 * Purpose: database specific num_rows. Returns number of rows in
433 *************************************************************************/
434 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
436 rlm_sql_oracle_conn_t *conn = handle->conn;
438 ub4 size = sizeof(ub4);
440 OCIAttrGet((CONST dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->error);
446 /*************************************************************************
448 * Function: sql_fetch_row
450 * Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
451 * with all the data for the query in 'handle->row'. Returns
452 * 0 on success, -1 on failure, RLM_SQL_RECONNECT if database is down.
454 *************************************************************************/
455 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
459 rlm_sql_oracle_conn_t *conn = handle->conn;
462 ERROR("rlm_sql_oracle: Socket not connected");
464 return RLM_SQL_RECONNECT;
469 status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
470 if (status == OCI_SUCCESS) {
471 handle->row = conn->row;
476 if (status == OCI_ERROR) {
477 ERROR("rlm_sql_oracle: fetch failed in sql_fetch_row: %s", sql_error(handle, config));
478 return sql_check_error(handle, config);
486 /*************************************************************************
488 * Function: sql_free_result
490 * Purpose: database specific free_result. Frees memory allocated
493 *************************************************************************/
494 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
496 rlm_sql_oracle_conn_t *conn = handle->conn;
498 /* Cancel the cursor first */
499 (void) OCIStmtFetch(conn->query, conn->error, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
501 TALLOC_FREE(conn->row);
502 conn->ind = NULL; /* ind is a child of row */
510 /*************************************************************************
512 * Function: sql_finish_query
514 * Purpose: End the query, such as freeing memory
516 *************************************************************************/
517 static sql_rcode_t sql_finish_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
524 /*************************************************************************
526 * Function: sql_finish_select_query
528 * Purpose: End the select query, such as freeing memory or result
530 *************************************************************************/
531 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
533 rlm_sql_oracle_conn_t *conn = handle->conn;
535 TALLOC_FREE(conn->row);
536 conn->ind = NULL; /* ind is a child of row */
543 /*************************************************************************
545 * Function: sql_affected_rows
547 * Purpose: Return the number of rows affected by the query (update,
550 *************************************************************************/
551 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
552 return sql_num_rows(handle, config);
556 /* Exported to rlm_sql */
557 rlm_sql_module_t rlm_sql_oracle = {
570 sql_finish_select_query,