40 RCSID(
"$Id: 4574f9fdf8d37e44ff50f906dbed432ddfb1656e $")
42 #define LOG_PREFIX "sql - postgresql"
44 #include <freeradius-devel/server/base.h>
45 #include <freeradius-devel/util/debug.h>
50 #include <postgres_ext.h>
57 # define NAMEDATALEN 64
90 {
"03",
"SQL statement not yet complete",
RLM_SQL_OK },
143 return atoi(PQcmdTuples(result));
151 TALLOC_FREE(conn->
row);
155 #if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
158 char const *error_code;
159 char const *error_msg;
162 error_code = PQresultErrorField(result, PG_DIAG_SQLSTATE);
168 case PGRES_COMMAND_OK:
169 #ifdef HAVE_PGRES_SINGLE_TUPLE
170 case PGRES_SINGLE_TUPLE:
172 #ifdef HAVE_PGRES_TUPLES_CHUNK
173 case PGRES_TUPLES_CHUNK:
175 case PGRES_TUPLES_OK:
176 #ifdef HAVE_PGRES_COPY_BOTH
177 case PGRES_COPY_BOTH:
181 error_code =
"00000";
184 case PGRES_EMPTY_QUERY:
185 error_code =
"42000";
188 #ifdef HAVE_PGRES_PIPELINE_SYNC
189 case PGRES_PIPELINE_SYNC:
190 case PGRES_PIPELINE_ABORTED:
191 ERROR(
"libpq reported aborted pipeline");
195 case PGRES_BAD_RESPONSE:
196 case PGRES_NONFATAL_ERROR:
197 case PGRES_FATAL_ERROR:
198 ERROR(
"libpq provided no error code");
205 ERROR(
"Can't classify: %s", error_code);
209 DEBUG3(
"sqlstate %s matched %s: %s (%s)", error_code,
216 error_msg = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
217 if (error_msg)
WARN(
"%s", error_msg);
225 ERROR(
"Error occurred, no more information available, rebuild with newer libpq");
233 PostgresPollingStatusType status;
237 status = PQconnectPoll(c->
db);
244 c->
fd = PQsocket(c->
db);
246 case PGRES_POLLING_OK:
247 DEBUG2(
"Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
248 PQdb(c->
db), PQhost(c->
db), PQserverVersion(c->
db), PQprotocolVersion(c->
db),
249 PQbackendPID(c->
db));
250 PQsetnonblocking(c->
db, 1);
254 case PGRES_POLLING_FAILED:
256 ERROR(
"Connection failed: %s", PQerrorMessage(c->
db));
260 case PGRES_POLLING_READING:
264 case PGRES_POLLING_WRITING:
280 PostgresPollingStatusType status;
286 DEBUG2(
"Starting connection to PostgreSQL server using parameters: %s",
inst->db_string);
288 c->
db = PQconnectStart(
inst->db_string);
290 ERROR(
"Connection failed: Out of memory");
295 switch (PQstatus(c->
db)) {
297 c->
fd = PQsocket(c->
db);
298 DEBUG2(
"Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
299 PQdb(c->
db), PQhost(c->
db), PQserverVersion(c->
db), PQprotocolVersion(c->
db),
300 PQbackendPID(c->
db));
301 PQsetnonblocking(c->
db, 1);
306 ERROR(
"Connection failed: %s", PQerrorMessage(c->
db));
317 status = PQconnectPoll(c->
db);
318 c->
fd = PQsocket(c->
db);
323 DEBUG2(
"Connecting to database '%s' on '%s', fd %d", PQdb(c->
db), PQhost(c->
db), c->
fd);
370 switch (query_ctx->
status) {
374 query_ctx->
tconn = tconn;
399 PGresult *tmp_result;
400 ExecStatusType status;
407 switch (query_ctx->
status) {
409 if (PQconsumeInput(sql_conn->
db) == 0) {
414 if (PQisBusy(sql_conn->
db))
return;
418 sql_conn->
result = PQgetResult(sql_conn->
db);
421 while ((tmp_result = PQgetResult(sql_conn->
db)) != NULL)
435 status = PQresultStatus(sql_conn->
result);
440 case PGRES_COMMAND_OK:
451 #ifdef HAVE_PGRES_SINGLE_TUPLE
452 case PGRES_SINGLE_TUPLE:
454 #ifdef HAVE_PGRES_TUPLES_CHUNK
455 case PGRES_TUPLES_CHUNK:
457 case PGRES_TUPLES_OK:
460 numfields = PQnfields(sql_conn->
result);
464 #ifdef HAVE_PGRES_COPY_BOTH
465 case PGRES_COPY_BOTH:
469 DEBUG2(
"Data transfer started");
475 case PGRES_EMPTY_QUERY:
476 case PGRES_BAD_RESPONSE:
477 case PGRES_NONFATAL_ERROR:
478 case PGRES_FATAL_ERROR:
479 #ifdef HAVE_PGRES_PIPELINE_SYNC
480 case PGRES_PIPELINE_SYNC:
481 case PGRES_PIPELINE_ABORTED:
503 if (!query_ctx->
treq)
return;
516 PGresult *tmp_result;
519 cancel = PQgetCancel(sql_conn->
db);
520 if (!cancel)
goto complete;
521 if (PQcancel(cancel, errbuf,
sizeof(errbuf)) == 0) {
522 ERROR(
"Failed to cancel query: %s", errbuf);
524 PQfreeCancel(cancel);
530 while ((tmp_result = PQgetResult(sql_conn->
db)) != NULL)
548 fields = PQnfields(conn->
result);
551 MEM(
names = talloc_array(query_ctx,
char const *, fields));
553 for (i = 0; i < fields; i++)
names[i] = PQfname(conn->
result, i);
565 query_ctx->
row = NULL;
572 records = PQnfields(conn->
result);
575 if ((PQntuples(conn->
result) > 0) && (records > 0)) {
576 conn->
row = talloc_zero_array(conn,
char *, records + 1);
577 for (i = 0; i < records; i++) {
580 conn->
row[i] = talloc_array(conn->
row,
char, len + 1);
584 query_ctx->
row = conn->
row;
596 if (query_ctx->
treq && !(query_ctx->
treq->state &
605 if (conn->
result != NULL) {
635 p = PQerrorMessage(conn->
db);
636 while ((q = strchr(p,
'\n'))) {
640 if (++i == outlen)
return outlen;
674 if ((
inlen * 2 + 1) > outlen)
return 0;
698 if (
inst->send_application_name) {
707 snprintf(application_name,
sizeof(application_name),
716 if (!strchr(
config->sql_db,
'=')) {
719 if (
config->sql_server[0] !=
'\0') {
720 db_string = talloc_asprintf_append(db_string,
" host='%s'",
config->sql_server);
724 db_string = talloc_asprintf_append(db_string,
" port=%i",
config->sql_port);
727 if (
config->sql_login[0] !=
'\0') {
728 db_string = talloc_asprintf_append(db_string,
" user='%s'",
config->sql_login);
731 if (
config->sql_password[0] !=
'\0') {
732 db_string = talloc_asprintf_append(db_string,
" password='%s'",
config->sql_password);
739 if (
inst->send_application_name) {
740 db_string = talloc_asprintf_append(db_string,
" application_name='%s'", application_name);
751 if ((
config->sql_server[0] !=
'\0') && !strstr(db_string,
"host=")) {
752 db_string = talloc_asprintf_append(db_string,
" host='%s'",
config->sql_server);
755 if (
config->sql_port && !strstr(db_string,
"port=")) {
756 db_string = talloc_asprintf_append(db_string,
" port=%i",
config->sql_port);
759 if ((
config->sql_login[0] !=
'\0') && !strstr(db_string,
"user=")) {
760 db_string = talloc_asprintf_append(db_string,
" user='%s'",
config->sql_login);
763 if ((
config->sql_password[0] !=
'\0') && !strstr(db_string,
"password=")) {
764 db_string = talloc_asprintf_append(db_string,
" password='%s'",
config->sql_password);
771 if (
inst->send_application_name && !strstr(db_string,
"application_name=")) {
772 db_string = talloc_asprintf_append(db_string,
" application_name='%s'", application_name);
775 inst->db_string = db_string;
799 #if defined(WITH_TLS) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
800 # ifdef HAVE_PQINITOPENSSL
816 .init = _sql_connection_init,
819 inst->config.trunk_conf.conn_conf,
823 PERROR(
"Failed allocating state handler for SQL escape connection");
842 .name =
"sql_postgresql",
849 .sql_query_resume = sql_query_resume,
850 .sql_select_query_resume = sql_query_resume,
862 .connection_alloc = sql_trunk_connection_alloc,
863 .connection_notify = sql_trunk_connection_notify,
864 .request_mux = sql_trunk_request_mux,
865 .request_demux = sql_trunk_request_demux,
866 .request_cancel = sql_request_cancel,
867 .request_cancel_mux = sql_request_cancel_mux,
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Defines a CONF_PAIR to C data type mapping.
A section grouping multiple CONF_PAIR.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
@ CONNECTION_STATE_FAILED
Connection has failed.
@ CONNECTION_STATE_HALTED
The connection is in a halted stat.
@ CONNECTION_STATE_CLOSED
Connection has been closed.
@ CONNECTION_STATE_CONNECTING
Waiting for connection to establish.
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Holds a complete set of functions for a connection.
fr_dcursor_eval_t void const * uctx
#define RADIUSD_VERSION_STRING
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Stores all information relating to an event list.
main_config_t const * main_config
Main server configuration.
char const * name
Name of the daemon, usually 'radiusd'.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for instantiation calls.
static const conf_parser_t config[]
rlm_rcode_t
Return codes indicating the result of the module call.
static int instantiate(module_inst_ctx_t const *mctx)
Prototypes and functions for the SQL module.
int sql_state_entries_from_table(fr_trie_t *states, sql_state_entry_t const table[])
Insert the contents of a state table into the state trie.
sql_rcode_t rcode
What should happen if we receive this error.
fr_sql_query_status_t status
Status of the query.
trunk_connection_t * tconn
Trunk connection this query is being run on.
char const * meaning
Verbose description.
fr_table_num_sorted_t const sql_rcode_table[]
fr_trie_t * sql_state_trie_alloc(TALLOC_CTX *ctx)
Allocate a sql_state trie, and insert the initial set of entries.
rlm_sql_t const * inst
Module instance for this query.
char const * query_str
Query string to run.
request_t * request
Request this query relates to.
sql_rcode_t
Action to take at end of an SQL query.
@ RLM_SQL_QUERY_INVALID
Query syntax error.
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
@ RLM_SQL_ERROR
General connection/server error.
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
char const * sql_state
2-5 char error code.
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
sql_state_entry_t const * sql_state_entry_find(fr_trie_t const *states, char const *sql_state)
Lookup an SQL state based on an error code returned from the SQL server or client library.
rlm_sql_row_t row
Row data from the last query.
int sql_state_entries_from_cs(fr_trie_t *states, CONF_SECTION *overrides)
Insert the contents of a CONF_SECTION into the state trie.
sql_rcode_t rcode
Result code.
trunk_request_t * treq
Trunk request for this query.
@ SQL_QUERY_RETURNED
Query has executed.
@ SQL_QUERY_SUBMITTED
Submitted for execution.
@ SQL_QUERY_PREPARED
Ready to submit.
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
static int mod_load(void)
static void * sql_escape_arg_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, void *uctx)
static sql_state_entry_t sql_state_table[]
These are PostgreSQL specific error codes which are not covered in SQL 2011.
static void _sql_connection_close(fr_event_list_t *el, void *h, UNUSED void *uctx)
SQL_QUERY_FAIL static SQL_QUERY_RESUME sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
rlm_sql_driver_t rlm_sql_postgresql
int fd
fd for this connection's I/O events.
static void _sql_connect_io_notify(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
static ssize_t sql_escape_func(request_t *request, char *out, size_t outlen, char const *in, void *arg)
static conf_parser_t driver_config[]
connection_t * conn
Generic connection structure for this connection.
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
static void sql_escape_arg_free(void *uctx)
fr_sql_query_t * query_ctx
Current query running on this connection.
fr_trie_t * states
sql state trie.
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
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...
char const * db_string
Text based configuration string.
static int mod_instantiate(module_inst_ctx_t const *mctx)
bool send_application_name
Whether we send the application name to PostgreSQL.
static void free_result_row(rlm_sql_postgres_conn_t *conn)
Free the row of the current result that's stored in the conn struct.
PostgreSQL configuration.
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
void connection_signal_connected(connection_t *conn)
Asynchronously signal that the connection is open.
CONF_SECTION * conf
Module's instance configuration.
void * data
Module's instance data.
module_instance_t const * parent
Parent module's instance (if any).
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
size_t strlcpy(char *dst, char const *src, size_t siz)
module_t common
Common fields for all loadable modules.
module_instance_t * driver_submodule
Driver's submodule.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
#define talloc_get_type_abort_const
static const char * names[8]
#define fr_time_delta_ispos(_a)
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
int trunk_connection_pop_cancellation(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a cancellation request off a connection's cancellation queue.
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
void trunk_request_signal_cancel_complete(trunk_request_t *treq)
Signal that a remote server acked our cancellation.
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
void trunk_request_signal_sent(trunk_request_t *treq)
Signal that the request was written to a connection successfully.
Associates request queues with a connection.
#define TRUNK_CONN_PROCESSING
States where the connection may be processing requests.
#define TRUNK_NOTIFY_FUNC(_name, _type)
Helper macro for building generic trunk notify callback.
trunk_cancel_reason_t
Reasons for a request being cancelled.
@ TRUNK_CANCEL_REASON_SIGNAL
Request cancelled due to a signal.
@ TRUNK_REQUEST_STATE_REAPABLE
Request has been written, needs to persist, but we are not currently waiting for any response.
@ TRUNK_REQUEST_STATE_COMPLETE
The request is complete.
@ TRUNK_REQUEST_STATE_SENT
Was written to a socket. Waiting for a response.
static fr_event_list_t * el
static size_t char fr_sbuff_t size_t inlen
static size_t char ** out