Skip to content

Commit

Permalink
added Connection::getLastQuery(), Result::getQuery(), DriverException…
Browse files Browse the repository at this point in the history
…::getQuery(), replaces getSql() and getParameters() pairs
  • Loading branch information
dg committed Aug 28, 2024
1 parent 2a39cb6 commit 306eda6
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 46 deletions.
14 changes: 7 additions & 7 deletions src/Bridges/DatabaseTracy/ConnectionPanel.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ private function logQuery(Connection $connection, $result): void
if ($result instanceof Result) {
$this->totalTime += $result->getTime();
if ($this->count < $this->maxQueries) {
$this->events[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null];
$this->events[] = [$connection, $result->getQuery(), $source, $result->getTime(), $result->getRowCount(), null];
}
} elseif ($result instanceof DriverException && $this->count < $this->maxQueries) {
$this->events[] = [$connection, $result->getQueryString(), null, $source, null, null, $result->getMessage()];
$this->events[] = [$connection, $result->getQuery(), $source, null, null, $result->getMessage()];
}
}

Expand All @@ -106,10 +106,9 @@ public static function renderException(?\Throwable $e): ?array
return null;
}

$sql = $e->getQueryString();
return $sql ? [
return $e->getQuery() ? [
'tab' => 'SQL',
'panel' => Helpers::dumpSql($sql, $e->params ?? []),
'panel' => Helpers::dumpSql($e->getQuery()),
] : null;
}

Expand All @@ -133,8 +132,9 @@ public function getPanel(): ?string

$events = [];
foreach ($this->events as $event) {
[$connection, $sql, $params, , , , $error] = $event;
[$connection, $query, , , , $error] = $event;
$explain = null;
$sql = $query->getSql();
$command = preg_match('#\s*\(?\s*(SELECT|INSERT|UPDATE|DELETE)\s#iA', $sql, $m)
? strtolower($m[1])
: null;
Expand All @@ -143,7 +143,7 @@ public function getPanel(): ?string
$cmd = is_string($this->explain)
? $this->explain
: 'EXPLAIN';
$rows = $connection->getConnectionDriver()->query("$cmd $sql", $params);
$rows = $connection->getConnectionDriver()->query("$cmd $sql", $query->getParameters());
for ($explain = []; $row = $rows->fetch(); $explain[] = $row);
} catch (DriverException) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use Tracy\Helpers;
<tr><th>Time&nbsp;ms</th><th>SQL Query</th><th>Rows</th></tr>
<?php
foreach ($events as $event):
[$connection, $sql, $params, $source, $time, $rows, $error, $command, $explain] = $event;
[$connection, $query, $source, $time, $rows, $error, $command, $explain] = $event;
?>
<tr>
<td style="background:rgba(255, 95, 23, <?= sprintf('%0.3f', log($time * 1000 + 1, 10) * $performanceScale) ?>)" data-order="<?= (float) $time ?>">
Expand All @@ -36,7 +36,7 @@ use Tracy\Helpers;
<br /><a class="tracy-toggle tracy-collapsed" data-tracy-ref="^tr .nette-DbConnectionPanel-explain">explain</a>
<?php endif ?>
</td>
<td class="nette-DbConnectionPanel-sql nette-DbConnectionPanel-sql-<?= Helpers::escapeHtml($command) ?>"><?= DbHelpers::dumpSql($sql, $params, $connection) ?>
<td class="nette-DbConnectionPanel-sql nette-DbConnectionPanel-sql-<?= Helpers::escapeHtml($command) ?>"><?= DbHelpers::dumpSql($query, $connection) ?>
<?php if ($explain): ?>
<table class="tracy-collapsed nette-DbConnectionPanel-explain">
<tr>
Expand Down
16 changes: 12 additions & 4 deletions src/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Connection
private ?Drivers\Engine $engine;
private ?SqlPreprocessor $preprocessor;
private TypeConverter $typeConverter;
private ?string $sql = null;
private ?SqlLiteral $query = null;
private int $transactionDepth = 0;


