23RCSID(
"$Id: d157225b289f1663a7b8789b9e94d794c69dd40a $")
25#define LOG_PREFIX "sql - oracle"
27#include <freeradius-devel/server/base.h>
28#include <freeradius-devel/util/debug.h>
42#if defined(__STDC__) && __STDC__
81#define MAX_DATASTR_LEN 64
100 OCIErrorGet((dvoid *) conn->
error, 1, (OraText *) NULL, &errcode, (OraText *)
out,
101 outlen, OCI_HTYPE_ERROR);
102 if (!errcode)
return -1;
127 if (ret < 0)
return 0;
130 out[0].msg = talloc_strdup(ctx, errbuff);
139 if (
inst->env) OCIHandleFree((dvoid *)
inst->env, OCI_HTYPE_ENV);
151 if (OCIEnvCreate(&
inst->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
152 ERROR(
"Couldn't init Oracle OCI environment (OCIEnvCreate())");
165 if (strstr(errbuff,
"ORA-03113") || strstr(errbuff,
"ORA-03114")) {
166 ERROR(
"OCI_SERVER_NOT_CONNECTED");
178 OCISessionEnd(conn->
ctx, conn->
error, conn->
sess, OCI_DEFAULT);
179 OCIHandleFree((dvoid *)conn->
sess, OCI_HTYPE_SESSION);
181 if (conn->
ctx) OCIHandleFree((dvoid *)conn->
ctx, OCI_HTYPE_SVCCTX);
183 OCIServerDetach(conn->
srv, conn->
error, OCI_DEFAULT);
184 OCIHandleFree((dvoid *)conn->
srv, OCI_HTYPE_SERVER);
186 if (conn->
error) OCIHandleFree((dvoid *)conn->
error, OCI_HTYPE_ERROR);
191#define ORACLE_ERROR(_message) \
192 sql_snprint_error(errbuff, sizeof(errbuff), c); \
193 ERROR(_message ": %s", errbuff); \
194 return CONNECTION_STATE_FAILED
203 OraText *sql_password = NULL;
204 OraText *sql_login = NULL;
210 .select_interval = 1000,
211 .query_interval = 1000,
224 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
error, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS) {
225 ERROR(
"Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
232 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
srv, (ub4)OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS) {
233 ERROR(
"Couldn't allocate Oracle SERVER handle");
243 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
ctx, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS) {
244 ERROR(
"Couldn't allocate Oracle SERVICE handle");
247 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->
srv, 0, OCI_ATTR_SERVER, c->
error) != OCI_SUCCESS) {
248 ORACLE_ERROR(
"Failed to link service and server handles");
252 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
sess, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS) {
253 ERROR(
"Couldn't allocate Oracle SESSION handle");
262 if (OCIAttrSet((dvoid *)c->
sess, OCI_HTYPE_SESSION, sql_login, strlen(c->
config->
sql_login),
263 OCI_ATTR_USERNAME, c->
error) != OCI_SUCCESS) {
267 OCI_ATTR_PASSWORD, c->
error) != OCI_SUCCESS) {
271 if (OCISessionBegin((dvoid *)c->
ctx, c->
error, c->
sess, OCI_CRED_RDBMS, OCI_STMT_CACHE) != OCI_SUCCESS) {
275 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->
sess, 0, OCI_ATTR_SESSION, c->
error) != OCI_SUCCESS) {
276 ORACLE_ERROR(
"Failed to link service and session handles");
279 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)&
inst->stmt_cache_size, 0,
280 OCI_ATTR_STMTCACHESIZE, c->
error) != OCI_SUCCESS) {
287 if (OCIAttrSet((dvoid *)c->
srv, OCI_HTYPE_SERVER, (dvoid *)0, 0, OCI_ATTR_NONBLOCKING_MODE, c->
error) != OCI_SUCCESS) {
289 WARN(
"Cound not set non-blocking mode: %s", errbuff);
315 switch(query_ctx->
status) {
318 if (OCIStmtPrepare2(sql_conn->
ctx, &sql_conn->
query, sql_conn->
error,
320 NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT)) {
322 ERROR(
"Failed to prepare query: %s", errbuff);
327 switch (query_ctx->
type) {
329 ret = OCIStmtExecute(sql_conn->
ctx, sql_conn->
query, sql_conn->
error, 0, 0, NULL, NULL, OCI_DEFAULT);
333 ret = OCIStmtExecute(sql_conn->
ctx, sql_conn->
query, sql_conn->
error, 1, 0, NULL, NULL,
334 OCI_COMMIT_ON_SUCCESS);
337 query_ctx->
tconn = tconn;
340 case OCI_STILL_EXECUTING:
364 ERROR(
"SQL query failed: %s", errbuff);
390 if (!query_ctx->
treq)
return;
414 status = OCIBreak(sql_conn->
ctx, sql_conn->
error);
416 case OCI_STILL_EXECUTING:
427 ERROR(
"Failed cancelling query: %s", errbuff);
430 OCIReset(sql_conn->
ctx, sql_conn->
error) ;
441 sword ret = OCI_SUCCESS;
443 switch (query_ctx->
status) {
445 switch (query_ctx->
type) {
447 ret = OCIStmtExecute(c->
ctx, c->
query, c->
error, 0, 0, NULL, NULL, OCI_DEFAULT);
451 ret = OCIStmtExecute(c->
ctx, c->
query, c->
error, 1, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS);
465 case OCI_STILL_EXECUTING:
470 ERROR(
"Unable to insert polling event");
505 if (ret == OCI_STILL_EXECUTING) {
509 ERROR(
"Unable to insert polling event");
555 ERROR(
"Unable to insert polling event");
565 ERROR(
"Unable to insert polling event");
592 if (OCIAttrGet((dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&conn->
col_count, NULL,
593 OCI_ATTR_PARAM_COUNT, conn->
error))
goto error;
598 MEM(row = talloc_zero_array(conn,
char*, conn->
col_count + 1));
602 if (OCIParamGet(conn->
query, OCI_HTYPE_STMT, conn->
error, (dvoid **)¶m, i + 1) != OCI_SUCCESS) {
603 ERROR(
"OCIParamGet() failed in sql_select_query");
607 if (OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
608 conn->
error) != OCI_SUCCESS) {
609 ERROR(
"OCIAttrGet() failed in sql_select_query");
629 if (OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
630 OCI_ATTR_DATA_SIZE, conn->
error) != OCI_SUCCESS) {
631 ERROR(
"OCIAttrGet() failed in sql_select_query");
643 MEM(row[i] = talloc_zero_array(row,
char, dsize + 1));
657 if (OCIDefineByPos(conn->
query, &
define, conn->
error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
658 (dvoid *)&
ind[i], NULL, NULL, OCI_DEFAULT) != OCI_SUCCESS) {
659 ERROR(
"OCIDefineByPos() failed in sql_select_query");
680 int fields, i, status;
684 if (OCIAttrGet((dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&fields, NULL, OCI_ATTR_PARAM_COUNT,
688 MEM(
names = talloc_array(query_ctx,
char const *, fields));
690 for (i = 0; i < fields; i++) {
691 OraText *pcol_name = NULL;
694 status = OCIParamGet(conn->
query, OCI_HTYPE_STMT, conn->
error, (dvoid **)¶m, i + 1);
695 if (status != OCI_SUCCESS) {
696 ERROR(
"OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
703 status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
704 OCI_ATTR_NAME, conn->
error);
705 if (status != OCI_SUCCESS) {
706 ERROR(
"OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
711 names[i] = (
char const *)pcol_name;
723 ub4 size =
sizeof(ub4);
725 OCIAttrGet((CONST dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->
error);
733 int status = OCI_STILL_EXECUTING;
737 ERROR(
"Socket not connected");
743 query_ctx->
row = NULL;
745 while (status == OCI_STILL_EXECUTING) {
746 status = OCIStmtFetch(conn->
query, conn->
error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
748 if (status == OCI_SUCCESS) {
749 query_ctx->
row = conn->
row;
755 if (status == OCI_NO_DATA) {
760 if (status == OCI_ERROR) {
761 ERROR(
"fetch failed in sql_fetch_row");
773 int status = OCI_STILL_EXECUTING;
776 while (status == OCI_STILL_EXECUTING) {
777 status = OCIStmtFetch(conn->
query, conn->
error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
780 TALLOC_FREE(conn->
row);
785 if (OCIStmtRelease(conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
786 ERROR(
"OCI release failed in sql_finish_query");
799 if (OCIStmtRelease(conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
800 ERROR(
"OCI release failed in sql_finish_query");
811 TALLOC_FREE(conn->
row);
816 if (OCIStmtRelease (conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
817 ERROR(
"OCI release failed in sql_finish_query");
828 .name =
"sql_oracle",
836 .sql_query_resume = sql_query_resume,
847 .connection_alloc = sql_trunk_connection_alloc,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define CC_NO_UBSAN(_sanitize)
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
conf_parser_flags_t flags
Flags which control parsing behaviour.
Defines a CONF_PAIR to C data type mapping.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define fr_event_timer_in(...)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Stores all information relating to an event list.
module_instance_t * mi
Module instance to detach.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for detach calls.
Temporary structure to hold arguments for instantiation calls.
static const conf_parser_t config[]
#define RETURN_MODULE_FAIL
rlm_rcode_t
Return codes indicating the result of the module call.
static int instantiate(module_inst_ctx_t const *mctx)
char const * sql_db
Database to run queries against.
fr_sql_query_status_t status
Status of the query.
trunk_connection_t * tconn
Trunk connection this query is being run on.
fr_sql_query_type_t type
Type of query.
char const * query_str
Query string to run.
request_t * request
Request this query relates to.
sql_rcode_t
Action to take at end of an SQL query.
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
@ RLM_SQL_ERROR
General connection/server error.
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
fr_time_delta_t query_timeout
How long to allow queries to run for.
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those resulting from a unique key violation.
char const * sql_login
Login credentials to use.
rlm_sql_row_t row
Row data from the last query.
sql_rcode_t rcode
Result code.
char const * sql_password
Login password to use.
trunk_request_t * treq
Trunk request for this query.
@ SQL_QUERY_CANCELLED
A cancellation has been sent to the server.
@ SQL_QUERY_RETURNED
Query has executed.
@ SQL_QUERY_FAILED
Failed to submit.
@ SQL_QUERY_SUBMITTED
Submitted for execution.
@ SQL_QUERY_PREPARED
Ready to submit.
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
rlm_sql_config_t const * config
SQL instance configuration.
int col_count
Number of columns associated with the result set.
OCISession * sess
Session handle.
rlm_sql_driver_t rlm_sql_oracle
static int mod_detach(module_detach_ctx_t const *mctx)
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCISvcCtx * ctx
Service handle.
rlm_sql_row_t row
Results row.
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIServer * srv
Server handle.
fr_sql_query_t * query_ctx
Current request running on the connection.
static SQL_TRUNK_CONNECTION_ALLOC void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
static void sql_trunk_connection_write_poll(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
uint32_t stmt_cache_size
Statement cache size.
static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
static sql_rcode_t sql_finish_select_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static int sql_snprint_error(char *out, size_t outlen, rlm_sql_oracle_conn_t *conn)
Write the last Oracle error out to a buffer.
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
static int sql_num_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
uint poll_count
How many polls have been done for the current query.
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static sql_rcode_t sql_check_reconnect(rlm_sql_oracle_conn_t *conn)
static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
OCIEnv * env
Environment handle.
OCIError * error
Error handle.
uint select_interval
How frequently this connection gets polled for select queries.
uint query_interval
How frequently this connection gets polled for other queries.
static void sql_trunk_connection_read_poll(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static const conf_parser_t driver_config[]
fr_event_timer_t const * read_ev
Timer event for polling reading this connection.
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
OCIStmt * query
Query handle.
#define ORACLE_ERROR(_message)
fr_event_timer_t const * write_ev
Timer event for polling writing this connection.
connection_t * conn
Generic connection structure for this connection.
static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
sb2 * ind
Indicators regarding contents of the results row.
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
size_t inst_size
Size of the module's instance data.
void * data
Module's instance data.
eap_aka_sim_process_conf_t * inst
module_t common
Common fields for all loadable modules.
module_instance_t * driver_submodule
Driver's submodule.
#define talloc_get_type_abort_const
static const char * names[8]
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Associates request queues with a connection.
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
trunk_cancel_reason_t
Reasons for a request being cancelled.
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
static fr_event_list_t * el
static size_t char ** out