Skip to content

Commit

Permalink
Cleanup error handling in PostgreSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
arr2036 committed Feb 20, 2014
1 parent 3c7e7fa commit 569e15b
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 383 deletions.
258 changes: 121 additions & 137 deletions src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c
Expand Up @@ -57,25 +57,18 @@ typedef struct rlm_sql_postgres_conn {
char **row;
} rlm_sql_postgres_conn_t;

/* Internal function. Return true if the postgresql status value
* indicates successful completion of the query. Return false otherwise
static int
status_is_ok(ExecStatusType status)
{
return status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK;
}
*/


/* Internal function. Return the number of affected rows of the result
* as an int instead of the string that postgresql provides */
/** Return the number of affected rows of the result as an int instead of the string that postgresql provides
*
*/
static int affected_rows(PGresult * result)
{
return atoi(PQcmdTuples(result));
}

/* Internal function. Free the row of the current result that's stored
* in the conn struct. */
/** Free the row of the current result that's stored in the conn struct
*
*/
static void free_result_row(rlm_sql_postgres_conn_t *conn)
{
int i;
Expand All @@ -91,41 +84,63 @@ static void free_result_row(rlm_sql_postgres_conn_t *conn)
}
}


/*************************************************************************
* Function: check_fatal_error
*
* Purpose: Check error type and behave accordingly
*
*************************************************************************/

