Skip to content

Commit

Permalink
query
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 27, 2024
1 parent 00338c6 commit 9cefd87
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 55 deletions.
29 changes: 15 additions & 14 deletions src/Bridges/DatabaseTracy/ConnectionPanel.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Nette\Database\DriverException;
use Nette\Database\Helpers;
use Nette\Database\Result;
use Nette\Database\SqlLiteral;
use Tracy;


Expand All @@ -36,7 +37,7 @@ class ConnectionPanel implements Tracy\IBarPanel

private int $count = 0;

private array $queries = [];
private array $records = [];

private Tracy\BlueScreen $blueScreen;

Expand Down Expand Up @@ -100,10 +101,10 @@ private function logQuery(Connection $connection, $result): void
if ($result instanceof Result) {
$this->totalTime += $result->getTime();
if ($this->count < $this->maxQueries) {
$this->queries[] = [$connection, $result->getQueryString(), $result->getParameters(), $source, $result->getTime(), $result->getRowCount(), null];
$this->records[] = [$connection, $result->getQuery(), $source, $result->getTime(), $result->getRowCount(), null];
}
} elseif ($result instanceof DriverException && $this->count < $this->maxQueries) {
$this->queries[] = [$connection, $result->getQueryString(), null, $source, null, null, $result->getMessage()];
$this->records[] = [$connection, $result->getQuery(), $source, null, null, $result->getMessage()];
}
}

Expand All @@ -114,10 +115,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 @@ -139,10 +139,11 @@ public function getPanel(): ?string
return null;
}

$queries = [];
foreach ($this->queries as $query) {
[$connection, $sql, $params, , , , $error] = $query;
$records = [];
foreach ($this->records as $record) {
[$connection, $query, , , , $error] = $record;
$explain = null;
$sql = $query->getSql();
$command = preg_match('#\s*\(?\s*(SELECT|INSERT|UPDATE|DELETE)\s#iA', $sql, $m)
? strtolower($m[1])
: null;
Expand All @@ -151,17 +152,17 @@ public function getPanel(): ?string
$cmd = is_string($this->explain)
? $this->explain
: 'EXPLAIN';
$explain = (new Result($connection, "$cmd $sql", $params))->fetchAll();
$explain = (new Result($connection, new SqlLiteral("$cmd $sql", $query->getParameters())))->fetchAll();
} catch (\PDOException) {
}
}

$query[] = $command;
$query[] = $explain;
$queries[] = $query;
$record[] = $command;
$record[] = $explain;
$records[] = $record;
}

return Nette\Utils\Helpers::capture(function () use ($queries, $connection) {
return Nette\Utils\Helpers::capture(function () use ($records, $connection) {

Check failure on line 165 in src/Bridges/DatabaseTracy/ConnectionPanel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $connection.

Check failure on line 165 in src/Bridges/DatabaseTracy/ConnectionPanel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Anonymous function has an unused use $records.

Check failure on line 165 in src/Bridges/DatabaseTracy/ConnectionPanel.php

View workflow job for this annotation

GitHub Actions / PHPStan

Variable $connection might not be defined.
$name = $this->name;
$count = $this->count;
$totalTime = $this->totalTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use Tracy\Helpers;
<table class="tracy-sortable">
<tr><th>Time&nbsp;ms</th><th>SQL Query</th><th>Rows</th></tr>
<?php
foreach ($queries as $query):
[$connection, $sql, $params, $source, $time, $rows, $error, $command, $explain] = $query;
foreach ($records as $record):
[$connection, $query, $source, $time, $rows, $error, $command, $explain] = $record;
?>
<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 All @@ -61,6 +61,6 @@ use Tracy\Helpers;
</tr>
<?php endforeach ?>
</table>
<?php if (count($queries) < $count): ?><p>...and more</p><?php endif ?>
<?php if (count($records) < $count): ?><p>...and more</p><?php endif ?>
</div>
</div>
16 changes: 12 additions & 4 deletions src/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Connection

/** @var callable(array, Result): array */
private $rowNormalizer = [Helpers::class, 'normalizeRow'];
private ?string $sql = null;
private ?SqlLiteral $query = null;
private int $transactionDepth = 0;


Expand Down Expand Up @@ -213,9 +213,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, $this->rowNormalizer);
$result = new Result($this, $this->query, $this->rowNormalizer);
} catch (DriverException $e) {
Arrays::invoke($this->onQuery, $this, $e);
throw $e;
Expand Down Expand Up @@ -248,9 +248,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 @@ -32,16 +32,16 @@ class Result implements \Iterator

public function __construct(
private readonly Connection $connection,
private readonly string $queryString,
private readonly array $params,
private readonly SqlLiteral $query,
?callable $normalizer = null,
) {
$time = microtime(true);
$this->normalizer = $normalizer;
$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 @@ -55,15 +55,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 9cefd87

Please sign in to comment.