Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Ability to log fake queries #9695

Merged
merged 34 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c72935c
Adds support for logging Stache queries
JohnathonKoster Mar 10, 2024
1818006
Update stache.php
JohnathonKoster Mar 10, 2024
4f3ea3a
Update LogsStacheQueries.php
JohnathonKoster Mar 10, 2024
c524230
Add extra FROM output from entry store
JohnathonKoster Mar 10, 2024
8b71078
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 12, 2024
dd3561a
Merge branch 'master' into adds-stache-query-logging-support
jasonvarga Mar 29, 2024
3dae5ee
fix merge
jasonvarga Mar 29, 2024
dee1b0a
stache is implied by namespace
jasonvarga Mar 29, 2024
b0dd160
move into its own namespace
jasonvarga Mar 29, 2024
5e4f4e7
avoid property for enabling it
jasonvarga Mar 30, 2024
88e2e42
Use actual bindings ...
jasonvarga Mar 30, 2024
131e08b
reuse it
jasonvarga Apr 1, 2024
869aae4
move to generic spot so it can be used in non-stache query builders
jasonvarga Apr 1, 2024
929b7ff
move setting
jasonvarga Apr 1, 2024
5e254ec
tweaks ...
jasonvarga Apr 1, 2024
5a4236c
use lowercase, it looks more like what laravel normally outputs
jasonvarga Apr 1, 2024
7679a6a
tweaks ...
jasonvarga Apr 1, 2024
dcc2825
only dump the wheres in a nested where clause
jasonvarga Apr 1, 2024
87bdff3
avoid outputting "where" when there arent any
jasonvarga Apr 1, 2024
575954d
one-line everything. debugbar is weird when we provide newlines. ray …
jasonvarga Apr 1, 2024
1bf8fdb
wip
jasonvarga Apr 1, 2024
609db02
implement for assets
jasonvarga Apr 1, 2024
17c343d
and terms
jasonvarga Apr 1, 2024
ce548f9
dont fake queries in iterator builder, it's too much. we use that for…
jasonvarga Apr 1, 2024
4adb4da
move table name up a level
jasonvarga Apr 1, 2024
d85ab55
fix nested where not showing in search queries since they arent stach…
jasonvarga Apr 1, 2024
5d5817c
match was wrong. we wish it was that fast.
jasonvarga Apr 1, 2024
fe32412
add onceWithColumns ported from laravel ...
jasonvarga Apr 1, 2024
d489e6d
apply fake queries to pluck
jasonvarga Apr 1, 2024
b9a5579
nitpick
jasonvarga Apr 1, 2024
9bbb62c
nitpick
jasonvarga Apr 1, 2024
2975ace
nitpick
jasonvarga Apr 1, 2024
e191b87
nitpick
jasonvarga Apr 1, 2024
2c1ef7d
just mirror the debug setting
jasonvarga Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/system.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,17 @@

'row_id_handle' => 'id',

/*
|--------------------------------------------------------------------------
| Fake SQL Queries
|--------------------------------------------------------------------------
|
| When enabled, Statamic's query builders will emit events that appear
| the same way as any other query. This can be useful for debugging.
| The generated SQL statements are approximations and not exact.
|
*/

'fake_sql_queries' => config('app.debug'),

];
21 changes: 21 additions & 0 deletions src/Assets/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\QueryBuilder as Contract;
use Statamic\Facades;
use Statamic\Stache\Query\Builder as BaseQueryBuilder;
use Statamic\Support\Arr;

class QueryBuilder extends BaseQueryBuilder implements Contract
{
Expand Down Expand Up @@ -119,4 +120,24 @@ protected function getWhereColumnKeyValuesByIndex($column)

return $items;
}

public function getTableNameForFakeQuery(): string
{
return 'assets';
}

public function prepareForFakeQuery(): array
{
$data = parent::prepareForFakeQuery();

$data['wheres'] = Arr::prepend($data['wheres'], [
'type' => 'Basic',
'column' => 'container',
'operator' => '=',
'value' => $this->getContainer()->handle(),
'boolean' => 'and',
]);

return $data;
}
}
18 changes: 18 additions & 0 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
use Statamic\Contracts\Query\Builder as Contract;
use Statamic\Extensions\Pagination\LengthAwarePaginator;
use Statamic\Facades\Pattern;
use Statamic\Query\Concerns\FakesQueries;

