23 RCSID(
"$Id: 228652d3d84af6a1dcf5f897a7f1228ed7d38b8a $")
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;
128 if (ret < 0)
return 0;
131 out[0].msg = talloc_strdup(ctx, errbuff);
140 if (
inst->env) OCIHandleFree((dvoid *)
inst->env, OCI_HTYPE_ENV);
152 if (OCIEnvCreate(&
inst->env, OCI_DEFAULT | OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL)) {
153 ERROR(
"Couldn't init Oracle OCI environment (OCIEnvCreate())");
166 if (strstr(errbuff,
"ORA-03113") || strstr(errbuff,
"ORA-03114")) {
167 ERROR(
"OCI_SERVER_NOT_CONNECTED");
179 OCISessionEnd(conn->
ctx, conn->
error, conn->
sess, OCI_DEFAULT);
180 OCIHandleFree((dvoid *)conn->
sess, OCI_HTYPE_SESSION);
182 if (conn->
ctx) OCIHandleFree((dvoid *)conn->
ctx, OCI_HTYPE_SVCCTX);
184 OCIServerDetach(conn->
srv, conn->
error, OCI_DEFAULT);
185 OCIHandleFree((dvoid *)conn->
srv, OCI_HTYPE_SERVER);
187 if (conn->
error) OCIHandleFree((dvoid *)conn->
error, OCI_HTYPE_ERROR);
192 #define ORACLE_ERROR(_message) \
193 sql_snprint_error(errbuff, sizeof(errbuff), c); \
194 ERROR(_message ": %s", errbuff); \
195 return CONNECTION_STATE_FAILED
204 OraText *sql_password = NULL;
205 OraText *sql_login = NULL;
211 .select_interval = 1000,
212 .query_interval = 1000,
225 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
error, OCI_HTYPE_ERROR, 0, NULL) != OCI_SUCCESS) {
226 ERROR(
"Couldn't init Oracle ERROR handle (OCIHandleAlloc())");
233 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
srv, (ub4)OCI_HTYPE_SERVER, 0, NULL) != OCI_SUCCESS) {
234 ERROR(
"Couldn't allocate Oracle SERVER handle");
244 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
ctx, OCI_HTYPE_SVCCTX, 0, NULL) != OCI_SUCCESS) {
245 ERROR(
"Couldn't allocate Oracle SERVICE handle");
248 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->
srv, 0, OCI_ATTR_SERVER, c->
error) != OCI_SUCCESS) {
249 ORACLE_ERROR(
"Failed to link service and server handles");
253 if (OCIHandleAlloc((dvoid *)
inst->env, (dvoid **)&c->
sess, OCI_HTYPE_SESSION, 0, NULL) != OCI_SUCCESS) {
254 ERROR(
"Couldn't allocate Oracle SESSION handle");
263 if (OCIAttrSet((dvoid *)c->
sess, OCI_HTYPE_SESSION, sql_login, strlen(c->
config->
sql_login),
264 OCI_ATTR_USERNAME, c->
error) != OCI_SUCCESS) {
268 OCI_ATTR_PASSWORD, c->
error) != OCI_SUCCESS) {
272 if (OCISessionBegin((dvoid *)c->
ctx, c->
error, c->
sess, OCI_CRED_RDBMS, OCI_STMT_CACHE) != OCI_SUCCESS) {
276 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)c->
sess, 0, OCI_ATTR_SESSION, c->
error) != OCI_SUCCESS) {
277 ORACLE_ERROR(
"Failed to link service and session handles");
280 if (OCIAttrSet((dvoid *)c->
ctx, OCI_HTYPE_SVCCTX, (dvoid *)&
inst->stmt_cache_size, 0,
281 OCI_ATTR_STMTCACHESIZE, c->
error) != OCI_SUCCESS) {
288 if (OCIAttrSet((dvoid *)c->
srv, OCI_HTYPE_SERVER, (dvoid *)0, 0, OCI_ATTR_NONBLOCKING_MODE, c->
error) != OCI_SUCCESS) {
290 WARN(
"Cound not set non-blocking mode: %s", errbuff);
316 switch(query_ctx->
status) {
319 if (OCIStmtPrepare2(sql_conn->
ctx, &sql_conn->
query, sql_conn->
error,
321 NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT)) {
323 ERROR(
"Failed to prepare query: %s", errbuff);
328 switch (query_ctx->
type) {
330 ret = OCIStmtExecute(sql_conn->
ctx, sql_conn->
query, sql_conn->
error, 0, 0, NULL, NULL, OCI_DEFAULT);
334 ret = OCIStmtExecute(sql_conn->
ctx, sql_conn->
query, sql_conn->
error, 1, 0, NULL, NULL,
335 OCI_COMMIT_ON_SUCCESS);
338 query_ctx->
tconn = tconn;
341 case OCI_STILL_EXECUTING:
365 ERROR(
"SQL query failed: %s", errbuff);
391 if (!query_ctx->
treq)
return;
415 status = OCIBreak(sql_conn->
ctx, sql_conn->
error);
417 case OCI_STILL_EXECUTING:
428 ERROR(
"Failed cancelling query: %s", errbuff);
431 OCIReset(sql_conn->
ctx, sql_conn->
error) ;
442 sword ret = OCI_SUCCESS;
444 switch (query_ctx->
status) {
446 switch (query_ctx->
type) {
448 ret = OCIStmtExecute(c->
ctx, c->
query, c->
error, 0, 0, NULL, NULL, OCI_DEFAULT);
452 ret = OCIStmtExecute(c->
ctx, c->
query, c->
error, 1, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS);
466 case OCI_STILL_EXECUTING:
471 ERROR(
"Unable to insert polling event");
506 if (ret == OCI_STILL_EXECUTING) {
510 ERROR(
"Unable to insert polling event");
556 ERROR(
"Unable to insert polling event");
566 ERROR(
"Unable to insert polling event");
593 if (OCIAttrGet((dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&conn->
col_count, NULL,
594 OCI_ATTR_PARAM_COUNT, conn->
error))
goto error;
599 MEM(row = talloc_zero_array(conn,
char*, conn->
col_count + 1));
603 if (OCIParamGet(conn->
query, OCI_HTYPE_STMT, conn->
error, (dvoid **)¶m, i + 1) != OCI_SUCCESS) {
604 ERROR(
"OCIParamGet() failed in sql_select_query");
608 if (OCIAttrGet((dvoid*)param, OCI_DTYPE_PARAM, (dvoid*)&dtype, NULL, OCI_ATTR_DATA_TYPE,
609 conn->
error) != OCI_SUCCESS) {
610 ERROR(
"OCIAttrGet() failed in sql_select_query");
630 if (OCIAttrGet((dvoid *)param, OCI_DTYPE_PARAM, (dvoid *)&dsize, NULL,
631 OCI_ATTR_DATA_SIZE, conn->
error) != OCI_SUCCESS) {
632 ERROR(
"OCIAttrGet() failed in sql_select_query");
644 MEM(row[i] = talloc_zero_array(row,
char, dsize + 1));
658 if (OCIDefineByPos(conn->
query, &
define, conn->
error, i + 1, (ub1 *)row[i], dsize + 1, SQLT_STR,
659 (dvoid *)&
ind[i], NULL, NULL, OCI_DEFAULT) != OCI_SUCCESS) {
660 ERROR(
"OCIDefineByPos() failed in sql_select_query");
681 int fields, i, status;
685 if (OCIAttrGet((dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&fields, NULL, OCI_ATTR_PARAM_COUNT,
689 MEM(
names = talloc_array(query_ctx,
char const *, fields));
691 for (i = 0; i < fields; i++) {
692 OraText *pcol_name = NULL;
695 status = OCIParamGet(conn->
query, OCI_HTYPE_STMT, conn->
error, (dvoid **)¶m, i + 1);
696 if (status != OCI_SUCCESS) {
697 ERROR(
"OCIParamGet(OCI_HTYPE_STMT) failed in sql_fields()");
704 status = OCIAttrGet((dvoid **)param, OCI_DTYPE_PARAM, &pcol_name, &pcol_size,
705 OCI_ATTR_NAME, conn->
error);
706 if (status != OCI_SUCCESS) {
707 ERROR(
"OCIParamGet(OCI_ATTR_NAME) failed in sql_fields()");
712 names[i] = (
char const *)pcol_name;
724 ub4 size =
sizeof(ub4);
726 OCIAttrGet((CONST dvoid *)conn->
query, OCI_HTYPE_STMT, (dvoid *)&rows, &size, OCI_ATTR_ROW_COUNT, conn->
error);
734 int status = OCI_STILL_EXECUTING;
738 ERROR(
"Socket not connected");
744 query_ctx->
row = NULL;
746 while (status == OCI_STILL_EXECUTING) {
747 status = OCIStmtFetch(conn->
query, conn->
error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
749 if (status == OCI_SUCCESS) {
750 query_ctx->
row = conn->
row;
756 if (status == OCI_NO_DATA) {
761 if (status == OCI_ERROR) {
762 ERROR(
"fetch failed in sql_fetch_row");
774 int status = OCI_STILL_EXECUTING;
777 while (status == OCI_STILL_EXECUTING) {
778 status = OCIStmtFetch(conn->
query, conn->
error, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
781 TALLOC_FREE(conn->
row);
786 if (OCIStmtRelease(conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
787 ERROR(
"OCI release failed in sql_finish_query");
800 if (OCIStmtRelease(conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
801 ERROR(
"OCI release failed in sql_finish_query");
812 TALLOC_FREE(conn->
row);
817 if (OCIStmtRelease (conn->
query, conn->
error, NULL, 0, OCI_DEFAULT) != OCI_SUCCESS ) {
818 ERROR(
"OCI release failed in sql_finish_query");
829 .name =
"sql_oracle",
837 .sql_query_resume = sql_query_resume,
849 .connection_alloc = sql_trunk_connection_alloc,
850 .connection_notify = sql_trunk_connection_notify,
851 .request_mux = sql_trunk_request_mux,
852 .request_cancel_mux = sql_request_cancel_mux,
853 .request_cancel = sql_request_cancel,
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 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
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.
fr_dcursor_eval_t void const * uctx
#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[]
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.
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 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 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 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)
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 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.
void * data
Module's instance data.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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