Skip to content

Commit 1eaedc1

Browse files
committed
Add structured trace for all DB drivers
1 parent 0307963 commit 1eaedc1

File tree

10 files changed

+123
-21
lines changed

10 files changed

+123
-21
lines changed

system/Common.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ function render_backtrace(array $backtrace): string
984984
$frame['class'],
985985
$frame['type'],
986986
$frame['function'],
987-
$args
987+
$args,
988988
);
989989
}
990990

system/Database/MySQLi/Connection.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,12 @@ protected function execute(string $sql)
326326
try {
327327
return $this->connID->query($this->prepQuery($sql), $this->resultMode);
328328
} catch (mysqli_sql_exception $e) {
329-
log_message('error', (string) $e);
329+
log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
330+
'message' => $e->getMessage(),
331+
'exFile' => clean_path($e->getFile()),
332+
'exLine' => $e->getLine(),
333+
'trace' => render_backtrace($e->getTrace()),
334+
]);
330335

331336
if ($this->DBDebug) {
332337
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);

system/Database/OCI8/Connection.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,14 @@ protected function execute(string $sql)
231231

232232
return $result;
233233
} catch (ErrorException $e) {
234-
log_message('error', (string) $e);
234+
$trace = array_slice($e->getTrace(), 2); // remove call to error handler
235+
236+
log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
237+
'message' => $e->getMessage(),
238+
'exFile' => clean_path($e->getFile()),
239+
'exLine' => $e->getLine(),
240+
'trace' => render_backtrace($trace),
241+
]);
235242

236243
if ($this->DBDebug) {
237244
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);

system/Database/Postgre/Connection.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,14 @@ protected function execute(string $sql)
205205
try {
206206
return pg_query($this->connID, $sql);
207207
} catch (ErrorException $e) {
208-
log_message('error', (string) $e);
208+
$trace = array_slice($e->getTrace(), 2); // remove the call to error handler
209+
210+
log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
211+
'message' => $e->getMessage(),
212+
'exFile' => clean_path($e->getFile()),
213+
'exLine' => $e->getLine(),
214+
'trace' => render_backtrace($trace),
215+
]);
209216

210217
if ($this->DBDebug) {
211218
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);

system/Database/SQLSRV/Connection.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,12 @@ public function getAllErrorMessages(): string
155155
$errors = [];
156156

157157
foreach (sqlsrv_errors() as $error) {
158-
$errors[] = $error['message']
159-
. ' SQLSTATE: ' . $error['SQLSTATE'] . ', code: ' . $error['code'];
158+
$errors[] = sprintf(
159+
'%s SQLSTATE: %s, code: %s',
160+
$error['message'],
161+
$error['SQLSTATE'],
162+
$error['code'],
163+
);
160164
}
161165

162166
return implode("\n", $errors);
@@ -522,17 +526,23 @@ public function setDatabase(?string $databaseName = null)
522526
*/
523527
protected function execute(string $sql)
524528
{
525-
$stmt = ($this->scrollable === false || $this->isWriteType($sql)) ?
526-
sqlsrv_query($this->connID, $sql) :
527-
sqlsrv_query($this->connID, $sql, [], ['Scrollable' => $this->scrollable]);
529+
$stmt = ($this->scrollable === false || $this->isWriteType($sql))
530+
? sqlsrv_query($this->connID, $sql)
531+
: sqlsrv_query($this->connID, $sql, [], ['Scrollable' => $this->scrollable]);
528532

529533
if ($stmt === false) {
530-
$error = $this->error();
534+
$trace = debug_backtrace();
535+
$first = array_shift($trace);
531536

532-
log_message('error', $error['message']);
537+
log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
538+
'message' => $this->getAllErrorMessages(),
539+
'exFile' => clean_path($first['file']),
540+
'exLine' => $first['line'],
541+
'trace' => render_backtrace($trace),
542+
]);
533543

534544
if ($this->DBDebug) {
535-
throw new DatabaseException($error['message']);
545+
throw new DatabaseException($this->getAllErrorMessages());
536546
}
537547
}
538548

system/Database/SQLite3/Connection.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ protected function execute(string $sql)
175175
? $this->connID->exec($sql)
176176
: $this->connID->query($sql);
177177
} catch (Exception $e) {
178-
log_message('error', (string) $e);
178+
log_message('error', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
179+
'message' => $e->getMessage(),
180+
'exFile' => clean_path($e->getFile()),
181+
'exLine' => $e->getLine(),
182+
'trace' => render_backtrace($e->getTrace()),
183+
]);
179184