abstract class Builder implements Contract
{
use FakesQueries;

protected $columns;
protected $limit;
protected $offset = 0;
Expand Down Expand Up @@ -570,6 +573,21 @@ abstract public function count();

abstract public function get($columns = ['*']);

protected function onceWithColumns($columns, $callback)
{
$original = $this->columns;

if (is_null($original)) {
$this->columns = $columns;
}

$result = $callback();

$this->columns = $original;

return $result;
}

abstract public function pluck($column, $key = null);

public function when($value, $callback, $default = null)
Expand Down
42 changes: 42 additions & 0 deletions src/Query/Concerns/FakesQueries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Statamic\Query\Concerns;

use Illuminate\Database\Events\QueryExecuted;
use Statamic\Query\Dumper\Dumper;

trait FakesQueries
{
protected function withFakeQueryLogging(\Closure $callback)
{
if (! config('statamic.system.fake_sql_queries', false)) {
return $callback();
}

$startTime = microtime(true);

$value = $callback();

$time = round((microtime(true) - $startTime) * 1000, 2);

event(new QueryExecuted(
($sql = new Dumper($this))->dump(),
$sql->bindings()->all(),
$time,
$sql->connection()
));

return $value;
}

public function prepareForFakeQuery(): array
{
return [
'wheres' => $this->wheres,
'columns' => $this->columns,
'orderBys' => $this->orderBys,
'limit' => $this->limit,
'offset' => $this->offset,
];
}
}
45 changes: 45 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryParts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryParts
{
protected function dumpColumns(): string
{
return implode(', ', $this->columns);
}

protected function dumpLimits(): string
{
if (! $this->limit) {
return '';
}

$limit = ' limit '.$this->limit;

if ($this->offset) {
$limit .= ' offset '.$this->offset;
}

return $limit;
}

protected function dumpOrderBys(): string
{
if (count($this->orderBys) === 0) {
return '';
}

$orders = [];

foreach ($this->orderBys as $orderBy) {
if (! $orderBy->sort) {
continue;
}

$orders[] = $orderBy->sort.' '.$orderBy->direction;
}

return ' order by '.implode(', ', $orders);
}
}
24 changes: 24 additions & 0 deletions src/Query/Dumper/Concerns/DumpsQueryValues.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

trait DumpsQueryValues
{
protected function dumpQueryArrayValues($array): string
{
if (count($array) === 0) {
return '[]';
}

return collect($array)->map(function ($value) {
return $this->dumpQueryValue($value);
})->implode(', ');
}

protected function dumpQueryValue($value): string
{
$this->bindings[] = $value;

return '?';
}
}
160 changes: 160 additions & 0 deletions src/Query/Dumper/Concerns/DumpsWheres.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

namespace Statamic\Query\Dumper\Concerns;

use Illuminate\Support\Str;
use Statamic\Query\Dumper\Dumper;

trait DumpsWheres
{
protected function dumpBasic($where): string
{
return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpArrayWhere($keyword, $where): string
{
return $where['column'].' '.$keyword.' ('.$this->dumpQueryArrayValues($where['values'] ?? []).')';
}

protected function dumpSimpleOperatorWhere($where): string
{
return $where['column'].' '.$where['operator'].$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpIn($where): string
{
return $this->dumpArrayWhere('in', $where);
}

protected function dumpNotIn($where): string
{
return $this->dumpArrayWhere('not in', $where);
}

protected function dumpNull($where): string
{
return $where['column'].' is null';
}

protected function dumpNotNull($where): string
{
return $where['column'].' is not null';
}

protected function dumpDatePartMethod($datePart, $where): string
{
return 'DATEPART('.$datePart.', '.$where['column'].') = '.$this->dumpQueryValue($where['value'] ?? null);
}

protected function dumpMonth($where): string
{
return $this->dumpDatePartMethod('MONTH', $where);
}

protected function dumpDay($where): string
{
return $this->dumpDatePartMethod('DAY', $where);
}

protected function dumpYear($where): string
{
return $this->dumpDatePartMethod('YEAR', $where);
}

protected function dumpTime($where): string
{
return $this->dumpDatePartMethod('TIMESTAMP', $where);
}

protected function dumpBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' between '.$valueOne.' and '.$valueTwo;
}

protected function dumpNotBetween($where): string
{
$valueOne = $this->dumpQueryValue($where['values'][0] ?? null);
$valueTwo = $this->dumpQueryValue($where['values'][1] ?? null);
$column = $where['column'];

return $column.' not between '.$valueOne.' and '.$valueTwo;
}

protected function dumpColumn($where): string
{
return $where['column'].' = '.$where['value'];
}

protected function dumpNested($where): string
{
$query = $where['query'] ?? null;

$sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres();

return "($sql)";
}

protected function dumpDate($where)
{
return $this->dumpSimpleOperatorWhere($where);
}

protected function dumpJsonMethod($where): string
{
$jsonMethod = strtoupper(Str::snake($where['type']));

if (isset($where['values'])) {
$valueString = $this->dumpQueryArrayValues($where['values']);
} else {
$valueString = $this->dumpQueryValue($where['value'] ?? null);
}

return $jsonMethod.'('.$where['column'].', '.$valueString.')';
}

protected function dumpWhere($isFirst, $where): string
{
$dumpedWhere = '';

if (! $isFirst) {
$dumpedWhere = $where['boolean'].' ';
}

$type = $where['type'];

if (Str::startsWith($type, 'Json')) {
$dumpedWhere .= $this->dumpJsonMethod($where);
} else {
$whereMethod = 'dump'.ucfirst($type);

if (method_exists($this, $whereMethod)) {
$dumpedWhere .= $this->{$whereMethod}($where);
} else {
// Fail-safe to dump "something".
$dumpedWhere .= strtoupper($type);
}
}

return $dumpedWhere;
}

protected function dumpWheres(): string
{
if (count($this->wheres) === 0) {
return '';
}

$parts = [];

for ($i = 0; $i < count($this->wheres); $i++) {
$parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]);
}

return implode(' ', $parts);
}
}
Loading
Loading