static int check_fatal_error (char *errorcode)
#if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
static sql_rcode_t sql_classify_error(PGresult const *result)
{
int x = 0;
int i;

char *errorcode;
char *errormsg;

/*
Check the error code to see if we should reconnect or not
Error Code table taken from
http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html
*/

if (!errorcode) return -1;

while(errorcodes[x].errorcode != NULL){
if (strcmp(errorcodes[x].errorcode, errorcode) == 0){
DEBUG("rlm_sql_postgresql: Postgresql Fatal Error: [%s: %s] Occurred!!", errorcode, errorcodes[x].meaning);
if (errorcodes[x].shouldreconnect == 1)
return RLM_SQL_RECONNECT;
else
return -1;
* Check the error code to see if we should reconnect or not
* Error Code table taken from:
* http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html
*/
errorcode = PQresultErrorField(result, PG_DIAG_SQLSTATE);
errormsg = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
if (!errorcode) {
ERROR("rlm_sql_postgresql: Error occurred, but unable to retrieve error code");
return RLM_SQL_ERROR;
}

/* SUCCESSFUL COMPLETION */
if (strcmp("00000", errorcode) == 0) {
return RLM_SQL_OK;
}

/* WARNING */
if (strcmp("01000", errorcode) == 0) {
WARN("%s", errormsg);
return RLM_SQL_OK;
}

/* UNIQUE VIOLATION */
if (strcmp("23505", errorcode) == 0) {
return RLM_SQL_DUPLICATE;
}

/* others */
for (i = 0; errorcodes[i].errorcode != NULL; i++) {
if (strcmp(errorcodes[i].errorcode, errorcode) == 0) {
ERROR("rlm_sql_postgresql: %s: %s", errorcode, errorcodes[i].meaning);

return (errorcodes[i].reconnect == true) ?
RLM_SQL_RECONNECT :
RLM_SQL_ERROR;
}
x++;
}

DEBUG("rlm_sql_postgresql: Postgresql Fatal Error: [%s] Occurred!!", errorcode);
/* We don't seem to have a matching error class/code */
return -1;
ERROR("rlm_sql_postgresql: Can't classify: %s", errorcode);
return RLM_SQL_ERROR;
}
# else
static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
{
ERROR("rlm_sql_postgresql: Error occurred, no more information available, rebuild with newer libpq");
return RLM_SQL_ERROR;
}
#endif

static int _sql_socket_destructor(rlm_sql_postgres_conn_t *conn)
{
Expand Down Expand Up @@ -208,118 +223,87 @@ static int sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
static sql_rcode_t sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config, char const *query)
{
rlm_sql_postgres_conn_t *conn = handle->conn;
ExecStatusType status;
int numfields = 0;
char *errorcode;
char *errormsg;

if (!conn->db) {
ERROR("rlm_sql_postgresql: Socket not connected");
return RLM_SQL_RECONNECT;
}

/*
* Returns a PGresult pointer or possibly a null pointer.
* A non-null pointer will generally be returned except in
* out-of-memory conditions or serious errors such as inability
* to send the command to the server. If a null pointer is
* returned, it should be treated like a PGRES_FATAL_ERROR
* result.
*/
conn->result = PQexec(conn->db, query);
/*
* Returns a PGresult pointer or possibly a null pointer.
* A non-null pointer will generally be returned except in
* out-of-memory conditions or serious errors such as inability
* to send the command to the server. If a null pointer is
* returned, it should be treated like a PGRES_FATAL_ERROR
* result.
*/
if (!conn->result) {
ERROR("rlm_sql_postgresql: PostgreSQL Query failed Error: %s",
PQerrorMessage(conn->db));
/* As this error COULD be a connection error OR an out-of-memory
* condition return value WILL be wrong SOME of the time regardless!
* Pick your poison....
*/
return RLM_SQL_RECONNECT;
} else {
ExecStatusType status = PQresultStatus(conn->result);
DEBUG("rlm_sql_postgresql: Status: %s", PQresStatus(status));

switch (status){

case PGRES_COMMAND_OK:
/*Successful completion of a command returning no data.*/

/*affected_rows function only returns
the number of affected rows of a command
returning no data...
*/
conn->affected_rows = affected_rows(conn->result);
DEBUG("rlm_sql_postgresql: query affected rows = %i", conn->affected_rows);
return 0;

break;

case PGRES_TUPLES_OK:
/*Successful completion of a command returning data (such as a SELECT or SHOW).*/

conn->cur_row = 0;
conn->affected_rows = PQntuples(conn->result);
numfields = PQnfields(conn->result); /*Check row storing functions..*/
DEBUG("rlm_sql_postgresql: query affected rows = %i , fields = %i", conn->affected_rows, numfields);
return 0;

break;

case PGRES_BAD_RESPONSE:
/*The server's response was not understood.*/
DEBUG("rlm_sql_postgresql: Bad Response From Server!!");
return -1;

break;

case PGRES_NONFATAL_ERROR:
/*A nonfatal error (a notice or warning) occurred. Possibly never returns*/

return -1;

break;

case PGRES_FATAL_ERROR:
#if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
/*A fatal error occurred.*/

errorcode = PQresultErrorField(conn->result, PG_DIAG_SQLSTATE);
errormsg = PQresultErrorField(conn->result, PG_DIAG_MESSAGE_PRIMARY);
DEBUG("rlm_sql_postgresql: Error %s", errormsg);
return check_fatal_error(errorcode);
#endif

break;

default:
/* FIXME: An unhandled error occurred.*/
/*
* As this error COULD be a connection error OR an out-of-memory
* condition return value WILL be wrong SOME of the time
* regardless! Pick your poison...
*/
if (!conn->result) {
ERROR("rlm_sql_postgresql: Failed getting query result: %s", PQerrorMessage(conn->db));
return RLM_SQL_RECONNECT;
}

/* PGRES_EMPTY_QUERY PGRES_COPY_OUT PGRES_COPY_IN */
status = PQresultStatus(conn->result);
DEBUG("rlm_sql_postgresql: Status: %s", PQresStatus(status));

return -1;
switch (status){
/*
* Successful completion of a command returning no data.
*/
case PGRES_COMMAND_OK:
/*
* Affected_rows function only returns the number of affected rows of a command
* returning no data...
*/
conn->affected_rows = affected_rows(conn->result);
DEBUG("rlm_sql_postgresql: query affected rows = %i", conn->affected_rows);
return RLM_SQL_OK;
/*
* Successful completion of a command returning data (such as a SELECT or SHOW).
*/
case PGRES_SINGLE_TUPLE:
case PGRES_TUPLES_OK:
conn->cur_row = 0;
conn->affected_rows = PQntuples(conn->result);
numfields = PQnfields(conn->result); /*Check row storing functions..*/
DEBUG("rlm_sql_postgresql: query affected rows = %i , fields = %i", conn->affected_rows, numfields);
return RLM_SQL_OK;

case PGRES_COPY_BOTH:
case PGRES_COPY_OUT:
case PGRES_COPY_IN:
DEBUG("rlm_sql_postgresql: Data transfer started");
return RLM_SQL_OK;

break;
/*
* Weird.. this shouldn't happen.
*/
case PGRES_EMPTY_QUERY:
ERROR("rlm_sql_postgresql: Empty query");
return RLM_SQL_QUERY_ERROR;

/*
* The server's response was not understood.
*/
case PGRES_BAD_RESPONSE:
ERROR("rlm_sql_postgresql: Bad Response From Server");
return RLM_SQL_RECONNECT;

}

/*
Note to self ... sql_store_result returns 0 anyway
after setting the handle->affected_rows..
sql_num_fields returns 0 at worst case which means the check below
has a really small chance to return false..
lets remove it then .. yuck!!
*/
/*
} else {
if ((sql_store_result(handle, config) == 0)
&& (sql_num_fields(handle, config) >= 0))
return 0;
else
return -1;
}
*/
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
return sql_classify_error(conn->result);
}
return -1;

return RLM_SQL_ERROR;
}


Expand Down

0 comments on commit 569e15b

Please sign in to comment.