diff --git a/src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c b/src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c index 6e9a2ea4f671..6c860b79375c 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c @@ -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; @@ -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) { @@ -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; } diff --git a/src/modules/rlm_sql/drivers/rlm_sql_postgresql/sql_postgresql.h b/src/modules/rlm_sql/drivers/rlm_sql_postgresql/sql_postgresql.h index f8885c8d2ffc..29ebf7f37fba 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_postgresql/sql_postgresql.h +++ b/src/modules/rlm_sql/drivers/rlm_sql_postgresql/sql_postgresql.h @@ -5,234 +5,233 @@ RCSIDH(sql_postgresql_h, "$Id$") -/************************************************** -* Error Codes and required information Lookup table -* Does this shite ever needed? Lets c.. -***************************************************/ +/** Error Codes and required information + * + */ typedef struct pgsql_error{ - char const *errorcode; - char const *meaning; - int shouldreconnect; -}pgerror; + char const *errorcode; //!< 5 char error code from PG_DIAG_SQLSTATE. + char const *meaning; //!< Verbose description. + bool reconnect; //!< Should reconnect socket when receiving this error. +} pgerror; pgerror errorcodes[]= { - { "1000", "WARNING", 0, }, - { "0100C", "DYNAMIC RESULT SETS RETURNED", 0, }, - { "1008", "IMPLICIT ZERO BIT PADDING", 0, }, - { "1003", "NULL VALUE ELIMINATED IN SET FUNCTION", 0, }, - { "1007", "PRIVILEGE NOT GRANTED", 0, }, - { "1006", "PRIVILEGE NOT REVOKED", 0, }, - { "1004", "STRING DATA RIGHT TRUNCATION", 0, }, - { "01P01", "DEPRECATED FEATURE", 0, }, - - { "2000", "NO DATA", 0, }, - { "2001", "NO ADDITIONAL DYNAMIC RESULT SETS RETURNED", 0, }, - - { "3000", "SQL STATEMENT NOT YET COMPLETE", 0, }, - - { "8000", "CONNECTION EXCEPTION", 0, }, - { "8003", "CONNECTION DOES NOT EXIST", 0, }, - { "8006", "CONNECTION FAILURE", 0, }, - { "8001", "SQLCLIENT UNABLE TO ESTABLISH SQLCONNECTION", 0, }, - { "8004", "SQLSERVER REJECTED ESTABLISHMENT OF SQLCONNECTION", 0, }, - { "8007", "TRANSACTION RESOLUTION UNKNOWN", 0, }, - { "08P01", "PROTOCOL VIOLATION", 0, }, - - { "9000", "TRIGGERED ACTION EXCEPTION", 0, }, - - { "0A000", "FEATURE NOT SUPPORTED", 0, }, - - { "0B000", "INVALID TRANSACTION INITIATION", 0, }, - - { "0F000", "LOCATOR EXCEPTION", 0, }, - { "0F001", "INVALID LOCATOR SPECIFICATION", 0, }, - - { "0L000", "INVALID GRANTOR", 0, }, - { "0LP01", "INVALID GRANT OPERATION", 0, }, - - { "21000", "CARDINALITY VIOLATION", 0, }, - - { "22000", "DATA EXCEPTION", 0, }, - { "2202E", "ARRAY SUBSCRIPT ERROR", 0, }, - { "22021", "CHARACTER NOT IN REPERTOIRE", 0, }, - { "22008", "DATETIME FIELD OVERFLOW", 0, }, - { "22012", "DIVISION BY ZERO", 0, }, - { "22005", "ERROR IN ASSIGNMENT", 0, }, - { "2200B", "ESCAPE CHARACTER CONFLICT", 0, }, - { "22022", "INDICATOR OVERFLOW", 0, }, - { "22015", "INTERVAL FIELD OVERFLOW", 0, }, - { "2201E", "INVALID ARGUMENT FOR LOGARITHM", 0, }, - { "2201F", "INVALID ARGUMENT FOR POWER FUNCTION", 0, }, - { "2201G", "INVALID ARGUMENT FOR WIDTH BUCKET FUNCTION", 0, }, - { "22018", "INVALID CHARACTER VALUE FOR CAST", 0, }, - { "22007", "INVALID DATETIME FORMAT", 0, }, - { "22019", "INVALID ESCAPE CHARACTER", 0, }, - { "2200D", "INVALID ESCAPE OCTET", 0, }, - { "22025", "INVALID ESCAPE SEQUENCE", 0, }, - { "22P06", "NONSTANDARD USE OF ESCAPE CHARACTER", 0, }, - { "22010", "INVALID INDICATOR PARAMETER VALUE", 0, }, - { "22020", "INVALID LIMIT VALUE", 0, }, - { "22023", "INVALID PARAMETER VALUE", 0, }, - { "2201B", "INVALID REGULAR EXPRESSION", 0, }, - { "22009", "INVALID TIME ZONE DISPLACEMENT VALUE", 0, }, - { "2200C", "INVALID USE OF ESCAPE CHARACTER", 0, }, - { "2200G", "MOST SPECIFIC TYPE MISMATCH", 0, }, - { "22004", "NULL VALUE NOT ALLOWED", 0, }, - { "22002", "NULL VALUE NO INDICATOR PARAMETER", 0, }, - { "22003", "NUMERIC VALUE OUT OF RANGE", 0, }, - { "22026", "STRING DATA LENGTH MISMATCH", 0, }, - { "22001", "STRING DATA RIGHT TRUNCATION", 0, }, - { "22011", "SUBSTRING ERROR", 0, }, - { "22027", "TRIM ERROR", 0, }, - { "22024", "UNTERMINATED C STRING", 0, }, - { "2200F", "ZERO LENGTH CHARACTER STRING", 0, }, - { "22P01", "FLOATING POINT EXCEPTION", 0, }, - { "22P02", "INVALID TEXT REPRESENTATION", 0, }, - { "22P03", "INVALID BINARY REPRESENTATION", 0, }, - { "22P04", "BAD COPY FILE FORMAT", 0, }, - { "22P05", "UNTRANSLATABLE CHARACTER", 0, }, - - { "23000", "INTEGRITY CONSTRAINT VIOLATION", 0, }, - { "23001", "RESTRICT VIOLATION", 0, }, - { "23502", "NOT NULL VIOLATION", 0, }, - { "23503", "FOREIGN KEY VIOLATION", 0, }, - { "23505", "UNIQUE VIOLATION", 0, }, - { "23514", "CHECK VIOLATION", 0, }, - - { "24000", "INVALID CURSOR STATE", 0, }, - - { "25000", "INVALID TRANSACTION STATE", 0, }, - { "25001", "ACTIVE SQL TRANSACTION", 0, }, - { "25002", "BRANCH TRANSACTION ALREADY ACTIVE", 0, }, - { "25008", "HELD CURSOR REQUIRES SAME ISOLATION LEVEL", 0, }, - { "25003", "INAPPROPRIATE ACCESS MODE FOR BRANCH TRANSACTION", 0, }, - { "25004", "INAPPROPRIATE ISOLATION LEVEL FOR BRANCH TRANSACTION", 0, }, - { "25005", "NO ACTIVE SQL TRANSACTION FOR BRANCH TRANSACTION", 0, }, - { "25006", "READ ONLY SQL TRANSACTION", 0, }, - { "25007", "SCHEMA AND DATA STATEMENT MIXING NOT SUPPORTED", 0, }, - { "25P01", "NO ACTIVE SQL TRANSACTION", 0, }, - { "25P02", "IN FAILED SQL TRANSACTION", 0, }, - - { "26000", "INVALID SQL STATEMENT NAME", 0, }, - - { "27000", "TRIGGERED DATA CHANGE VIOLATION", 0, }, - - { "28000", "INVALID AUTHORIZATION SPECIFICATION", 0, }, - - { "2B000", "DEPENDENT PRIVILEGE DESCRIPTORS STILL EXIST", 0, }, - { "2BP01", "DEPENDENT OBJECTS STILL EXIST", 0, }, - - { "2D000", "INVALID TRANSACTION TERMINATION", 0, }, - - { "2F000", "SQL ROUTINE EXCEPTION", 0, }, - { "2F005", "FUNCTION EXECUTED NO RETURN STATEMENT", 0, }, - { "2F002", "MODIFYING SQL DATA NOT PERMITTED", 0, }, - { "2F003", "PROHIBITED SQL STATEMENT ATTEMPTED", 0, }, - { "2F004", "READING SQL DATA NOT PERMITTED", 0, }, - - { "34000", "INVALID CURSOR NAME", 0, }, - - { "38000", "EXTERNAL ROUTINE EXCEPTION", 0, }, - { "38001", "CONTAINING SQL NOT PERMITTED", 0, }, - { "38002", "MODIFYING SQL DATA NOT PERMITTED", 0, }, - { "38003", "PROHIBITED SQL STATEMENT ATTEMPTED", 0, }, - { "38004", "READING SQL DATA NOT PERMITTED", 0, }, - - { "39000", "EXTERNAL ROUTINE INVOCATION EXCEPTION", 0, }, - { "39001", "INVALID SQLSTATE RETURNED", 0, }, - { "39004", "NULL VALUE NOT ALLOWED", 0, }, - { "39P01", "TRIGGER PROTOCOL VIOLATED", 0, }, - { "39P02", "SRF PROTOCOL VIOLATED", 0, }, - - { "3B000", "SAVEPOINT EXCEPTION", 0, }, - { "3B001", "INVALID SAVEPOINT SPECIFICATION", 0, }, - - { "3D000", "INVALID CATALOG NAME", 0, }, - { "3F000", "INVALID SCHEMA NAME", 0, }, - - { "40000", "TRANSACTION ROLLBACK", 0, }, - { "40002", "TRANSACTION INTEGRITY CONSTRAINT VIOLATION", 0, }, - { "40001", "SERIALIZATION FAILURE", 0, }, - { "40003", "STATEMENT COMPLETION UNKNOWN", 0, }, - { "40P01", "DEADLOCK DETECTED", 0, }, - - { "44000", "WITH CHECK OPTION VIOLATION", 0, }, - - { "53000", "INSUFFICIENT RESOURCES", 0, }, - { "53100", "DISK FULL", 0, }, - { "53200", "OUT OF MEMORY", 0, }, - { "53300", "TOO MANY CONNECTIONS", 0, }, - - { "54000", "PROGRAM LIMIT EXCEEDED", 0, }, - { "54001", "STATEMENT TOO COMPLEX", 0, }, - { "54011", "TOO MANY COLUMNS", 0, }, - { "54023", "TOO MANY ARGUMENTS", 0, }, - - { "55000", "OBJECT NOT IN PREREQUISITE STATE", 0, }, - { "55006", "OBJECT IN USE", 0, }, - { "55P02", "CANT CHANGE RUNTIME PARAM", 0, }, - { "55P03", "LOCK NOT AVAILABLE", 0, }, - - { "57000", "OPERATOR INTERVENTION", 1, }, - { "57014", "QUERY CANCELED", 1, }, - { "57P01", "ADMIN SHUTDOWN", 1, }, - { "57P02", "CRASH SHUTDOWN", 1, }, - { "57P03", "CANNOT CONNECT NOW", 1, }, - - { "58030", "IO ERROR", 1, }, - { "58P01", "UNDEFINED FILE", 1, }, - { "58P02", "DUPLICATE FILE", 1, }, - - { "F0000", "CONFIG FILE ERROR", 1, }, - { "F0001", "LOCK FILE EXISTS", 1, }, - - { "P0000", "PLPGSQL ERROR", 0, }, - { "P0001", "RAISE EXCEPTION", 0, }, - - { "42000", "SYNTAX ERROR OR ACCESS RULE VIOLATION", 0, }, - { "42601", "SYNTAX ERROR", 0, }, - { "42501", "INSUFFICIENT PRIVILEGE", 0, }, - { "42846", "CANNOT COERCE", 0, }, - { "42803", "GROUPING ERROR", 0, }, - { "42830", "INVALID FOREIGN KEY", 0, }, - { "42602", "INVALID NAME", 0, }, - { "42622", "NAME TOO LONG", 0, }, - { "42939", "RESERVED NAME", 0, }, - { "42804", "DATATYPE MISMATCH", 0, }, - { "42P18", "INDETERMINATE DATATYPE", 0, }, - { "42809", "WRONG OBJECT TYPE", 0, }, - { "42703", "UNDEFINED COLUMN", 0, }, - { "42883", "UNDEFINED FUNCTION", 0, }, - { "42P01", "UNDEFINED TABLE", 0, }, - { "42P02", "UNDEFINED PARAMETER", 0, }, - { "42704", "UNDEFINED OBJECT", 0, }, - { "42701", "DUPLICATE COLUMN", 0, }, - { "42P03", "DUPLICATE CURSOR", 0, }, - { "42P04", "DUPLICATE DATABASE", 0, }, - { "42723", "DUPLICATE FUNCTION", 0, }, - { "42P05", "DUPLICATE PREPARED STATEMENT", 0, }, - { "42P06", "DUPLICATE SCHEMA", 0, }, - { "42P07", "DUPLICATE TABLE", 0, }, - { "42712", "DUPLICATE ALIAS", 0, }, - { "42710", "DUPLICATE OBJECT", 0, }, - { "42702", "AMBIGUOUS COLUMN", 0, }, - { "42725", "AMBIGUOUS FUNCTION", 0, }, - { "42P08", "AMBIGUOUS PARAMETER", 0, }, - { "42P09", "AMBIGUOUS ALIAS", 0, }, - { "42P10", "INVALID COLUMN REFERENCE", 0, }, - { "42611", "INVALID COLUMN DEFINITION", 0, }, - { "42P11", "INVALID CURSOR DEFINITION", 0, }, - { "42P12", "INVALID DATABASE DEFINITION", 0, }, - { "42P13", "INVALID FUNCTION DEFINITION", 0, }, - { "42P14", "INVALID PREPARED STATEMENT DEFINITION", 0, }, - { "42P15", "INVALID SCHEMA DEFINITION", 0, }, - { "42P16", "INVALID TABLE DEFINITION", 0, }, - { "42P17", "INVALID OBJECT DEFINITION", 0, }, - - { "XX000", "INTERNAL ERROR", 0, }, - { "XX001", "DATA CORRUPTED", 0, }, - { "XX002", "INDEX CORRUPTED", 0, }, + { "01000", "WARNING", false, }, + { "0100C", "DYNAMIC RESULT SETS RETURNED", false, }, + { "01008", "IMPLICIT ZERO BIT PADDING", false, }, + { "01003", "NULL VALUE ELIMINATED IN SET FUNCTION", false, }, + { "01007", "PRIVILEGE NOT GRANTED", false, }, + { "01006", "PRIVILEGE NOT REVOKED", false, }, + { "01004", "STRING DATA RIGHT TRUNCATION", false, }, + { "01P01", "DEPRECATED FEATURE", false, }, + + { "02000", "NO DATA", false, }, + { "02001", "NO ADDITIONAL DYNAMIC RESULT SETS RETURNED", false, }, + + { "03000", "SQL STATEMENT NOT YET COMPLETE", false, }, + + { "08000", "CONNECTION EXCEPTION", false, }, + { "08003", "CONNECTION DOES NOT EXIST", false, }, + { "08006", "CONNECTION FAILURE", false, }, + { "08001", "SQLCLIENT UNABLE TO ESTABLISH SQLCONNECTION", false, }, + { "08004", "SQLSERVER REJECTED ESTABLISHMENT OF SQLCONNECTION", false, }, + { "08007", "TRANSACTION RESOLUTION UNKNOWN", false, }, + { "08P01", "PROTOCOL VIOLATION", false, }, + + { "9000", "TRIGGERED ACTION EXCEPTION", false, }, + + { "0A000", "FEATURE NOT SUPPORTED", false, }, + + { "0B000", "INVALID TRANSACTION INITIATION", false, }, + + { "0F000", "LOCATOR EXCEPTION", false, }, + { "0F001", "INVALID LOCATOR SPECIFICATION", false, }, + + { "0L000", "INVALID GRANTOR", false, }, + { "0LP01", "INVALID GRANT OPERATION", false, }, + + { "21000", "CARDINALITY VIOLATION", false, }, + + { "22000", "DATA EXCEPTION", false, }, + { "2202E", "ARRAY SUBSCRIPT ERROR", false, }, + { "22021", "CHARACTER NOT IN REPERTOIRE", false, }, + { "22008", "DATETIME FIELD OVERFLOW", false, }, + { "22012", "DIVISION BY ZERO", false, }, + { "22005", "ERROR IN ASSIGNMENT", false, }, + { "2200B", "ESCAPE CHARACTER CONFLICT", false, }, + { "22022", "INDICATOR OVERFLOW", false, }, + { "22015", "INTERVAL FIELD OVERFLOW", false, }, + { "2201E", "INVALID ARGUMENT FOR LOGARITHM", false, }, + { "2201F", "INVALID ARGUMENT FOR POWER FUNCTION", false, }, + { "2201G", "INVALID ARGUMENT FOR WIDTH BUCKET FUNCTION", false, }, + { "22018", "INVALID CHARACTER VALUE FOR CAST", false, }, + { "22007", "INVALID DATETIME FORMAT", false, }, + { "22019", "INVALID ESCAPE CHARACTER", false, }, + { "2200D", "INVALID ESCAPE OCTET", false, }, + { "22025", "INVALID ESCAPE SEQUENCE", false, }, + { "22P06", "NONSTANDARD USE OF ESCAPE CHARACTER", false, }, + { "22010", "INVALID INDICATOR PARAMETER VALUE", false, }, + { "22020", "INVALID LIMIT VALUE", false, }, + { "22023", "INVALID PARAMETER VALUE", false, }, + { "2201B", "INVALID REGULAR EXPRESSION", false, }, + { "22009", "INVALID TIME ZONE DISPLACEMENT VALUE", false, }, + { "2200C", "INVALID USE OF ESCAPE CHARACTER", false, }, + { "2200G", "MOST SPECIFIC TYPE MISMATCH", false, }, + { "22004", "NULL VALUE NOT ALLOWED", false, }, + { "22002", "NULL VALUE NO INDICATOR PARAMETER", false, }, + { "22003", "NUMERIC VALUE OUT OF RANGE", false, }, + { "22026", "STRING DATA LENGTH MISMATCH", false, }, + { "22001", "STRING DATA RIGHT TRUNCATION", false, }, + { "22011", "SUBSTRING ERROR", false, }, + { "22027", "TRIM ERROR", false, }, + { "22024", "UNTERMINATED C STRING", false, }, + { "2200F", "ZERO LENGTH CHARACTER STRING", false, }, + { "22P01", "FLOATING POINT EXCEPTION", false, }, + { "22P02", "INVALID TEXT REPRESENTATION", false, }, + { "22P03", "INVALID BINARY REPRESENTATION", false, }, + { "22P04", "BAD COPY FILE FORMAT", false, }, + { "22P05", "UNTRANSLATABLE CHARACTER", false, }, + + { "23000", "INTEGRITY CONSTRAINT VIOLATION", false, }, + { "23001", "RESTRICT VIOLATION", false, }, + { "23502", "NOT NULL VIOLATION", false, }, + { "23503", "FOREIGN KEY VIOLATION", false, }, + { "23505", "UNIQUE VIOLATION", false, }, + { "23514", "CHECK VIOLATION", false, }, + + { "24000", "INVALID CURSOR STATE", false, }, + + { "25000", "INVALID TRANSACTION STATE", false, }, + { "25001", "ACTIVE SQL TRANSACTION", false, }, + { "25002", "BRANCH TRANSACTION ALREADY ACTIVE", false, }, + { "25008", "HELD CURSOR REQUIRES SAME ISOLATION LEVEL", false, }, + { "25003", "INAPPROPRIATE ACCESS MODE FOR BRANCH TRANSACTION", false, }, + { "25004", "INAPPROPRIATE ISOLATION LEVEL FOR BRANCH TRANSACTION", false, }, + { "25005", "NO ACTIVE SQL TRANSACTION FOR BRANCH TRANSACTION", false, }, + { "25006", "READ ONLY SQL TRANSACTION", false, }, + { "25007", "SCHEMA AND DATA STATEMENT MIXING NOT SUPPORTED", false, }, + { "25P01", "NO ACTIVE SQL TRANSACTION", false, }, + { "25P02", "IN FAILED SQL TRANSACTION", false, }, + + { "26000", "INVALID SQL STATEMENT NAME", false, }, + + { "27000", "TRIGGERED DATA CHANGE VIOLATION", false, }, + + { "28000", "INVALID AUTHORIZATION SPECIFICATION", false, }, + + { "2B000", "DEPENDENT PRIVILEGE DESCRIPTORS STILL EXIST", false, }, + { "2BP01", "DEPENDENT OBJECTS STILL EXIST", false, }, + + { "2D000", "INVALID TRANSACTION TERMINATION", false, }, + + { "2F000", "SQL ROUTINE EXCEPTION", false, }, + { "2F005", "FUNCTION EXECUTED NO RETURN STATEMENT", false, }, + { "2F002", "MODIFYING SQL DATA NOT PERMITTED", false, }, + { "2F003", "PROHIBITED SQL STATEMENT ATTEMPTED", false, }, + { "2F004", "READING SQL DATA NOT PERMITTED", false, }, + + { "34000", "INVALID CURSOR NAME", false, }, + + { "38000", "EXTERNAL ROUTINE EXCEPTION", false, }, + { "38001", "CONTAINING SQL NOT PERMITTED", false, }, + { "38002", "MODIFYING SQL DATA NOT PERMITTED", false, }, + { "38003", "PROHIBITED SQL STATEMENT ATTEMPTED", false, }, + { "38004", "READING SQL DATA NOT PERMITTED", false, }, + + { "39000", "EXTERNAL ROUTINE INVOCATION EXCEPTION", false, }, + { "39001", "INVALID SQLSTATE RETURNED", false, }, + { "39004", "NULL VALUE NOT ALLOWED", false, }, + { "39P01", "TRIGGER PROTOCOL VIOLATED", false, }, + { "39P02", "SRF PROTOCOL VIOLATED", false, }, + + { "3B000", "SAVEPOINT EXCEPTION", false, }, + { "3B001", "INVALID SAVEPOINT SPECIFICATION", false, }, + + { "3D000", "INVALID CATALOG NAME", false, }, + { "3F000", "INVALID SCHEMA NAME", false, }, + + { "40000", "TRANSACTION ROLLBACK", false, }, + { "40002", "TRANSACTION INTEGRITY CONSTRAINT VIOLATION", false, }, + { "40001", "SERIALIZATION FAILURE", false, }, + { "40003", "STATEMENT COMPLETION UNKNOWN", false, }, + { "40P01", "DEADLOCK DETECTED", false, }, + + { "44000", "WITH CHECK OPTION VIOLATION", false, }, + + { "53000", "INSUFFICIENT RESOURCES", false, }, + { "53100", "DISK FULL", false, }, + { "53200", "OUT OF MEMORY", false, }, + { "53300", "TOO MANY CONNECTIONS", false, }, + + { "54000", "PROGRAM LIMIT EXCEEDED", false, }, + { "54001", "STATEMENT TOO COMPLEX", false, }, + { "54011", "TOO MANY COLUMNS", false, }, + { "54023", "TOO MANY ARGUMENTS", false, }, + + { "55000", "OBJECT NOT IN PREREQUISITE STATE", false, }, + { "55006", "OBJECT IN USE", false, }, + { "55P02", "CANT CHANGE RUNTIME PARAM", false, }, + { "55P03", "LOCK NOT AVAILABLE", false, }, + + { "57000", "OPERATOR INTERVENTION", true, }, + { "57014", "QUERY CANCELED", true, }, + { "57P01", "ADMIN SHUTDOWN", true, }, + { "57P02", "CRASH SHUTDOWN", true, }, + { "57P03", "CANNOT CONNECT NOW", true, }, + + { "58030", "IO ERROR", true, }, + { "58P01", "UNDEFINED FILE", true, }, + { "58P02", "DUPLICATE FILE", true, }, + + { "F0000", "CONFIG FILE ERROR", true, }, + { "F0001", "LOCK FILE EXISTS", true, }, + + { "P0000", "PLPGSQL ERROR", false, }, + { "P0001", "RAISE EXCEPTION", false, }, + + { "42000", "SYNTAX ERROR OR ACCESS RULE VIOLATION", false, }, + { "42601", "SYNTAX ERROR", false, }, + { "42501", "INSUFFICIENT PRIVILEGE", false, }, + { "42846", "CANNOT COERCE", false, }, + { "42803", "GROUPING ERROR", false, }, + { "42830", "INVALID FOREIGN KEY", false, }, + { "42602", "INVALID NAME", false, }, + { "42622", "NAME TOO LONG", false, }, + { "42939", "RESERVED NAME", false, }, + { "42804", "DATATYPE MISMATCH", false, }, + { "42P18", "INDETERMINATE DATATYPE", false, }, + { "42809", "WRONG OBJECT TYPE", false, }, + { "42703", "UNDEFINED COLUMN", false, }, + { "42883", "UNDEFINED FUNCTION", false, }, + { "42P01", "UNDEFINED TABLE", false, }, + { "42P02", "UNDEFINED PARAMETER", false, }, + { "42704", "UNDEFINED OBJECT", false, }, + { "42701", "DUPLICATE COLUMN", false, }, + { "42P03", "DUPLICATE CURSOR", false, }, + { "42P04", "DUPLICATE DATABASE", false, }, + { "42723", "DUPLICATE FUNCTION", false, }, + { "42P05", "DUPLICATE PREPARED STATEMENT", false, }, + { "42P06", "DUPLICATE SCHEMA", false, }, + { "42P07", "DUPLICATE TABLE", false, }, + { "42712", "DUPLICATE ALIAS", false, }, + { "42710", "DUPLICATE OBJECT", false, }, + { "42702", "AMBIGUOUS COLUMN", false, }, + { "42725", "AMBIGUOUS FUNCTION", false, }, + { "42P08", "AMBIGUOUS PARAMETER", false, }, + { "42P09", "AMBIGUOUS ALIAS", false, }, + { "42P10", "INVALID COLUMN REFERENCE", false, }, + { "42611", "INVALID COLUMN DEFINITION", false, }, + { "42P11", "INVALID CURSOR DEFINITION", false, }, + { "42P12", "INVALID DATABASE DEFINITION", false, }, + { "42P13", "INVALID FUNCTION DEFINITION", false, }, + { "42P14", "INVALID PREPARED STATEMENT DEFINITION", false, }, + { "42P15", "INVALID SCHEMA DEFINITION", false, }, + { "42P16", "INVALID TABLE DEFINITION", false, }, + { "42P17", "INVALID OBJECT DEFINITION", false, }, + + { "XX000", "INTERNAL ERROR", false, }, + { "XX001", "DATA CORRUPTED", false, }, + { "XX002", "INDEX CORRUPTED", false, }, { NULL, NULL, 0 } }; diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index 314e9589f074..b882a64c94e7 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -1253,11 +1253,14 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. + * + * @fixme We should actually be able to distinguish between key + * constraint violations (which we expect) and other errors. */ - if (sql_ret == 0) { + if (sql_ret == RLM_SQL_OK) { numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected > 0) { - break; + break; /* A query succeeded, were done! */ } RDEBUG("No records updated"); diff --git a/src/modules/rlm_sql/rlm_sql.h b/src/modules/rlm_sql/rlm_sql.h index 3242f7ad5d55..d4ca307bf7ef 100644 --- a/src/modules/rlm_sql/rlm_sql.h +++ b/src/modules/rlm_sql/rlm_sql.h @@ -20,10 +20,11 @@ RCSIDH(rlm_sql_h, "$Id$") /* SQL Errors */ typedef enum { - RLM_SQL_QUERY_ERROR = -3, - RLM_SQL_ERROR = -2, - RLM_SQL_OK = 0, - RLM_SQL_RECONNECT = 1 + RLM_SQL_QUERY_ERROR = -3, //!< Query syntax error + RLM_SQL_ERROR = -2, //!< General connection/server error + RLM_SQL_OK = 0, //!< Success + RLM_SQL_RECONNECT = 1, //!< Stale connection, should reconnect + RLM_SQL_DUPLICATE = 2 //!< Key constraint violation } sql_rcode_t; typedef char **rlm_sql_row_t; diff --git a/src/modules/rlm_sql/sql.c b/src/modules/rlm_sql/sql.c index 45a6d2104ea3..6c48a9717856 100644 --- a/src/modules/rlm_sql/sql.c +++ b/src/modules/rlm_sql/sql.c @@ -300,11 +300,11 @@ int rlm_sql_fetch_row(rlm_sql_handle_t **handle, rlm_sql_t *inst) return ret; } -static void rlm_sql_query_error(rlm_sql_handle_t **handle, rlm_sql_t *inst) +static void rlm_sql_query_error(rlm_sql_handle_t *handle, rlm_sql_t *inst) { char const *p, *q; - p = (inst->module->sql_error)(*handle, inst->config); + p = (inst->module->sql_error)(handle, inst->config); if (!p) { ERROR("rlm_sql (%s): Unknown query error", inst->config->xlat_name); return; @@ -327,6 +327,32 @@ static void rlm_sql_query_error(rlm_sql_handle_t **handle, rlm_sql_t *inst) } } +static void rlm_sql_query_debug(rlm_sql_handle_t *handle, rlm_sql_t *inst) +{ + char const *p, *q; + + p = (inst->module->sql_error)(handle, inst->config); + if (!p) { + return; + } + + /* + * Some drivers are nice and provide us with a ^ pointer to + * the place in the query string where the error occurred. + * + * For this to be useful we need to split log messages on + * \n and output each of the lines individually. + */ + while ((q = strchr(p, '\n'))) { + DEBUG2("rlm_sql (%s): %.*s", inst->config->xlat_name, (int) (q - p), p); + p = q + 1; + } + + if (*p != '\0') { + DEBUG2("rlm_sql (%s): %s", inst->config->xlat_name, p); + } +} + /************************************************************************* * * Function: rlm_sql_query @@ -349,22 +375,34 @@ int rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query) goto sql_down; } - while (1) { + while (true) { DEBUG("rlm_sql (%s): Executing query: '%s'", inst->config->xlat_name, query); ret = (inst->module->sql_query)(*handle, inst->config, query); + switch (ret) { + case RLM_SQL_OK: + break; + /* - * Run through all available sockets until we exhaust all existing - * sockets in the pool and fail to establish a *new* connection. + * Run through all available sockets until we exhaust all existing + * sockets in the pool and fail to establish a *new* connection. */ - if (ret == RLM_SQL_RECONNECT) { - sql_down: + case RLM_SQL_RECONNECT: + sql_down: *handle = fr_connection_reconnect(inst->pool, *handle); if (!*handle) return RLM_SQL_RECONNECT; - continue; + + case RLM_SQL_QUERY_ERROR: + case RLM_SQL_ERROR: + rlm_sql_query_error(*handle, inst); + break; + + case RLM_SQL_DUPLICATE: + rlm_sql_query_debug(*handle, inst); + break; + } - if (ret < 0) rlm_sql_query_error(handle, inst); return ret; } @@ -392,22 +430,34 @@ int rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const goto sql_down; } - while (1) { + while (true) { DEBUG("rlm_sql (%s): Executing query: '%s'", inst->config->xlat_name, query); ret = (inst->module->sql_select_query)(*handle, inst->config, query); + switch (ret) { + case RLM_SQL_OK: + break; + /* - * Run through all available sockets until we exhaust all existing - * sockets in the pool and fail to establish a *new* connection. + * Run through all available sockets until we exhaust all existing + * sockets in the pool and fail to establish a *new* connection. */ - if (ret == RLM_SQL_RECONNECT) { - sql_down: + case RLM_SQL_RECONNECT: + sql_down: *handle = fr_connection_reconnect(inst->pool, *handle); if (!*handle) return RLM_SQL_RECONNECT; - continue; + + case RLM_SQL_QUERY_ERROR: + case RLM_SQL_ERROR: + rlm_sql_query_error(*handle, inst); + break; + + case RLM_SQL_DUPLICATE: + rlm_sql_query_debug(*handle, inst); + break; + } - if (ret < 0) rlm_sql_query_error(handle, inst); return ret; }