21 RCSID(
"$Id: 58c1a88c69245cb32a8a91bb416cf0dffaa62954 $")
24 #define LOG_PREFIX "sql - unixodbc"
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/util/debug.h>
70 SQLINTEGER errornum = 0;
71 SQLSMALLINT length = 255;
74 if (SQL_SUCCEEDED(ret))
return 0;
76 error[0] = state[0] =
'\0';
78 SQLGetDiagRec(handle_type, handle, 1, state, &errornum, error,
sizeof(error), &length);
80 if (state[0] ==
'0') {
84 INFO(
"%s %s", state, error);
92 ERROR(
"SQL down %s %s", state, error);
98 ERROR(
"%s %s", state, error);
103 if (strcmp((
char const *)state,
"23000") == 0) {
106 ERROR(
"%s %s", state, error);
120 if (c->
stmt) SQLFreeHandle(SQL_HANDLE_STMT, c->
stmt);
123 SQLDisconnect(c->
dbc);
124 SQLFreeHandle(SQL_HANDLE_DBC, c->
dbc);
127 if (c->
env) SQLFreeHandle(SQL_HANDLE_ENV, c->
env);
133 char buff[256], verbuf[10];
137 SQLGetInfo(c->
dbc, SQL_DRIVER_NAME,
buff,
sizeof(
buff), NULL);
138 SQLGetInfo(c->
dbc, SQL_DRIVER_ODBC_VER, verbuf,
sizeof(verbuf), NULL);
142 DEBUG2(
"Using driver %s, ODBC version %s. Driver does not support async operations",
buff, verbuf);
144 case SQL_AM_CONNECTION:
145 DEBUG2(
"Using driver %s, ODBC version %s. Async operation is set per connection",
buff, verbuf);
146 ret = SQLSetConnectAttr(c->
dbc, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_UINTEGER);
149 case SQL_AM_STATEMENT:
150 DEBUG2(
"Using driver %s, ODBC version %s. Async operation is set per statement",
buff, verbuf);
155 ret = SQLAllocHandle(SQL_HANDLE_STMT, c->
dbc, &c->
stmt);
157 ERROR(
"Can't allocate the stmt");
162 ret = SQLSetStmtAttr(c->
stmt, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, 0);
167 SQLSetStmtAttr(c->
stmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)
timeout, SQL_IS_UINTEGER);
177 ret = SQLConnect(c->
dbc,
182 if (ret == SQL_STILL_EXECUTING) {
185 ERROR(
"Unable to insert polling event");
192 ERROR(
"Connection failed");
212 .select_interval = 1000,
213 .query_interval = 1000,
217 ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &c->
env);
218 if (ret == SQL_ERROR) {
219 ERROR(
"Can't allocate environment handle");
225 ret = SQLSetEnvAttr(c->
env, SQL_ATTR_ODBC_VERSION, (
void*)SQL_OV_ODBC3_80, 0);
227 ERROR(
"Can't register ODBC version");
232 ret = SQLAllocHandle(SQL_HANDLE_DBC, c->
env, &c->
dbc);
234 ERROR(
"Can't allocate connection handle");
245 SQLSetConnectAttr(c->
dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)
timeout, SQL_IS_UINTEGER);
246 SQLSetConnectAttr(c->
dbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)
timeout, SQL_IS_UINTEGER);
249 SQLSetConnectAttr(c->
dbc, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_UINTEGER);
252 ret = SQLConnect(c->
dbc,
257 if (ret == SQL_STILL_EXECUTING) {
260 ERROR(
"Unable to insert polling event");
268 ERROR(
"Connection failed");
293 switch(query_ctx->
status) {
297 query_ctx->
tconn = tconn;
299 if (ret == SQL_STILL_EXECUTING) {
309 switch(query_ctx->
rcode) {
339 if (!query_ctx->
treq)
return;
357 ret = SQLCancel(sql_conn->
stmt);
359 if (ret == SQL_STILL_EXECUTING) {
374 switch (query_ctx->
status) {
386 if (ret == SQL_STILL_EXECUTING) {
391 ERROR(
"Unable to insert polling event");
397 switch(query_ctx->
rcode) {
419 ret = SQLCancel(c->
stmt);
420 if (ret == SQL_STILL_EXECUTING) {
424 ERROR(
"Unable to insert polling event");
469 ERROR(
"Unable to insert polling event");
479 ERROR(
"Unable to insert polling event");
494 SQLRETURN ret = SQL_STILL_EXECUTING;
498 while (ret == SQL_STILL_EXECUTING) {
507 conn->
row = talloc_zero_array(conn,
char *, conn->
colcount + 1);
508 conn->
ind = talloc_zero_array(conn, SQLLEN, conn->
colcount);
510 for (i = 1; i <= conn->
colcount; i++) {
513 while (SQLColAttribute(conn->
stmt, (SQLUSMALLINT) i, SQL_DESC_LENGTH, NULL, 0, NULL, &len) == SQL_STILL_EXECUTING);
514 conn->
row[i - 1] = talloc_array(conn->
row,
char, ++len);
515 SQLBindCol(conn->
stmt, i, SQL_C_CHAR, (SQLCHAR *)conn->
row[i - 1], len, &conn->
ind[i - 1]);
533 for (i = 0; i < conn->
colcount; i++) {
535 ret = SQL_STILL_EXECUTING;
536 while (ret == SQL_STILL_EXECUTING) {
537 ret = SQLColAttribute(conn->
stmt, i + 1, SQL_DESC_NAME,
538 field,
sizeof(field), &len, NULL);
541 case SQL_INVALID_HANDLE:
543 ERROR(
"Failed retrieving field name at index %i", i);
552 MEM(p = talloc_array(
names,
char, (
size_t)len + 1));
553 strlcpy(p, field, (
size_t)len + 1);
565 SQLRETURN ret = SQL_STILL_EXECUTING;
568 query_ctx->
row = NULL;
570 while (ret == SQL_STILL_EXECUTING) {
571 ret = SQLFetch(conn->
stmt);
573 if (ret == SQL_NO_DATA_FOUND) {
584 for (i = 0; i < conn->
colcount; i++) {
585 if (conn->
ind[i] == SQL_NULL_DATA) conn->
row[i] = NULL;
588 query_ctx->
row = conn->
row;
598 TALLOC_FREE(conn->
row);
599 TALLOC_FREE(conn->
ind);
612 if (query_ctx->
treq && !(query_ctx->
treq->state &
622 TALLOC_FREE(conn->
row);
638 SQLFreeStmt(conn->
stmt, SQL_CLOSE);
659 SQLCHAR errbuff[256];
660 SQLINTEGER errnum = 0;
661 SQLSMALLINT length = 255;
670 errbuff[0] = state[0] =
'\0';
671 SQLGetDiagRec(SQL_HANDLE_ENV, conn->
env, 1, state, &errnum, errbuff,
sizeof(errbuff), &length);
677 if (conn->
dbc == SQL_NULL_HANDLE)
return i;
679 SQLGetDiagRec(SQL_HANDLE_DBC, conn->
dbc, 1, state, &errnum, errbuff,
sizeof(errbuff), &length);
685 if (conn->
stmt == SQL_NULL_HANDLE)
return i;
687 SQLGetDiagRec(SQL_HANDLE_STMT, conn->
stmt, 1, state, &errnum, errbuff,
sizeof(errbuff), &length);
723 .name =
"sql_unixodbc"
726 .sql_query_resume = sql_query_resume,
737 .connection_alloc = sql_trunk_connection_alloc,
738 .connection_notify = sql_trunk_connection_notify,
739 .request_mux = sql_trunk_request_mux,
740 .request_cancel_mux = sql_request_cancel_mux,
741 .request_cancel = sql_request_cancel,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define USES_APPLE_DEPRECATED_API
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
fr_time_delta_t connection_timeout
How long to wait for the connection to open or for shutdown to close the connection.
fr_dcursor_eval_t void const * uctx
static fr_time_delta_t timeout
#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.
static const conf_parser_t config[]
rlm_rcode_t
Return codes indicating the result of the module call.
Prototypes and functions for the SQL module.
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.
char const * sql_server
Server to connect to.
#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.
trunk_conf_t trunk_conf
Configuration for trunk connections.
@ 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)
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
fr_event_timer_t const * write_ev
rlm_sql_driver_t rlm_sql_unixodbc
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static sql_rcode_t sql_finish_query(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
fr_event_timer_t const * read_ev
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)
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static USES_APPLE_DEPRECATED_API sql_rcode_t sql_check_error(SQLRETURN ret, SQLSMALLINT handle_type, SQLHANDLE handle)
Checks the error code to determine if the connection needs to be re-esttablished.
fr_sql_query_t * query_ctx
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
static void sql_trunk_connection_read_poll(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static connection_state_t sql_trunk_connection_init_stmt(rlm_sql_unixodbc_conn_t *c)
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
rlm_sql_config_t const * config
static void sql_trunk_connection_init_poll(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
static char buff[sizeof("18446744073709551615")+3]
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
size_t strlcpy(char *dst, char const *src, size_t siz)
module_t common
Common fields for all loadable modules.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
#define talloc_get_type_abort_const
static const char * names[8]
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
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.
connection_conf_t const * conn_conf
Connection configuration.
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.
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
static fr_event_list_t * el
static size_t char ** out