180185
if ($this->DBDebug) {
181186
throw new DatabaseException($e->getMessage(), $e->getCode(), $e);

system/Debug/Exceptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ private function handleDeprecationError(string $message, ?string $file = null, ?
528528
'errFile' => clean_path($file ?? ''),
529529
'errLine' => $line ?? 0,
530530
'trace' => render_backtrace($trace),
531-
]
531+
],
532532
);
533533

534534
return true;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace CodeIgniter\Database\Live;
15+
16+
use CodeIgniter\Test\CIUnitTestCase;
17+
use CodeIgniter\Test\TestLogger;
18+
use Config\Database;
19+
use PHPUnit\Framework\Attributes\Group;
20+
21+
/**
22+
* @internal
23+
*/
24+
#[Group('DatabaseLive')]
25+
final class ExecuteLogMessageFormatTest extends CIUnitTestCase
26+
{
27+
protected function setUp(): void
28+
{
29+
parent::setUp();
30+
31+
self::setPrivateProperty(TestLogger::class, 'op_logs', []);
32+
}
33+
34+
protected function tearDown(): void
35+
{
36+
parent::tearDown();
37+
38+
self::setPrivateProperty(TestLogger::class, 'op_logs', []);
39+
}
40+
41+
public function testLogMessageWhenExecuteFailsShowFullStructuredBacktrace(): void
42+
{
43+
$db = Database::connect('tests', false);
44+
self::setPrivateProperty($db, 'DBDebug', false);
45+
46+
$sql = 'SELECT * FROM some_table WHERE id = ? AND status = ? AND author = ?';
47+
$db->query($sql, [3, 'live', 'Rick']);
48+
49+
$pattern = match ($db->DBDriver) {
50+
'MySQLi' => '/Table \'test\.some_table\' doesn\'t exist/',
51+
'Postgre' => '/pg_query\(\): Query failed: ERROR: relation "some_table" does not exist/',
52+
'SQLite3' => '/Unable to prepare statement:\s(\d+,\s)?no such table: some_table/',
53+
'OCI8' => '/oci_execute\(\): ORA-00942: table or view does not exist/',
54+
'SQLSRV' => '/\[Microsoft\]\[ODBC Driver \d+ for SQL Server\]\[SQL Server\]Invalid object name \'some_table\'/',
55+
default => '/Unknown DB error/',
56+
};
57+
$messageFromLogs = explode("\n", self::getPrivateProperty(TestLogger::class, 'op_logs')[0]['message']);
58+
59+
$this->assertMatchesRegularExpression($pattern, array_shift($messageFromLogs));
60+
61+
if ($db->DBDriver === 'Postgre') {
62+
$messageFromLogs = array_slice($messageFromLogs, 2);
63+
} elseif ($db->DBDriver === 'OCI8') {
64+
$messageFromLogs = array_slice($messageFromLogs, 1);
65+
}
66+
67+
$this->assertMatchesRegularExpression('/^in \S+ on line \d+\.$/', array_shift($messageFromLogs));
68+
69+
foreach ($messageFromLogs as $line) {
70+
$this->assertMatchesRegularExpression('/^\s*\d* .+(?:\(\d+\))?: \S+(?:(?:\->|::)\S+)?\(.*\)$/', $line);
71+
}
72+
}
73+
}

utils/phpstan-baseline/loader.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# total 2817 errors
1+
# total 2816 errors
22
includes:
33
- argument.type.neon
44
- assign.propertyType.neon

utils/phpstan-baseline/missingType.iterableValue.neon

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# total 1408 errors
1+
# total 1407 errors
22

33
parameters:
44
ignoreErrors:
@@ -2047,11 +2047,6 @@ parameters:
20472047
count: 1
20482048
path: ../../system/Debug/Exceptions.php
20492049

2050-
-
2051-
message: '#^Method CodeIgniter\\Debug\\Exceptions\:\:renderBacktrace\(\) has parameter \$backtrace with no value type specified in iterable type array\.$#'
2052-
count: 1
2053-
path: ../../system/Debug/Exceptions.php
2054-
20552050
-
20562051
message: '#^Property CodeIgniter\\Debug\\Iterator\:\:\$results type has no value type specified in iterable type array\.$#'
20572052
count: 1

0 commit comments

Comments
 (0)