The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_sql_unixodbc.c
Go to the documentation of this file.
1/*
2 * sql_unixodbc.c unixODBC rlm_sql driver
3 *
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.
8 *
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.
13 *
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
17 *
18 * @copyright 2000,2006 The FreeRADIUS server project
19 * @copyright 2000 Dmitri Ageev (d_ageev@ortcc.ru)
20 */
21RCSID("$Id: dca8006a55390d3463346974a3ec41adba12b4d8 $")
23
24#define LOG_PREFIX "sql - unixodbc"
25
26#include <freeradius-devel/server/base.h>
27#include <freeradius-devel/util/debug.h>
28
29#include <sqltypes.h>
30#include "rlm_sql.h"
31#include "rlm_sql_trunk.h"
32
33typedef struct {
34 SQLHENV env; /* Environment handle */
35 SQLHDBC dbc; /* Database connection handle */
36 SQLHSTMT stmt; /* Statement handle */
37 SQLSMALLINT colcount; /* Number of columns in last result */
38 rlm_sql_row_t row; /* Results row */
39 SQLLEN *ind; /* Data length / NULL indicators */
40 connection_t *conn; /* Generic connection structure for this connection */
41 rlm_sql_config_t const *config; /* SQL instance configuration */
42 SQLUSMALLINT async_mode; /* What Async mode does this driver support */
43 fr_sql_query_t *query_ctx; /* Current query running on the connection */
44 fr_event_timer_t const *read_ev; /* Timer event for polling reading this connection */
45 fr_event_timer_t const *write_ev; /* Timer event for polling writing this connection */
46 uint select_interval; /* How frequently this connection gets polled for select queries */
47 uint query_interval; /* How frequently this connection gets polled for other queries */
48 uint poll_count; /* How many polls have been done for the current query */
50
52#include <sql.h>
53#include <sqlext.h>
54
55/** Checks the error code to determine if the connection needs to be re-esttablished
56 *
57 * @param ret Return code from a failed unixodbc call.
58 * @param handle_type Type of ODBC handle
59 * @param handle ODBC handle
60 * @return
61 * - #RLM_SQL_OK on success.
62 * - #RLM_SQL_ALT_QUERY if alternate queries should be tried.
63 * - #RLM_SQL_RECONNECT if reconnect is needed.
64 * - #RLM_SQL_ERROR on error.
65 */
66static sql_rcode_t sql_check_error(SQLRETURN ret, SQLSMALLINT handle_type, SQLHANDLE handle)
67{
68 SQLCHAR state[6];
69 SQLCHAR error[256];
70 SQLINTEGER errornum = 0;
71 SQLSMALLINT length = 255;
72 int res = RLM_SQL_ERROR;
73
74 if (SQL_SUCCEEDED(ret)) return 0; /* on success, just return 0 */
75
76 error[0] = state[0] = '\0';
77
78 SQLGetDiagRec(handle_type, handle, 1, state, &errornum, error, sizeof(error), &length);
79
80 if (state[0] == '0') {
81 switch (state[1]) {
82 /* SQLSTATE 01 class contains info and warning messages */
83 case '1':
84 INFO("%s %s", state, error);
86 case '0': /* SQLSTATE 00 class means success */
87 res = RLM_SQL_OK;
88 break;
89
90 /* SQLSTATE 08 class describes various connection errors */
91 case '8':
92 ERROR("SQL down %s %s", state, error);
94 break;
95
96 /* any other SQLSTATE means error */
97 default:
98 ERROR("%s %s", state, error);
99 break;
100 }
101 } else {
102 /* SQLSTATE 23000 is "Integrity constraint violation" - such as duplicate key */
103 if (strcmp((char const *)state, "23000") == 0) {
104 res = RLM_SQL_ALT_QUERY;
105 } else {
106 ERROR("%s %s", state, error);
107 }
108 }
109
110 return res;
111}
112
113static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
114{
115 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(h, rlm_sql_unixodbc_conn_t);
116
119
120 if (c->stmt) SQLFreeHandle(SQL_HANDLE_STMT, c->stmt);
121
122 if (c->dbc) {
123 SQLDisconnect(c->dbc);
124 SQLFreeHandle(SQL_HANDLE_DBC, c->dbc);
125 }
126
127 if (c->env) SQLFreeHandle(SQL_HANDLE_ENV, c->env);
128 talloc_free(h);
129}
130
132{
133 char buff[256], verbuf[10];
134 SQLRETURN ret;
135 SQLULEN timeout;
136
137 SQLGetInfo(c->dbc, SQL_DRIVER_NAME, buff, sizeof(buff), NULL);
138 SQLGetInfo(c->dbc, SQL_DRIVER_ODBC_VER, verbuf, sizeof(verbuf), NULL);
139 SQLGetInfo(c->dbc, SQL_ASYNC_MODE, &c->async_mode, 0, NULL);
140 switch(c->async_mode) {
141 case SQL_AM_NONE:
142 DEBUG2("Using driver %s, ODBC version %s. Driver does not support async operations", buff, verbuf);
143 break;
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);
147 sql_check_error(ret, SQL_HANDLE_DBC, c->dbc);
148 break;
149 case SQL_AM_STATEMENT:
150 DEBUG2("Using driver %s, ODBC version %s. Async operation is set per statement", buff, verbuf);
151 break;
152 }
153
154 /* Allocate the stmt handle */
155 ret = SQLAllocHandle(SQL_HANDLE_STMT, c->dbc, &c->stmt);
156 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
157 ERROR("Can't allocate the stmt");
158 _sql_connection_close(NULL, c, NULL);
160 }
161 if (c->async_mode == SQL_AM_STATEMENT) {
162 ret = SQLSetStmtAttr(c->stmt, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, 0);
163 sql_check_error(ret, SQL_HANDLE_STMT, c->stmt);
164 }
165
167 SQLSetStmtAttr(c->stmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)timeout, SQL_IS_UINTEGER);
168
170}
171
173{
174 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_unixodbc_conn_t);
175 SQLRETURN ret;
176
177 ret = SQLConnect(c->dbc,
178 UNCONST(SQLCHAR *, c->config->sql_server), strlen(c->config->sql_server),
179 UNCONST(SQLCHAR *, c->config->sql_login), strlen(c->config->sql_login),
180 UNCONST(SQLCHAR *, c->config->sql_password), strlen(c->config->sql_password));
181
182 if (ret == SQL_STILL_EXECUTING) {
185 ERROR("Unable to insert polling event");
187 }
188 return;
189 }
190
191 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
192 ERROR("Connection failed");
194 }
195
197}
198
199CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
200static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
201{
203 rlm_sql_config_t const *config = &sql->config;
205 SQLRETURN ret;
207
208 MEM(c = talloc_zero(conn, rlm_sql_unixodbc_conn_t));
210 .conn = conn,
211 .config = config,
212 .select_interval = 1000, /* Default starting poll interval - 1ms*/
213 .query_interval = 1000,
214 };
215
216 /* Allocate environment handle and register version */
217 ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &c->env);
218 if (ret == SQL_ERROR) {
219 ERROR("Can't allocate environment handle");
220 error:
221 _sql_connection_close(NULL, c, NULL);
223 }
224
225 ret = SQLSetEnvAttr(c->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3_80, 0);
226 if (sql_check_error(ret, SQL_HANDLE_ENV, c->env)) {
227 ERROR("Can't register ODBC version");
228 goto error;
229 }
230
231 /* Allocate connection handle */
232 ret = SQLAllocHandle(SQL_HANDLE_DBC, c->env, &c->dbc);
233 if (sql_check_error(ret, SQL_HANDLE_ENV, c->env)) {
234 ERROR("Can't allocate connection handle");
235 goto error;
236 }
237
238 /*
239 * Set the login / connection timeout
240 *
241 * Note SQLSetConnectionAttr and SQLSetStmtAttr have an insane parameter passing
242 * model. The 3rd parameter can be an integer, or a pointer to a string
243 * so integers get cast to pointers to match the function signature.
244 */
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);
247
248 /* Set the connection handle to Async */
249 SQLSetConnectAttr(c->dbc, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_UINTEGER);
250
251 /* Connect to the datasource */
252 ret = SQLConnect(c->dbc,
253 UNCONST(SQLCHAR *, config->sql_server), strlen(config->sql_server),
254 UNCONST(SQLCHAR *, config->sql_login), strlen(config->sql_login),
255 UNCONST(SQLCHAR *, config->sql_password), strlen(config->sql_password));
256
257 if (ret == SQL_STILL_EXECUTING) {
260 ERROR("Unable to insert polling event");
261 goto error;
262 }
263 *h = c;
265 }
266
267 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
268 ERROR("Connection failed");
269 goto error;
270 }
271
272 *h = c;
274}
275
277
278CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
280{
281 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
282 request_t *request;
283 trunk_request_t *treq;
284 fr_sql_query_t *query_ctx;
285 SQLRETURN ret;
286
287 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
288 if (!treq) return;
289
290 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
291 request = query_ctx->request;
292
293 switch(query_ctx->status) {
295 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
296 ret = SQLExecDirect(sql_conn->stmt, UNCONST(SQLCHAR *, query_ctx->query_str), strlen(query_ctx->query_str));
297 query_ctx->tconn = tconn;
298
299 if (ret == SQL_STILL_EXECUTING) {
300 ROPTIONAL(RDEBUG3, DEBUG3, "Awaiting response");
301 query_ctx->status = SQL_QUERY_SUBMITTED;
302 sql_conn->query_ctx = query_ctx;
303 sql_conn->poll_count = 0;
305 return;
306 }
307
308 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, sql_conn->stmt);
309 switch(query_ctx->rcode) {
310 case RLM_SQL_OK:
312 break;
313
314 default:
315 query_ctx->status = SQL_QUERY_FAILED;
318 return;
319 }
320 query_ctx->status = SQL_QUERY_RETURNED;
321 break;
322
323 default:
324 return;
325 }
326
327 ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
329 if (request) unlang_interpret_mark_runnable(request);
330}
331
332CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
333static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
334 UNUSED void *uctx)
335{
336 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
337 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
338
339 if (!query_ctx->treq) return;
340 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
341 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
342}
343
344CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
346 connection_t *conn, UNUSED void *uctx)
347{
348 trunk_request_t *treq;
349 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
350 SQLRETURN ret;
351 fr_sql_query_t *query_ctx;
352
353 if ((trunk_connection_pop_cancellation(&treq, tconn)) != 0) return;
354 if (!treq) return;
355
356 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
357 ret = SQLCancel(sql_conn->stmt);
358 query_ctx->status = SQL_QUERY_CANCELLED;
359 if (ret == SQL_STILL_EXECUTING) {
361 return;
362 }
364}
365
367{
368 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_unixodbc_conn_t);
369 fr_sql_query_t *query_ctx = c->query_ctx;
370 SQLRETURN ret;
371 trunk_request_t *treq = query_ctx->treq;
372 request_t *request = query_ctx->request;
373
374 switch (query_ctx->status) {
376 ret = SQLExecDirect(c->stmt, UNCONST(SQLCHAR *, query_ctx->query_str), strlen(query_ctx->query_str));
377 c->poll_count++;
378 /* Back off the poll interval, up to half the query timeout */
379 if (c->poll_count > 2) {
380 if (query_ctx->type == SQL_QUERY_SELECT) {
382 } else {
384 }
385 }
386 if (ret == SQL_STILL_EXECUTING) {
387 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
388 if (fr_event_timer_in(c, el, &c->read_ev,
391 ERROR("Unable to insert polling event");
392 }
393 return;
394 }
395
396 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, c->stmt);
397 switch(query_ctx->rcode) {
398 case RLM_SQL_OK:
400 /* If we only polled once, reduce the interval*/
401 if (c->poll_count == 1) {
402 if (query_ctx->type == SQL_QUERY_SELECT) {
403 c->select_interval /= 2;
404 } else {
405 c->query_interval /= 2;
406 }
407 }
408 break;
409
410 default:
411 query_ctx->status = SQL_QUERY_FAILED;
414 return;
415 }
416 break;
417
419 ret = SQLCancel(c->stmt);
420 if (ret == SQL_STILL_EXECUTING) {
421 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
424 ERROR("Unable to insert polling event");
425 }
426 return;
427 }
429 return;
430
431 default:
432 return;
433 }
434
435 if (request) unlang_interpret_mark_runnable(request);
436}
437
439{
440 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
441
443}
444
445/*
446 * UnixODBC doesn't support event driven async, so in this case
447 * we have to resort to polling.
448 *
449 * This "notify" callback sets up the appropriate polling events.
450 */
451CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
453 trunk_connection_event_t notify_on, UNUSED void *uctx)
454{
455 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
456 fr_sql_query_t *query_ctx = c->query_ctx;
457 uint poll_interval = (query_ctx && query_ctx->type != SQL_QUERY_SELECT) ? c->query_interval : c->select_interval;
458 switch (notify_on) {
462 return;
463
466 if (c->query_ctx) {
467 if (fr_event_timer_in(c, el, &c->read_ev, fr_time_delta_from_usec(poll_interval),
469 ERROR("Unable to insert polling event");
470 }
471 }
472 if (notify_on == TRUNK_CONN_EVENT_READ) return;
473
475
479 ERROR("Unable to insert polling event");
480 }
481 return;
482 }
483}
484
487
488static unlang_action_t sql_select_query_resume(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
489{
490 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
491 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
492 SQLINTEGER i;
493 SQLLEN len;
494 SQLRETURN ret = SQL_STILL_EXECUTING;
495
496 if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
497
498 while (ret == SQL_STILL_EXECUTING) {
499 ret = SQLNumResultCols(conn->stmt, &conn->colcount);
500 }
501 if (sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt)) {
502 query_ctx->rcode = RLM_SQL_ERROR;
504 }
505
506 /* Reserving memory for result */
507 conn->row = talloc_zero_array(conn, char *, conn->colcount + 1); /* Space for pointers */
508 conn->ind = talloc_zero_array(conn, SQLLEN, conn->colcount); /* Space for indicators */
509
510 for (i = 1; i <= conn->colcount; i++) {
511 len = 0;
512 /* SQLColAttribute can in theory run Async */
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]);
516 }
517
519}
520
521static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
522{
523 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
524 SQLSMALLINT len, i;
525 SQLRETURN ret;
526 char const **names;
527 char field[128];
528
529 if (conn->colcount == 0) return RLM_SQL_ERROR;
530
531 MEM(names = talloc_array(query_ctx, char const *, conn->colcount));
532
533 for (i = 0; i < conn->colcount; i++) {
534 char *p;
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);
539 }
540 switch (ret) {
541 case SQL_INVALID_HANDLE:
542 case SQL_ERROR:
543 ERROR("Failed retrieving field name at index %i", i);
544 sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt);
546 return RLM_SQL_ERROR;
547
548 default:
549 break;
550 }
551
552 MEM(p = talloc_array(names, char, (size_t)len + 1));
553 strlcpy(p, field, (size_t)len + 1);
554 names[i] = p;
555 }
556 *out = names;
557
558 return RLM_SQL_OK;
559}
560
561static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
562{
563 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
564 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
565 SQLRETURN ret = SQL_STILL_EXECUTING;
566 SQLINTEGER i;
567
568 query_ctx->row = NULL;
569
570 while (ret == SQL_STILL_EXECUTING) {
571 ret = SQLFetch(conn->stmt);
572 }
573 if (ret == SQL_NO_DATA_FOUND) {
574 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
576 }
577
578 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt);
579 if (query_ctx->rcode != RLM_SQL_OK) RETURN_MODULE_FAIL;
580
581 /*
582 * If the field is NULL, then SQLFetch doesn't touch pointer, so set it here
583 */
584 for (i = 0; i < conn->colcount; i++) {
585 if (conn->ind[i] == SQL_NULL_DATA) conn->row[i] = NULL;
586 }
587
588 query_ctx->row = conn->row;
589
590 query_ctx->rcode = RLM_SQL_OK;
592}
593
595{
596 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
597
598 TALLOC_FREE(conn->row);
599 TALLOC_FREE(conn->ind);
600 conn->colcount = 0;
601
602 return RLM_SQL_OK;
603}
604
606{
608
609 /*
610 * If the query is not in a state which would return results, then do nothing.
611 */
612 if (query_ctx->treq && !(query_ctx->treq->state &
614
615 /*
616 * If the connection doesn't exist there's nothing to do
617 */
618 if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
619
620 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
621
622 TALLOC_FREE(conn->row);
623 conn->colcount = 0;
624 conn->query_ctx = NULL;
625
626 /*
627 * SQL_CLOSE - The cursor (if any) associated with the statement
628 * handle (StatementHandle) is closed and all pending results are
629 * discarded. The application can reopen the cursor by calling
630 * SQLExecute() with the same or different values in the
631 * application variables (if any) that are bound to StatementHandle.
632 * If no cursor has been associated with the statement handle,
633 * this option has no effect (no warning or error is generated).
634 *
635 * So, this call does NOT free the statement at all, it merely
636 * resets it for the next call. This is terrible terrible naming.
637 */
638 SQLFreeStmt(conn->stmt, SQL_CLOSE);
639
640 return RLM_SQL_OK;
641}
642
643/** Retrieves any errors associated with the query context
644 *
645 * @note Caller will free any memory allocated in ctx.
646 *
647 * @param ctx to allocate temporary error buffers in.
648 * @param out Array of #sql_log_entry_t to fill.
649 * @param outlen Length of out array.
650 * @param query_ctx Query context to retrieve error for.
651 * @return number of errors written to the #sql_log_entry_t array.
652 */
653static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
654 fr_sql_query_t *query_ctx)
655{
656 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
657 SQLCHAR state[256];
658 SQLCHAR errbuff[256];
659 SQLINTEGER errnum = 0;
660 SQLSMALLINT length = 255;
661 size_t i = 0;
662
663 fr_assert(outlen > 2);
664
665 /*
666 * Depending on which handles exist at the time of calling there
667 * may be 1, 2 or 3 handles to check errors on.
668 */
669 errbuff[0] = state[0] = '\0';
670 SQLGetDiagRec(SQL_HANDLE_ENV, conn->env, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
671 if (errnum != 0) {
672 out[i].type = L_ERR;
673 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
674 i++;
675 }
676 if (conn->dbc == SQL_NULL_HANDLE) return i;
677
678 SQLGetDiagRec(SQL_HANDLE_DBC, conn->dbc, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
679 if (errnum != 0) {
680 out[i].type = L_ERR;
681 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
682 i++;
683 }
684 if (conn->stmt == SQL_NULL_HANDLE) return i;
685
686 SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
687 if (errnum != 0) {
688 out[i].type = L_ERR;
689 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
690 i++;
691 }
692
693 return i;
694}
695
696/*************************************************************************
697 *
698 * Function: sql_affected_rows
699 *
700 * Purpose: Return the number of rows affected by the query (update,
701 * or insert)
702 *
703 *************************************************************************/
705{
706 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
707 SQLRETURN ret;
708 SQLLEN affected_rows;
709
710 ret = SQLRowCount(conn->stmt, &affected_rows);
711 if (sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt)) return -1;
712
713 return affected_rows;
714}
715
716
717/* Exported to rlm_sql */
720 .common = {
721 .magic = MODULE_MAGIC_INIT,
722 .name = "sql_unixodbc"
723 },
725 .sql_query_resume = sql_query_resume,
726 .sql_select_query_resume = sql_select_query_resume,
727 .sql_affected_rows = sql_affected_rows,
728 .sql_fields = sql_fields,
729 .sql_fetch_row = sql_fetch_row,
730 .sql_free_result = sql_free_result,
731 .sql_error = sql_error,
732 .sql_finish_query = sql_finish_query,
733 .sql_finish_select_query = sql_finish_query,
734 .trunk_io_funcs = {
735 .connection_alloc = sql_trunk_connection_alloc,
736 .connection_notify = sql_trunk_connection_notify,
737 .request_mux = sql_trunk_request_mux,
738 .request_cancel_mux = sql_request_cancel_mux,
739 .request_cancel = sql_request_cancel,
740 .request_fail = sql_request_fail,
741 }
742};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define NDEBUG_UNUSED
Definition build.h:326
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
connection_state_t
Definition connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:52
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:50
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:84
fr_time_delta_t connection_timeout
How long to wait for the connection to open or for shutdown to close the connection.
Definition connection.h:92
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define fr_event_timer_in(...)
Definition event.h:255
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1359
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RDEBUG3(fmt,...)
Definition log.h:343
talloc_free(reap)
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition event.c:1611
Stores all information relating to an event list.
Definition event.c:411
A timer event.
Definition event.c:102
@ L_ERR
Error message.
Definition log.h:56
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define INFO(fmt,...)
Definition radict.c:54
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition rlm_sql.h:138
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:134
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:137
char const * query_str
Query string to run.
Definition rlm_sql.h:136
request_t * request
Request this query relates to.
Definition rlm_sql.h:132
sql_rcode_t
Action to take at end of an SQL query.
Definition rlm_sql.h:44
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition rlm_sql.h:49
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition rlm_sql.h:48
@ RLM_SQL_ERROR
General connection/server error.
Definition rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition rlm_sql.h:50
@ SQL_QUERY_SELECT
Definition rlm_sql.h:114
fr_time_delta_t query_timeout
How long to allow queries to run for.
Definition rlm_sql.h:94
char const * sql_server
Server to connect to.
Definition rlm_sql.h:74
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those resulting from a unique key violation.
Definition rlm_sql.h:164
char ** rlm_sql_row_t
Definition rlm_sql.h:59
char const * sql_login
Login credentials to use.
Definition rlm_sql.h:76
rlm_sql_row_t row
Row data from the last query.
Definition rlm_sql.h:140
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:139
char const * sql_password
Login password to use.
Definition rlm_sql.h:77
trunk_request_t * treq
Trunk request for this query.
Definition rlm_sql.h:135
trunk_conf_t trunk_conf
Configuration for trunk connections.
Definition rlm_sql.h:99
@ SQL_QUERY_CANCELLED
A cancellation has been sent to the server.
Definition rlm_sql.h:127
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:124
@ SQL_QUERY_FAILED
Failed to submit.
Definition rlm_sql.h:121
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:123
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:122
Definition rlm_sql.h:61
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_QUERY_RESUME
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
#define SQL_QUERY_FAIL
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 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)
static connection_state_t _sql_connection_init(void **h, connection_t *conn, 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.
static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason, UNUSED void *uctx)
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_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, 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_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 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.
module_flags_t flags
Flags that control how a module starts up and how a module is called.
Definition module.h:227
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
module_t common
Common fields for all loadable modules.
Definition rlm_sql.h:194
rlm_sql_config_t config
Definition rlm_sql.h:221
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:492
#define talloc_get_type_abort_const
Definition talloc.h:282
static const char * names[8]
Definition time.c:621
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
static int64_t fr_time_delta_to_usec(fr_time_delta_t delta)
Definition time.h:632
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
Definition time.h:568
"server local" time.
Definition time.h:69
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
Definition trunk.c:3835
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2132
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition trunk.c:2260
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2284
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition trunk.c:3883
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2050
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition trunk.c:3903
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition trunk.c:2072
Associates request queues with a connection.
Definition trunk.c:134
Wraps a normal request.
Definition trunk.c:100
connection_conf_t const * conn_conf
Connection configuration.
Definition trunk.h:225
trunk_connection_event_t
What type of I/O events the trunk connection is currently interested in receiving.
Definition trunk.h:72
@ TRUNK_CONN_EVENT_BOTH
Trunk should be notified if a connection is readable or writable.
Definition trunk.h:79
@ TRUNK_CONN_EVENT_WRITE
Trunk should be notified if a connection is writable.
Definition trunk.h:77
@ TRUNK_CONN_EVENT_NONE
Don't notify the trunk on connection state changes.
Definition trunk.h:73
@ TRUNK_CONN_EVENT_READ
Trunk should be notified if a connection is readable.
Definition trunk.h:75
trunk_cancel_reason_t
Reasons for a request being cancelled.
Definition trunk.h:55
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
Definition trunk.h:57
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
Definition trunk.h:173
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
Definition trunk.h:182
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition trunk.h:172
static fr_event_list_t * el
static size_t char ** out
Definition value.h:997