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: 99543110267560be590f73a5187b3d6ea6cc8e6e $")
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_timer_t *read_ev; /* Timer event for polling reading this connection */
45 fr_timer_t *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
117 if (c->stmt) SQLFreeHandle(SQL_HANDLE_STMT, c->stmt);
118
119 if (c->dbc) {
120 SQLDisconnect(c->dbc);
121 SQLFreeHandle(SQL_HANDLE_DBC, c->dbc);
122 }
123
124 if (c->env) SQLFreeHandle(SQL_HANDLE_ENV, c->env);
125 talloc_free(h);
126}
127
129{
130 char buff[256], verbuf[10];
131 SQLRETURN ret;
132 SQLULEN timeout;
133
134 SQLGetInfo(c->dbc, SQL_DRIVER_NAME, buff, sizeof(buff), NULL);
135 SQLGetInfo(c->dbc, SQL_DRIVER_ODBC_VER, verbuf, sizeof(verbuf), NULL);
136 SQLGetInfo(c->dbc, SQL_ASYNC_MODE, &c->async_mode, 0, NULL);
137 switch(c->async_mode) {
138 case SQL_AM_NONE:
139 DEBUG2("Using driver %s, ODBC version %s. Driver does not support async operations", buff, verbuf);
140 break;
141 case SQL_AM_CONNECTION:
142 DEBUG2("Using driver %s, ODBC version %s. Async operation is set per connection", buff, verbuf);
143 ret = SQLSetConnectAttr(c->dbc, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, SQL_IS_UINTEGER);
144 sql_check_error(ret, SQL_HANDLE_DBC, c->dbc);
145 break;
146 case SQL_AM_STATEMENT:
147 DEBUG2("Using driver %s, ODBC version %s. Async operation is set per statement", buff, verbuf);
148 break;
149 }
150
151 /* Allocate the stmt handle */
152 ret = SQLAllocHandle(SQL_HANDLE_STMT, c->dbc, &c->stmt);
153 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
154 ERROR("Can't allocate the stmt");
155 _sql_connection_close(NULL, c, NULL);
157 }
158 if (c->async_mode == SQL_AM_STATEMENT) {
159 ret = SQLSetStmtAttr(c->stmt, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, 0);
160 sql_check_error(ret, SQL_HANDLE_STMT, c->stmt);
161 }
162
164 SQLSetStmtAttr(c->stmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)timeout, SQL_IS_UINTEGER);
165
167}
168
170{
171 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_unixodbc_conn_t);
172 SQLRETURN ret;
173
174 ret = SQLConnect(c->dbc,
175 UNCONST(SQLCHAR *, c->config->sql_server), strlen(c->config->sql_server),
176 UNCONST(SQLCHAR *, c->config->sql_login), strlen(c->config->sql_login),
177 UNCONST(SQLCHAR *, c->config->sql_password), strlen(c->config->sql_password));
178
179 if (ret == SQL_STILL_EXECUTING) {
181 false, sql_trunk_connection_init_poll, c) < 0) {
182 ERROR("Unable to insert polling event");
184 }
185 return;
186 }
187
188 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
189 ERROR("Connection failed");
191 return;
192 }
193
195}
196
197CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
198static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
199{
201 rlm_sql_config_t const *config = &sql->config;
203 SQLRETURN ret;
205
206 MEM(c = talloc_zero(conn, rlm_sql_unixodbc_conn_t));
208 .conn = conn,
209 .config = config,
210 .select_interval = 1000, /* Default starting poll interval - 1ms*/
211 .query_interval = 1000,
212 };
213
214 /* Allocate environment handle and register version */
215 ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &c->env);
216 if (ret == SQL_ERROR) {
217 ERROR("Can't allocate environment handle");
218 error:
219 _sql_connection_close(NULL, c, NULL);
221 }
222
223 ret = SQLSetEnvAttr(c->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3_80, 0);
224 if (sql_check_error(ret, SQL_HANDLE_ENV, c->env)) {
225 ERROR("Can't register ODBC version");
226 goto error;
227 }
228
229 /* Allocate connection handle */
230 ret = SQLAllocHandle(SQL_HANDLE_DBC, c->env, &c->dbc);
231 if (sql_check_error(ret, SQL_HANDLE_ENV, c->env)) {
232 ERROR("Can't allocate connection handle");
233 goto error;
234 }
235
236 /*
237 * Set the login / connection timeout
238 *
239 * Note SQLSetConnectionAttr and SQLSetStmtAttr have an insane parameter passing
240 * model. The 3rd parameter can be an integer, or a pointer to a string
241 * so integers get cast to pointers to match the function signature.
242 */
243 SQLSetConnectAttr(c->dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)timeout, SQL_IS_UINTEGER);
244 SQLSetConnectAttr(c->dbc, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)timeout, SQL_IS_UINTEGER);
245
246 /* Set the connection handle to Async */
247 SQLSetConnectAttr(c->dbc, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_UINTEGER);
248
249 /* Connect to the datasource */
250 ret = SQLConnect(c->dbc,
251 UNCONST(SQLCHAR *, config->sql_server), strlen(config->sql_server),
252 UNCONST(SQLCHAR *, config->sql_login), strlen(config->sql_login),
253 UNCONST(SQLCHAR *, config->sql_password), strlen(config->sql_password));
254
255 if (ret == SQL_STILL_EXECUTING) {
256 if (fr_timer_in(c, conn->el->tl, &c->read_ev, fr_time_delta_from_usec(c->query_interval),
257 false, sql_trunk_connection_init_poll, c) < 0) {
258 ERROR("Unable to insert polling event");
259 goto error;
260 }
261 *h = c;
263 }
264
265 if (sql_check_error(ret, SQL_HANDLE_DBC, c->dbc)) {
266 ERROR("Connection failed");
267 goto error;
268 }
269
270 *h = c;
272}
273
275
276CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
278{
279 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
280 request_t *request;
281 trunk_request_t *treq;
282 fr_sql_query_t *query_ctx;
283 SQLRETURN ret;
284
285 if (trunk_connection_pop_request(&treq, tconn) != 0) return;
286 if (!treq) return;
287
288 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
289 request = query_ctx->request;
290
291 switch(query_ctx->status) {
293 ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
294 ret = SQLExecDirect(sql_conn->stmt, UNCONST(SQLCHAR *, query_ctx->query_str), strlen(query_ctx->query_str));
295 query_ctx->tconn = tconn;
296
297 if (ret == SQL_STILL_EXECUTING) {
298 ROPTIONAL(RDEBUG3, DEBUG3, "Awaiting response");
299 query_ctx->status = SQL_QUERY_SUBMITTED;
300 sql_conn->query_ctx = query_ctx;
301 sql_conn->poll_count = 0;
303 return;
304 }
305
306 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, sql_conn->stmt);
307 switch(query_ctx->rcode) {
308 case RLM_SQL_OK:
310 break;
311
312 default:
313 query_ctx->status = SQL_QUERY_FAILED;
316 return;
317 }
318 query_ctx->status = SQL_QUERY_RETURNED;
319 break;
320
321 default:
322 return;
323 }
324
325 ROPTIONAL(RDEBUG3, DEBUG3, "Got immediate response");
327 if (request) unlang_interpret_mark_runnable(request);
328}
329
330CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
331static void sql_request_cancel(connection_t *conn, void *preq, trunk_cancel_reason_t reason,
332 UNUSED void *uctx)
333{
334 fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
335 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
336
337 if (!query_ctx->treq) return;
338 if (reason != TRUNK_CANCEL_REASON_SIGNAL) return;
339 if (sql_conn->query_ctx == query_ctx) sql_conn->query_ctx = NULL;
340}
341
342CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
344 connection_t *conn, UNUSED void *uctx)
345{
346 trunk_request_t *treq;
347 rlm_sql_unixodbc_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
348 SQLRETURN ret;
349 fr_sql_query_t *query_ctx;
350
351 if ((trunk_connection_pop_cancellation(&treq, tconn)) != 0) return;
352 if (!treq) return;
353
354 query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
355 ret = SQLCancel(sql_conn->stmt);
356 query_ctx->status = SQL_QUERY_CANCELLED;
357 if (ret == SQL_STILL_EXECUTING) {
359 return;
360 }
362}
363
365{
366 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(uctx, rlm_sql_unixodbc_conn_t);
367 fr_sql_query_t *query_ctx = c->query_ctx;
368 SQLRETURN ret;
369 trunk_request_t *treq = query_ctx->treq;
370 request_t *request = query_ctx->request;
371
372 switch (query_ctx->status) {
374 ret = SQLExecDirect(c->stmt, UNCONST(SQLCHAR *, query_ctx->query_str), strlen(query_ctx->query_str));
375 c->poll_count++;
376 /* Back off the poll interval, up to half the query timeout */
377 if (c->poll_count > 2) {
378 if (query_ctx->type == SQL_QUERY_SELECT) {
380 } else {
382 }
383 }
384 if (ret == SQL_STILL_EXECUTING) {
385 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
386 if (fr_timer_in(c, tl, &c->read_ev,
388 false, sql_trunk_connection_read_poll, c) < 0) {
389 ERROR("Unable to insert polling event");
390 }
391 return;
392 }
393
394 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, c->stmt);
395 switch(query_ctx->rcode) {
396 case RLM_SQL_OK:
398 /* If we only polled once, reduce the interval*/
399 if (c->poll_count == 1) {
400 if (query_ctx->type == SQL_QUERY_SELECT) {
401 c->select_interval /= 2;
402 } else {
403 c->query_interval /= 2;
404 }
405 }
406 break;
407
408 default:
409 query_ctx->status = SQL_QUERY_FAILED;
412 return;
413 }
414 break;
415
417 ret = SQLCancel(c->stmt);
418 if (ret == SQL_STILL_EXECUTING) {
419 ROPTIONAL(RDEBUG3, DEBUG3, "Still awaiting response");
421 false, sql_trunk_connection_read_poll, c) < 0) {
422 ERROR("Unable to insert polling event");
423 }
424 return;
425 }
427 return;
428
429 default:
430 return;
431 }
432
433 if (request) unlang_interpret_mark_runnable(request);
434}
435
437{
438 trunk_connection_t *tconn = talloc_get_type_abort(uctx, trunk_connection_t);
439
441}
442
443/*
444 * UnixODBC doesn't support event driven async, so in this case
445 * we have to resort to polling.
446 *
447 * This "notify" callback sets up the appropriate polling events.
448 */
449CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
451 trunk_connection_event_t notify_on, UNUSED void *uctx)
452{
453 rlm_sql_unixodbc_conn_t *c = talloc_get_type_abort(conn->h, rlm_sql_unixodbc_conn_t);
454 fr_sql_query_t *query_ctx = c->query_ctx;
455 uint poll_interval = (query_ctx && query_ctx->type != SQL_QUERY_SELECT) ? c->query_interval : c->select_interval;
456 switch (notify_on) {
460 return;
461
464 if (c->query_ctx) {
465 if (fr_timer_in(c, el->tl, &c->read_ev, fr_time_delta_from_usec(poll_interval),
466 false, sql_trunk_connection_read_poll, c) < 0) {
467 ERROR("Unable to insert polling event");
468 }
469 }
470 if (notify_on == TRUNK_CONN_EVENT_READ) return;
471
473
476 false, sql_trunk_connection_write_poll, tconn) < 0) {
477 ERROR("Unable to insert polling event");
478 }
479 return;
480 }
481}
482
485
487{
488 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
489 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
490 SQLINTEGER i;
491 SQLLEN len;
492 SQLRETURN ret = SQL_STILL_EXECUTING;
493
494 if (query_ctx->rcode != RLM_SQL_OK) RETURN_UNLANG_FAIL;
495
496 while (ret == SQL_STILL_EXECUTING) {
497 ret = SQLNumResultCols(conn->stmt, &conn->colcount);
498 }
499 if (sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt)) {
500 query_ctx->rcode = RLM_SQL_ERROR;
502 }
503
504 /* Reserving memory for result */
505 conn->row = talloc_zero_array(conn, char *, conn->colcount + 1); /* Space for pointers */
506 conn->ind = talloc_zero_array(conn, SQLLEN, conn->colcount); /* Space for indicators */
507
508 for (i = 1; i <= conn->colcount; i++) {
509 len = 0;
510 /* SQLColAttribute can in theory run Async */
511 while (SQLColAttribute(conn->stmt, (SQLUSMALLINT) i, SQL_DESC_LENGTH, NULL, 0, NULL, &len) == SQL_STILL_EXECUTING);
512 conn->row[i - 1] = talloc_array(conn->row, char, ++len);
513 SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, &conn->ind[i - 1]);
514 }
515
517}
518
519static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
520{
521 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
522 SQLSMALLINT len, i;
523 SQLRETURN ret;
524 char const **names;
525 char field[128];
526
527 if (conn->colcount == 0) return RLM_SQL_ERROR;
528
529 MEM(names = talloc_array(query_ctx, char const *, conn->colcount));
530
531 for (i = 0; i < conn->colcount; i++) {
532 char *p;
533 ret = SQL_STILL_EXECUTING;
534 while (ret == SQL_STILL_EXECUTING) {
535 ret = SQLColAttribute(conn->stmt, i + 1, SQL_DESC_NAME,
536 field, sizeof(field), &len, NULL);
537 }
538 switch (ret) {
539 case SQL_INVALID_HANDLE:
540 case SQL_ERROR:
541 ERROR("Failed retrieving field name at index %i", i);
542 sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt);
544 return RLM_SQL_ERROR;
545
546 default:
547 break;
548 }
549
550 MEM(p = talloc_array(names, char, (size_t)len + 1));
551 strlcpy(p, field, (size_t)len + 1);
552 names[i] = p;
553 }
554 *out = names;
555
556 return RLM_SQL_OK;
557}
558
559static unlang_action_t sql_fetch_row(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
560{
561 fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
562 rlm_sql_unixodbc_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
563 SQLRETURN ret = SQL_STILL_EXECUTING;
564 SQLINTEGER i;
565
566 query_ctx->row = NULL;
567
568 while (ret == SQL_STILL_EXECUTING) {
569 ret = SQLFetch(conn->stmt);
570 }
571 if (ret == SQL_NO_DATA_FOUND) {
572 query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
574 }
575
576 query_ctx->rcode = sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt);
577 if (query_ctx->rcode != RLM_SQL_OK) RETURN_UNLANG_FAIL;
578
579 /*
580 * If the field is NULL, then SQLFetch doesn't touch pointer, so set it here
581 */
582 for (i = 0; i < conn->colcount; i++) {
583 if (conn->ind[i] == SQL_NULL_DATA) conn->row[i] = NULL;
584 }
585
586 query_ctx->row = conn->row;
587
588 query_ctx->rcode = RLM_SQL_OK;
590}
591
593{
594 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
595
596 TALLOC_FREE(conn->row);
597 TALLOC_FREE(conn->ind);
598 conn->colcount = 0;
599
600 return RLM_SQL_OK;
601}
602
604{
606
607 /*
608 * If the query is not in a state which would return results, then do nothing.
609 */
610 if (query_ctx->treq && !(query_ctx->treq->state &
612
613 /*
614 * If the connection doesn't exist there's nothing to do
615 */
616 if (!query_ctx->tconn || !query_ctx->tconn->conn || !query_ctx->tconn->conn->h) return RLM_SQL_ERROR;
617
618 conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_unixodbc_conn_t);
619
620 TALLOC_FREE(conn->row);
621 TALLOC_FREE(conn->ind);
622 conn->colcount = 0;
623 conn->query_ctx = NULL;
624
625 /*
626 * SQL_CLOSE - The cursor (if any) associated with the statement
627 * handle (StatementHandle) is closed and all pending results are
628 * discarded. The application can reopen the cursor by calling
629 * SQLExecute() with the same or different values in the
630 * application variables (if any) that are bound to StatementHandle.
631 * If no cursor has been associated with the statement handle,
632 * this option has no effect (no warning or error is generated).
633 *
634 * So, this call does NOT free the statement at all, it merely
635 * resets it for the next call. This is terrible terrible naming.
636 */
637 SQLFreeStmt(conn->stmt, SQL_CLOSE);
638
639 return RLM_SQL_OK;
640}
641
642/** Retrieves any errors associated with the query context
643 *
644 * @note Caller will free any memory allocated in ctx.
645 *
646 * @param ctx to allocate temporary error buffers in.
647 * @param out Array of #sql_log_entry_t to fill.
648 * @param outlen Length of out array.
649 * @param query_ctx Query context to retrieve error for.
650 * @return number of errors written to the #sql_log_entry_t array.
651 */
652static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
653 fr_sql_query_t *query_ctx)
654{
655 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
656 SQLCHAR state[256];
657 SQLCHAR errbuff[256];
658 SQLINTEGER errnum = 0;
659 SQLSMALLINT length = 255;
660 size_t i = 0;
661
662 fr_assert(outlen > 2);
663
664 /*
665 * Depending on which handles exist at the time of calling there
666 * may be 1, 2 or 3 handles to check errors on.
667 */
668 errbuff[0] = state[0] = '\0';
669 SQLGetDiagRec(SQL_HANDLE_ENV, conn->env, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
670 if (errnum != 0) {
671 out[i].type = L_ERR;
672 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
673 i++;
674 }
675 if (conn->dbc == SQL_NULL_HANDLE) return i;
676
677 SQLGetDiagRec(SQL_HANDLE_DBC, conn->dbc, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
678 if (errnum != 0) {
679 out[i].type = L_ERR;
680 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
681 i++;
682 }
683 if (conn->stmt == SQL_NULL_HANDLE) return i;
684
685 SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
686 if (errnum != 0) {
687 out[i].type = L_ERR;
688 out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
689 i++;
690 }
691
692 return i;
693}
694
695/*************************************************************************
696 *
697 * Function: sql_affected_rows
698 *
699 * Purpose: Return the number of rows affected by the query (update,
700 * or insert)
701 *
702 *************************************************************************/
704{
705 rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
706 SQLRETURN ret;
707 SQLLEN affected_rows;
708
709 ret = SQLRowCount(conn->stmt, &affected_rows);
710 if (sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt)) return -1;
711
712 return affected_rows;
713}
714
715
716/* Exported to rlm_sql */
719 .common = {
720 .magic = MODULE_MAGIC_INIT,
721 .name = "sql_unixodbc"
722 },
724 .sql_query_resume = sql_query_resume,
725 .sql_select_query_resume = sql_select_query_resume,
726 .sql_affected_rows = sql_affected_rows,
727 .sql_fields = sql_fields,
728 .sql_fetch_row = sql_fetch_row,
729 .sql_free_result = sql_free_result,
730 .sql_error = sql_error,
731 .sql_finish_query = sql_finish_query,
732 .sql_finish_select_query = sql_finish_query,
733 .trunk_io_funcs = {
734 .connection_alloc = sql_trunk_connection_alloc,
735 .connection_notify = sql_trunk_connection_notify,
736 .request_mux = sql_trunk_request_mux,
737 .request_cancel_mux = sql_request_cancel_mux,
738 .request_cancel = sql_request_cancel,
739 .request_fail = sql_request_fail,
740 }
741};
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:474
#define RCSID(id)
Definition build.h:487
#define NDEBUG_UNUSED
Definition build.h:328
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define CC_NO_UBSAN(_sanitize)
Definition build.h:430
#define UNUSED
Definition build.h:317
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition connection.h:56
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
Definition connection.h:52
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:85
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:93
#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
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1638
#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:540
#define RDEBUG3(fmt,...)
Definition log.h:355
Stores all information relating to an event list.
Definition event.c:377
@ L_ERR
Error message.
Definition log.h:56
static const conf_parser_t config[]
Definition base.c:169
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
#define INFO(fmt,...)
Definition radict.c:64
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_OK
Definition rcode.h:64
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition rlm_sql.h:144
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition rlm_sql.h:140
fr_sql_query_type_t type
Type of query.
Definition rlm_sql.h:143
char const * query_str
Query string to run.
Definition rlm_sql.h:142
request_t * request
Request this query relates to.
Definition rlm_sql.h:138
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:120
fr_time_delta_t query_timeout
How long to allow queries to run for.
Definition rlm_sql.h:96
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:171
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:146
sql_rcode_t rcode
Result code.
Definition rlm_sql.h:145
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:141
trunk_conf_t trunk_conf
Configuration for trunk connections.
Definition rlm_sql.h:105
@ SQL_QUERY_CANCELLED
A cancellation has been sent to the server.
Definition rlm_sql.h:133
@ SQL_QUERY_RETURNED
Query has executed.
Definition rlm_sql.h:130
@ SQL_QUERY_FAILED
Failed to submit.
Definition rlm_sql.h:127
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition rlm_sql.h:129
@ SQL_QUERY_PREPARED
Ready to submit.
Definition rlm_sql.h:128
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
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)
SQL_QUERY_FAIL static SQL_QUERY_RESUME unlang_action_t sql_select_query_resume(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
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 unlang_action_t sql_fetch_row(unlang_result_t *p_result, UNUSED request_t *request, void *uctx)
static connection_state_t _sql_connection_init(void **h, connection_t *conn, 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_init_poll(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, fr_event_list_t *el, trunk_connection_event_t notify_on, UNUSED void *uctx)
static void sql_trunk_connection_read_poll(fr_timer_list_t *tl, 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_write_poll(UNUSED fr_timer_list_t *tl, 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:236
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:201
rlm_sql_config_t config
Definition rlm_sql.h:228
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:545
#define talloc_get_type_abort_const
Definition talloc.h:113
static const char * names[8]
Definition time.c:584
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
An event timer list.
Definition timer.c:50
A timer event.
Definition timer.c:84
#define fr_timer_in(...)
Definition timer.h:87
#define FR_TIMER_DISARM(_ev)
Definition timer.h:91
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:3856
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition trunk.c:2150
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition trunk.c:2278
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition trunk.c:2302
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:3904
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition trunk.c:2068
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition trunk.c:3924
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:2090
Associates request queues with a connection.
Definition trunk.c:133
Wraps a normal request.
Definition trunk.c:99
connection_conf_t const * conn_conf
Connection configuration.
Definition trunk.h:226
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:174
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
Definition trunk.h:183
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
Definition trunk.h:173
static fr_event_list_t * el
static size_t char ** out
Definition value.h:1030