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>
25 #include <freeradius-devel/rad_assert.h>
30 * There are typos in the Oracle Instaclient where the definition controlling prototype
31 * format is _STDC_ (not __STDC__).
33 * There are still cases where the oracle headers do not declare ANSI C function types
34 * but this at least cuts down the errors.
36 * -Wno-strict-prototypes does the rest.
38 DIAG_OFF(unused-macros)
39 #if defined(__STDC__) && __STDC__
44 DIAG_ON(unused-macros)
48 typedef struct rlm_sql_oracle_conn_t {
56 int col_count; //!< Number of columns associated with the result set
58 } rlm_sql_oracle_conn_t;
60 #define MAX_DATASTR_LEN 64
62 /** Write the last Oracle error out to a buffer
64 * @param out Where to write the error (should be at least 512 bytes).
65 * @param outlen The length of the error buffer.
66 * @param handle sql handle.
67 * @param config Instance config.
68 * @return 0 on success, -1 if there was no error.
70 static int sql_prints_error(char *out, size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
73 rlm_sql_oracle_conn_t *conn = handle->conn;
79 OCIErrorGet((dvoid *) conn->error, 1, (OraText *) NULL, &errcode, (OraText *) out,
80 outlen, OCI_HTYPE_ERROR);
81 if (!errcode) return -1;
86 /** Retrieves any errors associated with the connection handle
88 * @note Caller will free any memory allocated in ctx.
90 * @param ctx to allocate temporary error buffers in.
91 * @param out Array of sql_log_entrys to fill.
92 * @param outlen Length of out array.
93 * @param handle rlm_sql connection handle.
94 * @param config rlm_sql config.
95 * @return number of errors written to the sql_log_entry array.
97 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
98 rlm_sql_handle_t *handle, rlm_sql_config_t *config)
103 rad_assert(outlen > 0);
105 ret = sql_prints_error(errbuff, sizeof(errbuff), handle, config);
106 if (ret < 0) return 0;
109 out[0].msg = talloc_strdup(ctx, errbuff);
114 static int sql_check_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
118 if (sql_prints_error(errbuff, sizeof(errbuff), handle, config) < 0) goto unknown;
120 if (strstr(errbuff, "ORA-03113") || strstr(errbuff, "ORA-03114")) {
121 ERROR("rlm_sql_oracle: OCI_SERVER_NOT_CONNECTED");
122 return RLM_SQL_RECONNECT;
126 ERROR("rlm_sql_oracle: OCI_SERVER_NORMAL");
130 static int _sql_socket_destructor(rlm_sql_oracle_conn_t *conn)
132 if (conn->ctx) OCILogoff(conn->ctx, conn->error);
133 if (conn->query) OCIHandleFree((dvoid *)conn->query, OCI_HTYPE_STMT);
134 if (conn->error) OCIHandleFree((dvoid *)conn->error, OCI_HTYPE_ERROR);
135 if (conn->env) OCIHandleFree((dvoid *)conn->env, OCI_HTYPE_ENV);
140 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
144 rlm_sql_oracle_conn_t *conn;
146 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_oracle_conn_t));
147 talloc_set_destructor(conn, _sql_socket_destructor);
150 * Initialises the oracle environment
152 if (OCIEnvCreate(&conn->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
153 ERROR("rlm_sql_oracle: Couldn't init Oracle OCI environment (OCIEnvCreate())");
155 return RLM_SQL_ERROR;
159 * Allocates an error handle
161 if (OCIHandleAlloc((dvoid *)conn->env, (dvoid **)&conn->error, OCI_HTYPE_ERROR, 0, NULL)) {
162 ERROR("rlm_sql_oracle: Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
164 return RLM_SQL_ERROR;
168 * Allocate handles for select and update queries
170 if (OCIHandleAlloc((dvoid *)conn->env, (dvoid **)&conn->query, OCI_HTYPE_STMT, 0, NULL)) {
171 ERROR("rlm_sql_oracle: Couldn't init Oracle query handles: %s",
172 (sql_prints_error(errbuff, sizeof(errbuff), handle, config) == 0) ? errbuff : "unknown");
174 return RLM_SQL_ERROR;
178 * Login to the oracle server
180 if (OCILogon(conn->env, conn->error, &conn->ctx,
181 (OraText const *)config->sql_login, strlen(config->sql_login),
182 (OraText const *)config->sql_password, strlen(config->sql_password),
183 (OraText const *)config->sql_db, strlen(config->sql_db))) {
184 ERROR("rlm_sql_oracle: Oracle logon failed: '%s'",
185 (sql_prints_error(errbuff, sizeof(errbuff), handle, config) == 0) ? errbuff : "unknown");
187 return RLM_SQL_ERROR;
193 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
196 rlm_sql_oracle_conn_t *conn = handle->conn;
198 /* get the number of columns in the select list */
199 if (OCIAttrGet((dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&count, NULL, OCI_ATTR_PARAM_COUNT,
200 conn->error)) return -1;
205 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, rlm_sql_config_t *config)
207 rlm_sql_oracle_conn_t *conn = handle->conn;
208 int fields, i, status;
212 fields = sql_num_fields(handle, config);
213 if (fields <= 0) return RLM_SQL_ERROR;
215 MEM(names = talloc_array(handle, char const *, fields));
217 for (i = 0; i < fields; i++) {
218 OraText *pcol_name = NULL;
221 status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)¶m, i + 1);
222 if (status != OCI_SUCCESS) {
223 ERROR("rlm_sql_oracle: OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
227 return RLM_SQL_ERROR;
230 status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
231 OCI_ATTR_NAME, conn->error);
232 if (status != OCI_SUCCESS) {
233 ERROR("rlm_sql_oracle: OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
238 names[i] = (char const *)pcol_name;
246 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
249 rlm_sql_oracle_conn_t *conn = handle->conn;
251 OraText *oracle_query;
253 memcpy(&oracle_query, &query, sizeof(oracle_query));
256 ERROR("rlm_sql_oracle: Socket not connected");
258 return RLM_SQL_RECONNECT;
261 if (OCIStmtPrepare(conn->query, conn->error, oracle_query, strlen(query),
262 OCI_NTV_SYNTAX, OCI_DEFAULT)) {
263 ERROR("rlm_sql_oracle: prepare failed in sql_query");
265 return RLM_SQL_ERROR;
268 status = OCIStmtExecute(conn->ctx, conn->query, conn->error, 1, 0,
269 NULL, NULL, OCI_COMMIT_ON_SUCCESS);
271 if (status == OCI_SUCCESS) return RLM_SQL_OK;
272 if (status == OCI_ERROR) {
273 ERROR("rlm_sql_oracle: execute query failed in sql_query");
275 return sql_check_error(handle, config);
278 return RLM_SQL_ERROR;
281 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
295 OraText *oracle_query;
297 rlm_sql_oracle_conn_t *conn = handle->conn;
299 memcpy(&oracle_query, &query, sizeof(oracle_query));
301 if (OCIStmtPrepare(conn->query, conn->error, oracle_query, strlen(query), OCI_NTV_SYNTAX,
303 ERROR("rlm_sql_oracle: prepare failed in sql_select_query");
305 return RLM_SQL_ERROR;
309 * Retrieve a single row
311 status = OCIStmtExecute(conn->ctx, conn->query, conn->error, 0, 0, NULL, NULL, OCI_DEFAULT);
312 if (status == OCI_NO_DATA) return RLM_SQL_OK;
313 if (status != OCI_SUCCESS) {
314 ERROR("rlm_sql_oracle: query failed in sql_select_query");
316 return sql_check_error(handle, config);
320 * We only need to do this once per result set, because
321 * the number of columns won't change.
323 if (conn->col_count == 0) {
324 conn->col_count = sql_num_fields(handle, config);
326 if (conn->col_count == 0) return RLM_SQL_ERROR;
329 MEM(row = talloc_zero_array(conn, char*, conn->col_count + 1));
330 MEM(ind = talloc_zero_array(row, sb2, conn->col_count + 1));
332 for (i = 0; i < conn->col_count; i++) {
333 status = OCIParamGet(conn->query, OCI_HTYPE_STMT, conn->error, (dvoid **)¶m, i + 1);
334 if (status != OCI_SUCCESS) {
335 ERROR("rlm_sql_oracle: OCIParamGet() failed in sql_select_query");
340 status = OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
342 if (status != OCI_SUCCESS) {
343 ERROR("rlm_sql_oracle: OCIAttrGet() failed in sql_select_query");
348 dsize = MAX_DATASTR_LEN;
351 * Use the retrieved length of dname to allocate an output buffer, and then define the output
352 * variable (but only for char/string type columns).
356 case SQLT_AFC: /* ansii fixed char */
359 case SQLT_AFV: /* ansii var char */
361 case SQLT_VCS: /* var char */
362 case SQLT_CHR: /* char */
363 case SQLT_STR: /* string */
364 status = OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
365 OCI_ATTR_DATA_SIZE, conn->error);
366 if (status != OCI_SUCCESS) {
367 ERROR("rlm_sql_oracle: OCIAttrGet() failed in sql_select_query");
372 MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
382 MEM(row[i] = talloc_zero_array(row, char, dsize + 1));
394 * Grab the actual row value and write it to the buffer we allocated.
396 status = OCIDefineByPos(conn->query, &define, conn->error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
397 (dvoid *)&ind[i], NULL, NULL, OCI_DEFAULT);
399 if (status != OCI_SUCCESS) {
400 ERROR("rlm_sql_oracle: OCIDefineByPos() failed in sql_select_query");
413 return RLM_SQL_ERROR;
416 static int sql_num_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
418 rlm_sql_oracle_conn_t *conn = handle->conn;
420 ub4 size = sizeof(ub4);
422 OCIAttrGet((CONST dvoid *)conn->query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->error);
427 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
430 rlm_sql_oracle_conn_t *conn = handle->conn;
433 ERROR("rlm_sql_oracle: Socket not connected");
435 return RLM_SQL_RECONNECT;
440 status = OCIStmtFetch(conn->query, conn->error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
441 if (status == OCI_SUCCESS) {
442 handle->row = conn->row;
447 if (status == OCI_NO_DATA) {
453 if (status == OCI_ERROR) {
454 ERROR("rlm_sql_oracle: fetch failed in sql_fetch_row");
455 return sql_check_error(handle, config);
458 return RLM_SQL_ERROR;
461 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
463 rlm_sql_oracle_conn_t *conn = handle->conn;
465 /* Cancel the cursor first */
466 (void) OCIStmtFetch(conn->query, conn->error, 0, OCI_FETCH_NEXT, OCI_DEFAULT);
468 TALLOC_FREE(conn->row);
469 conn->ind = NULL; /* ind is a child of row */
475 static sql_rcode_t sql_finish_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
480 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
482 rlm_sql_oracle_conn_t *conn = handle->conn;
484 TALLOC_FREE(conn->row);
485 conn->ind = NULL; /* ind is a child of row */
491 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
493 return sql_num_rows(handle, config);
496 /* Exported to rlm_sql */
497 extern rlm_sql_module_t rlm_sql_oracle;
498 rlm_sql_module_t rlm_sql_oracle = {
499 .name = "rlm_sql_oracle",
500 .sql_socket_init = sql_socket_init,
501 .sql_query = sql_query,
502 .sql_select_query = sql_select_query,
503 .sql_num_fields = sql_num_fields,
504 .sql_num_rows = sql_num_rows,
505 .sql_affected_rows = sql_affected_rows,
506 .sql_fetch_row = sql_fetch_row,
507 .sql_fields = sql_fields,
508 .sql_free_result = sql_free_result,
509 .sql_error = sql_error,
510 .sql_finish_query = sql_finish_query,
511 .sql_finish_select_query = sql_finish_select_query