2 * sql_postgresql.c Postgresql rlm_sql driver
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
21 * Copyright 2000 Mike Machado <mike@innercite.com>
22 * Copyright 2000 Alan DeKok <aland@ox.org>
28 * Use blocking queries and delete unused functions. In
29 * rlm_sql_postgresql replace all functions that are not really used
30 * with the not_implemented function.
32 * Add a new field to the rlm_sql_postgres_conn_t struct to store the
33 * number of rows affected by a query because the sql module calls
34 * finish_query before it retrieves the number of affected rows from the
37 * Bernhard Herzog <bh@intevation.de>
42 #include <freeradius-devel/radiusd.h>
43 #include <freeradius-devel/rad_assert.h>
48 #include <postgres_ext.h>
52 #include "sql_postgresql.h"
55 # define NAMEDATALEN 64
58 typedef struct rlm_sql_postgres_config {
59 char const *db_string;
60 bool send_application_name;
61 } rlm_sql_postgres_config_t;
63 typedef struct rlm_sql_postgres_conn {
70 } rlm_sql_postgres_conn_t;
72 static CONF_PARSER driver_config[] = {
73 { "send_application_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_postgres_config_t, send_application_name), "no" },
74 CONF_PARSER_TERMINATOR
77 static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
79 #if defined(HAVE_OPENSSL_CRYPTO_H) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
80 static bool ssl_init = false;
83 rlm_sql_postgres_config_t *driver;
84 char application_name[NAMEDATALEN];
87 #if defined(HAVE_OPENSSL_CRYPTO_H) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
89 # ifdef HAVE_PQINITOPENSSL
98 MEM(driver = config->driver = talloc_zero(config, rlm_sql_postgres_config_t));
99 if (cf_section_parse(conf, driver, driver_config) < 0) {
104 * Allow the user to set their own, or disable it
106 if (driver->send_application_name) {
110 cs = cf_item_parent(cf_section_to_item(conf));
112 name = cf_section_name2(cs);
113 if (!name) name = cf_section_name1(cs);
115 snprintf(application_name, sizeof(application_name),
116 "FreeRADIUS " RADIUSD_VERSION_STRING " - %s (%s)", progname, name);
120 * Old style database name
122 * Append options if they were set in the config
124 if (!strchr(config->sql_db, '=')) {
125 db_string = talloc_typed_asprintf(driver, "dbname='%s'", config->sql_db);
127 if (config->sql_server[0] != '\0') {
128 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
131 if (config->sql_port) {
132 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
135 if (config->sql_login[0] != '\0') {
136 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
139 if (config->sql_password[0] != '\0') {
140 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
143 if (driver->send_application_name) {
144 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
148 * New style parameter string
150 * Only append options when not already present
153 db_string = talloc_typed_strdup(driver, config->sql_db);
155 if ((config->sql_server[0] != '\0') && !strstr(db_string, "host=")) {
156 db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
159 if (config->sql_port && !strstr(db_string, "port=")) {
160 db_string = talloc_asprintf_append(db_string, " port=%i", config->sql_port);
163 if ((config->sql_login[0] != '\0') && !strstr(db_string, "user=")) {
164 db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
167 if ((config->sql_password[0] != '\0') && !strstr(db_string, "password=")) {
168 db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
171 if (driver->send_application_name && !strstr(db_string, "application_name=")) {
172 db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
175 driver->db_string = db_string;
180 /** Return the number of affected rows of the result as an int instead of the string that postgresql provides
183 static int affected_rows(PGresult * result)
185 return atoi(PQcmdTuples(result));
188 /** Free the row of the current result that's stored in the conn struct
191 static void free_result_row(rlm_sql_postgres_conn_t *conn)
193 TALLOC_FREE(conn->row);
194 conn->num_fields = 0;
197 #if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
198 static sql_rcode_t sql_classify_error(PGresult const *result)
206 * Check the error code to see if we should reconnect or not
207 * Error Code table taken from:
208 * http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html
210 errorcode = PQresultErrorField(result, PG_DIAG_SQLSTATE);
211 errormsg = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
213 ERROR("rlm_sql_postgresql: Error occurred, but unable to retrieve error code");
214 return RLM_SQL_ERROR;
217 /* SUCCESSFUL COMPLETION */
218 if (strcmp("00000", errorcode) == 0) {
223 if (strcmp("01000", errorcode) == 0) {
224 WARN("%s", errormsg);
228 /* UNIQUE VIOLATION */
229 if (strcmp("23505", errorcode) == 0) {
230 return RLM_SQL_ALT_QUERY;
234 for (i = 0; errorcodes[i].errorcode != NULL; i++) {
235 if (strcmp(errorcodes[i].errorcode, errorcode) == 0) {
236 ERROR("rlm_sql_postgresql: %s: %s", errorcode, errorcodes[i].meaning);
238 return (errorcodes[i].reconnect == true) ?
244 ERROR("rlm_sql_postgresql: Can't classify: %s", errorcode);
245 return RLM_SQL_ERROR;
248 static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
250 ERROR("rlm_sql_postgresql: Error occurred, no more information available, rebuild with newer libpq");
251 return RLM_SQL_ERROR;
255 static int _sql_socket_destructor(rlm_sql_postgres_conn_t *conn)
257 DEBUG2("rlm_sql_postgresql: Socket destructor called, closing socket");
259 if (!conn->db) return 0;
261 /* PQfinish also frees the memory used by the PGconn structure */
267 static int CC_HINT(nonnull) sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
269 rlm_sql_postgres_config_t *driver = config->driver;
270 rlm_sql_postgres_conn_t *conn;
272 MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_postgres_conn_t));
273 talloc_set_destructor(conn, _sql_socket_destructor);
275 DEBUG2("rlm_sql_postgresql: Connecting using parameters: %s", driver->db_string);
276 conn->db = PQconnectdb(driver->db_string);
278 ERROR("rlm_sql_postgresql: Connection failed: Out of memory");
281 if (PQstatus(conn->db) != CONNECTION_OK) {
282 ERROR("rlm_sql_postgresql: Connection failed: %s", PQerrorMessage(conn->db));
288 DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
289 PQdb(conn->db), PQhost(conn->db), PQserverVersion(conn->db), PQprotocolVersion(conn->db),
290 PQbackendPID(conn->db));
295 static CC_HINT(nonnull) sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config,
298 rlm_sql_postgres_conn_t *conn = handle->conn;
299 ExecStatusType status;
303 ERROR("rlm_sql_postgresql: Socket not connected");
304 return RLM_SQL_RECONNECT;
308 * Returns a PGresult pointer or possibly a null pointer.
309 * A non-null pointer will generally be returned except in
310 * out-of-memory conditions or serious errors such as inability
311 * to send the command to the server. If a null pointer is
312 * returned, it should be treated like a PGRES_FATAL_ERROR
315 conn->result = PQexec(conn->db, query);
318 * As this error COULD be a connection error OR an out-of-memory
319 * condition return value WILL be wrong SOME of the time
320 * regardless! Pick your poison...
323 ERROR("rlm_sql_postgresql: Failed getting query result: %s", PQerrorMessage(conn->db));
324 return RLM_SQL_RECONNECT;
327 status = PQresultStatus(conn->result);
328 DEBUG("rlm_sql_postgresql: Status: %s", PQresStatus(status));
332 * Successful completion of a command returning no data.
334 case PGRES_COMMAND_OK:
336 * Affected_rows function only returns the number of affected rows of a command
337 * returning no data...
339 conn->affected_rows = affected_rows(conn->result);
340 DEBUG("rlm_sql_postgresql: query affected rows = %i", conn->affected_rows);
343 * Successful completion of a command returning data (such as a SELECT or SHOW).
345 #ifdef HAVE_PGRES_SINGLE_TUPLE
346 case PGRES_SINGLE_TUPLE:
348 case PGRES_TUPLES_OK:
350 conn->affected_rows = PQntuples(conn->result);
351 numfields = PQnfields(conn->result); /*Check row storing functions..*/
352 DEBUG("rlm_sql_postgresql: query affected rows = %i , fields = %i", conn->affected_rows, numfields);
355 #ifdef HAVE_PGRES_COPY_BOTH
356 case PGRES_COPY_BOTH:
360 DEBUG("rlm_sql_postgresql: Data transfer started");
364 * Weird.. this shouldn't happen.
366 case PGRES_EMPTY_QUERY:
367 ERROR("rlm_sql_postgresql: Empty query");
368 return RLM_SQL_QUERY_INVALID;
371 * The server's response was not understood.
373 case PGRES_BAD_RESPONSE:
374 ERROR("rlm_sql_postgresql: Bad Response From Server");
375 return RLM_SQL_RECONNECT;
378 case PGRES_NONFATAL_ERROR:
379 case PGRES_FATAL_ERROR:
380 return sql_classify_error(conn->result);
383 return RLM_SQL_ERROR;
386 static sql_rcode_t sql_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config, char const *query)
388 return sql_query(handle, config, query);
391 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
393 rlm_sql_postgres_conn_t *conn = handle->conn;
398 fields = PQnfields(conn->result);
399 if (fields <= 0) return RLM_SQL_ERROR;
401 MEM(names = talloc_zero_array(handle, char const *, fields + 1));
403 for (i = 0; i < fields; i++) names[i] = PQfname(conn->result, i);
409 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
413 rlm_sql_postgres_conn_t *conn = handle->conn;
417 if (conn->cur_row >= PQntuples(conn->result))
420 free_result_row(conn);
422 records = PQnfields(conn->result);
423 conn->num_fields = records;
425 if ((PQntuples(conn->result) > 0) && (records > 0)) {
426 conn->row = talloc_zero_array(conn, char *, records + 1);
427 for (i = 0; i < records; i++) {
428 len = PQgetlength(conn->result, conn->cur_row, i);
429 conn->row[i] = talloc_array(conn->row, char, len + 1);
430 strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row, i), len + 1);
433 handle->row = conn->row;
439 static int sql_num_fields(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
441 rlm_sql_postgres_conn_t *conn = handle->conn;
443 conn->affected_rows = PQntuples(conn->result);
445 return PQnfields(conn->result);
450 static sql_rcode_t sql_free_result(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
452 rlm_sql_postgres_conn_t *conn = handle->conn;
454 if (conn->result != NULL) {
455 PQclear(conn->result);
459 free_result_row(conn);
464 /** Retrieves any errors associated with the connection handle
466 * @note Caller will free any memory allocated in ctx.
468 * @param ctx to allocate temporary error buffers in.
469 * @param out Array of sql_log_entrys to fill.
470 * @param outlen Length of out array.
471 * @param handle rlm_sql connection handle.
472 * @param config rlm_sql config.
473 * @return number of errors written to the sql_log_entry array.
475 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
476 rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
478 rlm_sql_postgres_conn_t *conn = handle->conn;
482 rad_assert(outlen > 0);
484 p = PQerrorMessage(conn->db);
485 while ((q = strchr(p, '\n'))) {
487 out[i].msg = talloc_asprintf(ctx, "%.*s", (int) (q - p), p);
489 if (++i == outlen) return outlen;
500 static int sql_affected_rows(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
502 rlm_sql_postgres_conn_t *conn = handle->conn;
504 return conn->affected_rows;
507 /* Exported to rlm_sql */
508 extern rlm_sql_module_t rlm_sql_postgresql;
509 rlm_sql_module_t rlm_sql_postgresql = {
510 .name = "rlm_sql_postgresql",
511 // .flags = RLM_SQL_RCODE_FLAGS_ALT_QUERY, /* Needs more testing */
512 .mod_instantiate = mod_instantiate,
513 .sql_socket_init = sql_socket_init,
514 .sql_query = sql_query,
515 .sql_select_query = sql_select_query,
516 .sql_num_fields = sql_num_fields,
517 .sql_fields = sql_fields,
518 .sql_fetch_row = sql_fetch_row,
519 .sql_error = sql_error,
520 .sql_finish_query = sql_free_result,
521 .sql_finish_select_query = sql_free_result,
522 .sql_affected_rows = sql_affected_rows