diff --git a/classes/CCR/Loggable.php b/classes/CCR/Loggable.php index b3e45c2737..9e2f0cf83b 100644 --- a/classes/CCR/Loggable.php +++ b/classes/CCR/Loggable.php @@ -103,11 +103,29 @@ public function logAndThrowException($message, array $options = null) $message = "{$this}: " . ( is_string($message) ? $message : "" ); $logLevel = PEAR_LOG_ERR; $exceptionProvided = false; + $code = 0; if ( null !== $options ) { if ( array_key_exists('exception', $options) && $options['exception'] instanceof Exception ) { - $message .= " Exception: '" . $options['exception']->getMessage() . "'"; + // Don't add the exception message if it is the same as the general message + $exceptionMessage = $options['exception']->getMessage(); + if ( $message != $exceptionMessage ) { + $message .= sprintf(" Exception: '%s'", $exceptionMessage); + } + + // PDOException uses a string exception code (typically a five characters alphanumeric + // identifier defined in the ANSI SQL standard) while Exception uses an int. Use the + // driver specific error code instead so that we can propagate the error code with + // the newly thrown Exception. + // See: https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html + + if ( $options['exception'] instanceof PDOException ) { + $code = $options['exception']->errorInfo[1]; + } else { + $code = $options['exception']->getCode(); + } + $exceptionProvided = true; } @@ -130,7 +148,7 @@ public function logAndThrowException($message, array $options = null) $logMessage['message'] = $message; $this->logger->log($logMessage, $logLevel); - throw new Exception($message); + throw new Exception($message, $code); } // logAndThrowException() diff --git a/open_xdmod/modules/xdmod/component_tests/lib/Utilities/LogAndThrowExceptionTest.php b/open_xdmod/modules/xdmod/component_tests/lib/Utilities/LogAndThrowExceptionTest.php new file mode 100644 index 0000000000..6e1843946d --- /dev/null +++ b/open_xdmod/modules/xdmod/component_tests/lib/Utilities/LogAndThrowExceptionTest.php @@ -0,0 +1,87 @@ +db = DB::factory('datawarehouse'); + $this->loggable = new Loggable(); + } + + /** + * Test #1: Provide only a log/exception message. + */ + + public function testNoExceptionCode() + { + $msg = "No Code"; + $excpectedMsg = (string) $this->loggable . " $msg"; + try { + $this->loggable->logAndThrowException($msg); + } catch ( Exception $e ) { + $this->assertEquals($expectedMsg, $e->getMessage()); + $this->assertEquals(0, $e->getCode()); + } + } + + /** + * Test #2: Provide a log/exception message and an exception with the same message and a code. + */ + + public function testExceptionCode() + { + $msg = "Code = 10"; + $excpectedMsg = (string) $this->loggable . " $msg"; + try { + $this->loggable->logAndThrowException( + $msg, + array('exception' => new Exception($msg, 10)) + ); + } catch ( Exception $e ) { + $this->assertEquals($expectedMsg, $e->getMessage()); + $this->assertEquals(10, $e->getCode()); + } + } + + /** + * Test #3: Query the database for a table that doesn't exist. Ensure the SQLSTATE 42S02 + * is returned as the PDOException error code and the MySQL error number is returned by + * the exception thrown via Loggable::logAndThrowException(). + * See https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html + */ + + public function testPdoException() + { + $sql = sprintf("SELECT count(*) FROM %s;", uniqid('modw.table_does_not_exist_')); + try { + try { + $this->db->query($sql); + } catch ( PDOException $p ) { + $this->assertEquals('42S02', $p->getCode()); + $this->loggable->logAndThrowException( + $p->getMessage(), + array( + 'exception' => $p, + 'sql' => $sql + ) + ); + } + } catch ( Exception $e ) { + $this->assertEquals(1146, $e->getCode()); + } + } +}