Skip to content

Commit

Permalink
add QueryException message handling without replacing bindings.
Browse files Browse the repository at this point in the history
  • Loading branch information
tzmfreedom committed Jan 15, 2025
1 parent 620f717 commit 5000f6c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 6 deletions.
5 changes: 3 additions & 2 deletions src/Illuminate/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -816,14 +816,15 @@ protected function runQueryCallback($query, $bindings, Closure $callback)
// message to include the bindings with SQL, which will make this exception a
// lot more helpful to the developer instead of just the database's errors.
catch (Exception $e) {
$mask = Arr::get($this->config, 'mask', false);
if ($this->isUniqueConstraintError($e)) {
throw new UniqueConstraintViolationException(
$this->getName(), $query, $this->prepareBindings($bindings), $e
$this->getName(), $query, $this->prepareBindings($bindings), $e, $mask
);
}

throw new QueryException(
$this->getName(), $query, $this->prepareBindings($bindings), $e
$this->getName(), $query, $this->prepareBindings($bindings), $e, $mask
);
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/Illuminate/Database/QueryException.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ class QueryException extends PDOException
* @param \Throwable $previous
* @return void
*/
public function __construct($connectionName, $sql, array $bindings, Throwable $previous)
public function __construct($connectionName, $sql, array $bindings, Throwable $previous, bool $maskParameters = false)
{
parent::__construct('', 0, $previous);

$this->connectionName = $connectionName;
$this->sql = $sql;
$this->bindings = $bindings;
$this->code = $previous->getCode();
$this->message = $this->formatMessage($connectionName, $sql, $bindings, $previous);
$this->message = $this->formatMessage($connectionName, $sql, $bindings, $previous, $maskParameters);

if ($previous instanceof PDOException) {
$this->errorInfo = $previous->errorInfo;
Expand All @@ -60,11 +60,15 @@ public function __construct($connectionName, $sql, array $bindings, Throwable $p
* @param string $sql
* @param array $bindings
* @param \Throwable $previous
* @param bool $maskPatameters
* @return string
*/
protected function formatMessage($connectionName, $sql, $bindings, Throwable $previous)
protected function formatMessage($connectionName, $sql, $bindings, Throwable $previous, bool $maskPatameters)
{
return $previous->getMessage().' (Connection: '.$connectionName.', SQL: '.Str::replaceArray('?', $bindings, $sql).')';
if (! $maskPatameters) {
$sql = Str::replaceArray('?', $bindings, $sql);
}
return $previous->getMessage().' (Connection: '.$connectionName.', SQL: '.$sql.')';
}

/**
Expand Down
34 changes: 34 additions & 0 deletions tests/Database/DatabaseConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,40 @@ public function testGetRawQueryLog()
$this->assertEquals(1.23, $log[0]['time']);
}

public function testQueryExceptionMessageOnNoMasking()
{
$this->expectException(QueryException::class);
$this->expectExceptionMessage('server has gone away (Connection: , SQL: SELECT * FROM users WHERE id = 1)');

$pdo = m::mock(PDO::class);
$pdo->shouldReceive('beginTransaction')->once();
$statement = m::mock(PDOStatement::class);
$statement->shouldReceive('bindValue')->once();
$pdo->shouldReceive('prepare')->once()->andReturn($statement);
$statement->shouldReceive('execute')->once()->andThrow(new PDOException('server has gone away'));

$connection = new Connection($pdo);
$connection->beginTransaction();
$connection->statement('SELECT * FROM users WHERE id = ?', [1]);
}

public function testQueryExceptionMessageOnMasking()
{
$this->expectException(QueryException::class);
$this->expectExceptionMessage('server has gone away (Connection: , SQL: SELECT * FROM users WHERE id = ?)');

$pdo = m::mock(PDO::class);
$pdo->shouldReceive('beginTransaction')->once();
$statement = m::mock(PDOStatement::class);
$statement->shouldReceive('bindValue')->once();
$pdo->shouldReceive('prepare')->once()->andReturn($statement);
$statement->shouldReceive('execute')->once()->andThrow(new PDOException('server has gone away'));

$connection = new Connection($pdo, '', '', ['mask' => true]);
$connection->beginTransaction();
$connection->statement('SELECT * FROM users WHERE id = ?', [1]);
}

protected function getMockConnection($methods = [], $pdo = null)
{
$pdo = $pdo ?: new DatabaseConnectionTestMockPDO;
Expand Down

0 comments on commit 5000f6c

Please sign in to comment.