The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
21 RCSID("$Id: 58c1a88c69245cb32a8a91bb416cf0dffaa62954 $")
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 
33 typedef 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  */
66 static 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);
93  res = RLM_SQL_RECONNECT;
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 
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 
199 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
200 static 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));
209  *c = (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 
278 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
279 static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn, connection_t *conn, UNUSED void *uctx)
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) {
294  case SQL_QUERY_PREPARED:
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:
311  case RLM_SQL_ALT_QUERY:
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 
332 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
333 static 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 
344 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
345 static void sql_request_cancel_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
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) {
375  case SQL_QUERY_SUBMITTED:
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:
399  case RLM_SQL_ALT_QUERY:
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 
418  case SQL_QUERY_CANCELLED:
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  */
451 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
452 static void sql_trunk_connection_notify(UNUSED trunk_connection_t *tconn, connection_t *conn, UNUSED fr_event_list_t *el,
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 
474  FALL_THROUGH;
475 
478  sql_trunk_connection_write_poll, tconn) < 0) {
479  ERROR("Unable to insert polling event");
480  }
481  return;
482  }
483 }
484 
487 
488 static 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 
521 static 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 
561 static 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  * @param config rlm_sql config.
652  * @return number of errors written to the #sql_log_entry_t array.
653  */
654 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
655  fr_sql_query_t *query_ctx)
656 {
657  rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
658  SQLCHAR state[256];
659  SQLCHAR errbuff[256];
660  SQLINTEGER errnum = 0;
661  SQLSMALLINT length = 255;
662  size_t i = 0;
663 
664  fr_assert(outlen > 2);
665 
666  /*
667  * Depending on which handles exist at the time of calling there
668  * may be 1, 2 or 3 handles to check errors on.
669  */
670  errbuff[0] = state[0] = '\0';
671  SQLGetDiagRec(SQL_HANDLE_ENV, conn->env, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
672  if (errnum != 0) {
673  out[i].type = L_ERR;
674  out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
675  i++;
676  }
677  if (conn->dbc == SQL_NULL_HANDLE) return i;
678 
679  SQLGetDiagRec(SQL_HANDLE_DBC, conn->dbc, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
680  if (errnum != 0) {
681  out[i].type = L_ERR;
682  out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
683  i++;
684  }
685  if (conn->stmt == SQL_NULL_HANDLE) return i;
686 
687  SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, state, &errnum, errbuff, sizeof(errbuff), &length);
688  if (errnum != 0) {
689  out[i].type = L_ERR;
690  out[i].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
691  i++;
692  }
693 
694  return i;
695 }
696 
697 /*************************************************************************
698  *
699  * Function: sql_affected_rows
700  *
701  * Purpose: Return the number of rows affected by the query (update,
702  * or insert)
703  *
704  *************************************************************************/
706 {
707  rlm_sql_unixodbc_conn_t *conn = query_ctx->tconn->conn->h;
708  SQLRETURN ret;
709  SQLLEN affected_rows;
710 
711  ret = SQLRowCount(conn->stmt, &affected_rows);
712  if (sql_check_error(ret, SQL_HANDLE_STMT, conn->stmt)) return -1;
713 
714  return affected_rows;
715 }
716 
717 
718 /* Exported to rlm_sql */
721  .common = {
722  .magic = MODULE_MAGIC_INIT,
723  .name = "sql_unixodbc"
724  },
726  .sql_query_resume = sql_query_resume,
727  .sql_select_query_resume = sql_select_query_resume,
728  .sql_affected_rows = sql_affected_rows,
729  .sql_fields = sql_fields,
730  .sql_fetch_row = sql_fetch_row,
731  .sql_free_result = sql_free_result,
732  .sql_error = sql_error,
733  .sql_finish_query = sql_finish_query,
734  .sql_finish_select_query = sql_finish_query,
735  .uses_trunks = true,
736  .trunk_io_funcs = {
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,
742  .request_fail = sql_request_fail,
743  }
744 };
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:165
#define USES_APPLE_DEPRECATED_API
Definition: build.h:468
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
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
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#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
switch(errno)
Definition: fd_errno.h:4
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 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
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:146
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition: rlm_sql.h:142
fr_sql_query_type_t type
Type of query.
Definition: rlm_sql.h:145
char const * query_str
Query string to run.
Definition: rlm_sql.h:144
request_t * request
Request this query relates to.
Definition: rlm_sql.h:139
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:121
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.
Definition: rlm_sql.h:172
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:148
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:147
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:143
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:134
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
@ SQL_QUERY_FAILED
Failed to submit.
Definition: rlm_sql.h:128
@ SQL_QUERY_SUBMITTED
Submitted for execution.
Definition: rlm_sql.h:130
@ SQL_QUERY_PREPARED
Ready to submit.
Definition: rlm_sql.h:129
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)
Definition: rlm_sql_db2.c:301
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
Definition: rlm_sql_trunk.h:56
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
Definition: rlm_sql_trunk.h:35
#define SQL_QUERY_FAIL
Definition: rlm_sql_trunk.h:64
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)
CC_NO_UBSAN(function)
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.
Definition: connection.c:1167
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
Definition: connection.c:1137
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:204
rlm_sql_config_t config
Definition: rlm_sql.h:238
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:617
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:3813
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
void trunk_request_signal_cancel_sent(trunk_request_t *treq)
Signal that a remote server has been notified of the cancellation.
Definition: trunk.c:2248
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
Definition: trunk.c:2272
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:3861
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Definition: trunk.c:2043
void trunk_connection_signal_writable(trunk_connection_t *tconn)
Signal that a trunk connection is writable.
Definition: trunk.c:3881
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:2065
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
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