Expand Down Expand Up @@ -210,9 +210,9 @@ public function transaction(callable $callback): mixed
*/
public function query(#[Language('SQL')] string $sql, #[Language('GenericSQL')] ...$params): Result
{
[$this->sql, $params] = $this->preprocess($sql, ...$params);
$this->query = new SqlLiteral(...$this->preprocess($sql, ...$params));
try {
$result = new Result($this, $this->sql, $params);
$result = new Result($this, $this->query);
} catch (DriverException $e) {
Arrays::invoke($this->onQuery, $this, $e);
throw $e;
Expand Down Expand Up @@ -245,9 +245,17 @@ public function preprocess(string $sql, ...$params): array
}


public function getLastQuery(): ?SqlLiteral
{
return $this->query;
}


/** @deprecated use getLastQuery()->getSql() */
public function getLastQueryString(): ?string
{
return $this->sql;
trigger_error(__METHOD__ . '() is deprecated, use getLastQuery()->getSql()', E_USER_DEPRECATED);
return $this->query?->getSql();
}


Expand Down
8 changes: 8 additions & 0 deletions src/Database/DriverException.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ public function getSqlState(): ?string
}


public function getQuery(): ?SqlLiteral
{
return $this->query;
}


/** @deprecated use getQuery()->getSql() */
public function getQueryString(): ?string
{
return $this->query?->getSql();
}


/** @deprecated use getQuery()->getParameters() */
public function getParameters(): ?array
{
return $this->query?->getParameters();
Expand Down
13 changes: 7 additions & 6 deletions src/Database/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Helpers
*/
public static function dumpResult(Result $result): void
{
echo "\n<table class=\"dump\">\n<caption>" . htmlspecialchars($result->getQueryString(), ENT_IGNORE, 'UTF-8') . "</caption>\n";
echo "\n<table class=\"dump\">\n<caption>" . htmlspecialchars($result->getQuery()->getSql(), ENT_IGNORE, 'UTF-8') . "</caption>\n";
if (!$result->getColumnCount()) {
echo "\t<tr>\n\t\t<th>Affected rows:</th>\n\t\t<td>", $result->getRowCount(), "</td>\n\t</tr>\n</table>\n";
return;
Expand Down Expand Up @@ -75,12 +75,13 @@ public static function dumpResult(Result $result): void
/**
* Returns syntax highlighted SQL command.
*/
public static function dumpSql(string $sql, ?array $params = null, ?Connection $connection = null): string
public static function dumpSql(SqlLiteral $query, ?Connection $connection = null): string
{
$keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
$keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|[RI]?LIKE|REGEXP|TRUE|FALSE';

// insert new lines
$sql = $query->getSql();
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);

Expand Down Expand Up @@ -108,14 +109,14 @@ public static function dumpSql(string $sql, ?array $params = null, ?Connection $
}, $sql);

// parameters
$params = $query->getParameters();
$sql = preg_replace_callback('#\?#', function () use ($params, $connection): string {
static $i = 0;
if (!isset($params[$i])) {
$param = $params[$i++] ?? null;
if ($param === null) {
return '?';
}

$param = $params[$i++];
if (
} elseif (
is_string($param)
&& (
preg_match('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u', $param)
Expand Down
18 changes: 13 additions & 5 deletions src/Database/Result.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class Result implements \Iterator

public function __construct(
private readonly Connection $connection,
private readonly string $queryString,
private readonly array $params,
private readonly SqlLiteral $query,
) {
$time = microtime(true);
$queryString = $query->getSql();
if (str_starts_with($queryString, '::')) {
$connection->getConnectionDriver()->{substr($queryString, 2)}();
} else {
$this->result = $connection->getConnectionDriver()->query($queryString, $params);
$this->result = $connection->getConnectionDriver()->query($queryString, $query->getParameters());
}
$this->time = microtime(true) - $time;
}
Expand All @@ -50,15 +50,23 @@ public function getConnection(): Connection
}


public function getQuery(): SqlLiteral
{
return $this->query;
}


/** @deprecated use getQuery()->getSql() */
public function getQueryString(): string
{
return $this->queryString;
return $this->query->getSql();
}


/** @deprecated use getQuery()->getParameters() */
public function getParameters(): array
{
return $this->params;
return $this->query->getParameters();
}


Expand Down
14 changes: 7 additions & 7 deletions tests/Database/Connection.query.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName
test('', function () use ($connection) {
$res = $connection->query('SELECT id FROM author WHERE id = ?', 11);
Assert::type(Nette\Database\Result::class, $res);
Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString());
Assert::same([11], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ?', $connection->getLastQueryString());
Assert::same('SELECT id FROM author WHERE id = ?', $res->getQuery()->getSql());
Assert::same([11], $res->getQuery()->getParameters());
Assert::same('SELECT id FROM author WHERE id = ?', $connection->getLastQuery()->getSql());
});


test('', function () use ($connection) {
$res = $connection->query('SELECT id FROM author WHERE id = ? OR id = ?', 11, 12);
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString());
Assert::same([11, 12], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql());
Assert::same([11, 12], $res->getQuery()->getParameters());
});


test('', function () use ($connection) {
$res = @$connection->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString());
Assert::same([11, 12], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql());
Assert::same([11, 12], $res->getQuery()->getParameters());
});
12 changes: 6 additions & 6 deletions tests/Database/Explorer.query.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName
test('', function () use ($explorer) {
$res = $explorer->query('SELECT id FROM author WHERE id = ?', 11);
Assert::type(Nette\Database\Result::class, $res);
Assert::same('SELECT id FROM author WHERE id = ?', $res->getQueryString());
Assert::same([11], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ?', $res->getQuery()->getSql());
Assert::same([11], $res->getQuery()->getParameters());
});


test('', function () use ($explorer) {
$res = $explorer->query('SELECT id FROM author WHERE id = ? OR id = ?', 11, 12);
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString());
Assert::same([11, 12], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql());
Assert::same([11, 12], $res->getQuery()->getParameters());
});


test('', function () use ($explorer) {
$res = @$explorer->queryArgs('SELECT id FROM author WHERE id = ? OR id = ?', [11, 12]); // is deprecated
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQueryString());
Assert::same([11, 12], $res->getParameters());
Assert::same('SELECT id FROM author WHERE id = ? OR id = ?', $res->getQuery()->getSql());
Assert::same([11, 12], $res->getQuery()->getParameters());
});
2 changes: 1 addition & 1 deletion tests/Database/Explorer/Explorer.cache.observer.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ $explorer = new Nette\Database\Explorer($connection, $explorer->getStructure(),

$queries = 0;
$connection->onQuery[] = function ($dao, Result $result) use (&$queries) {
if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQueryString())) {
if (!preg_match('#SHOW|CONSTRAINT_NAME|pg_catalog|sys\.|SET|PRAGMA|FROM sqlite_#i', $result->getQuery()->getSql())) {
$queries++;
}
};
Expand Down
18 changes: 11 additions & 7 deletions tests/Database/Helpers.dumpSql.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

declare(strict_types=1);

use Nette\Database\SqlLiteral;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';
Expand All @@ -18,47 +19,50 @@ Nette\Database\Helpers::loadFromFile($connection, __DIR__ . "/files/{$driverName
test('int check', function () use ($connection) {
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> id = 10 <strong style=\"color:green\">OR</strong> id = 11</pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE id = ? OR id = ?', [10, 11], $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE id = ? OR id = ?', [10, 11]), $connection),
);
});

test('bool check', function () use ($connection) {
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> deleted = 0</pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE deleted = ?', [false], $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE deleted = ?', [false]), $connection),
);
});

test('string check', function () use ($connection) {
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> name = <span title=\"Length 15 characters\">'Alexej Chruščev'</span></pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ['Alexej Chruščev'], $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ['Alexej Chruščev']), $connection),
);
});

test('string check with \'', function () use ($connection) {
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> name = <span title=\"Length 16 characters\">'Alexej Ch\\'ruščev'</span></pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection),
);
});

test('string check without connection', function () {
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> name = <span title=\"Length 16 characters\">'Alexej Ch'ruščev'</span></pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"])),
);
});


test('string compare with $connection vs without', function () use ($connection) {
Assert::notSame(Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection), Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]));
Assert::notSame(
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"])),
);
});

test('string check with \'', function () use ($connection) {
Nette\Database\Helpers::$maxLength = 10;
Assert::same(
"<pre class=\"dump\"><strong style=\"color:blue\">SELECT</strong> id \n<strong style=\"color:blue\">FROM</strong> author \n<strong style=\"color:blue\">WHERE</strong> name = <span title=\"Length 16 characters\">'Alexej Ch…'</span></pre>\n",
Nette\Database\Helpers::dumpSql('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"], $connection),
Nette\Database\Helpers::dumpSql(new SqlLiteral('SELECT id FROM author WHERE name = ?', ["Alexej Ch'ruščev"]), $connection),
);
});
2 changes: 1 addition & 1 deletion tests/Database/Result.fetchAll().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ match ($driverName) {
};

Assert::same(1, $res->getColumnCount());
Assert::same('SELECT id FROM book ORDER BY id', $res->getQueryString());
Assert::same('SELECT id FROM book ORDER BY id', $res->getQuery()->getSql());

Assert::equal([
Nette\Database\Row::from(['id' => 1]),
Expand Down

0 comments on commit 306eda6

Please sign in to comment.