From c72935c9f249b2727336681632c48052f8e69371 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 10 Mar 2024 14:40:27 -0500 Subject: [PATCH 01/32] Adds support for logging Stache queries --- config/stache.php | 16 ++ src/Query/Builder.php | 3 + src/Stache/Query/Builder.php | 12 +- src/Stache/Query/Concerns/DumpsQueryParts.php | 51 ++++++ .../Query/Concerns/DumpsQueryValues.php | 60 +++++++ src/Stache/Query/Concerns/DumpsWheres.php | 162 ++++++++++++++++++ .../Query/Concerns/LogsStacheQueries.php | 41 +++++ src/Stache/Query/StacheQueryDumper.php | 48 ++++++ 8 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 src/Stache/Query/Concerns/DumpsQueryParts.php create mode 100644 src/Stache/Query/Concerns/DumpsQueryValues.php create mode 100644 src/Stache/Query/Concerns/DumpsWheres.php create mode 100644 src/Stache/Query/Concerns/LogsStacheQueries.php create mode 100644 src/Stache/Query/StacheQueryDumper.php diff --git a/config/stache.php b/config/stache.php index e0db82e05ca..86c75d81bff 100644 --- a/config/stache.php +++ b/config/stache.php @@ -124,4 +124,20 @@ 'timeout' => 30, ], + /* + |-------------------------------------------------------------------------- + | Query Logging + |-------------------------------------------------------------------------- + | + | When enabled, the Stache query builders will log queries + | like normal SQL Queries. To log actual query values, + | set the dump_values configuration option to true. + | + */ + + 'query_logging' => [ + 'enabled' => true, + 'dump_values' => false, + ], + ]; diff --git a/src/Query/Builder.php b/src/Query/Builder.php index ec7dbaca0ff..19a2720dcf6 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -12,9 +12,12 @@ use Statamic\Contracts\Query\Builder as Contract; use Statamic\Extensions\Pagination\LengthAwarePaginator; use Statamic\Facades\Pattern; +use Statamic\Stache\Query\Concerns\LogsStacheQueries; abstract class Builder implements Contract { + use LogsStacheQueries; + protected $columns; protected $limit; protected $offset = 0; diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index 139d5553d7f..71dff28aa99 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -14,6 +14,8 @@ abstract class Builder extends BaseBuilder public function __construct(Store $store) { $this->store = $store; + $this->loggerEnabled = config('statamic.stache.query_logging.enabled', false); + $this->logRealValues = config('statamic.stache.query_logging.dump_values', false); } public function count() @@ -23,6 +25,8 @@ public function count() public function get($columns = ['*']) { + $startTime = hrtime(true); + $keys = $this->getFilteredKeys(); $keys = $this->orderKeys($keys); @@ -35,7 +39,13 @@ public function get($columns = ['*']) ->selectedQueryColumns($this->columns ?? $columns) ->selectedQueryRelations($this->with)); - return $this->collect($items)->values(); + $values = $this->collect($items)->values(); + + $endTime = hrtime(true); + + $this->emitQueryEvent($startTime, $endTime); + + return $values; } abstract protected function getFilteredKeys(); diff --git a/src/Stache/Query/Concerns/DumpsQueryParts.php b/src/Stache/Query/Concerns/DumpsQueryParts.php new file mode 100644 index 00000000000..e7586f281eb --- /dev/null +++ b/src/Stache/Query/Concerns/DumpsQueryParts.php @@ -0,0 +1,51 @@ +columns != null) { + $columns = implode(', ', $this->columns); + } + + return $columns; + } + + protected function dumpLimits(): string + { + if (! $this->limit) { + return ''; + } + + $limit = "\n".'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.' '.strtoupper($orderBy->direction); + } + + return "\n".'ORDER BY '.implode(', ', $orders); + } +} diff --git a/src/Stache/Query/Concerns/DumpsQueryValues.php b/src/Stache/Query/Concerns/DumpsQueryValues.php new file mode 100644 index 00000000000..a97b81bc5c8 --- /dev/null +++ b/src/Stache/Query/Concerns/DumpsQueryValues.php @@ -0,0 +1,60 @@ +dumpActualValues) { + return implode(', ', array_fill(0, count($array), '?')); + } + + return collect($array)->map(function ($value) { + return $this->dumpQueryValue($value); + })->implode(', '); + } + + protected function dumpQueryValue($value): string + { + if (! $this->dumpActualValues) { + return '?'; + } + + if (is_string($value)) { + return "'$value'"; + } + + if (is_bool($value)) { + if ($value === true) { + return '1'; + } + + return '0'; + } + + if (is_null($value)) { + return 'NULL'; + } + + if ($value instanceof Carbon) { + return $value->toIso8601String(); + } + + if (is_numeric($value)) { + return (string) $value; + } + + if (is_object($value)) { + return '{object}'; + } + + return '{value}'; + } +} diff --git a/src/Stache/Query/Concerns/DumpsWheres.php b/src/Stache/Query/Concerns/DumpsWheres.php new file mode 100644 index 00000000000..f13a3e67ea8 --- /dev/null +++ b/src/Stache/Query/Concerns/DumpsWheres.php @@ -0,0 +1,162 @@ +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; + + if (! $query instanceof Builder) { + return ''; + } + + return '('.$query->dumpStacheQuery().')'; + } + + 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 = strtoupper($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 "\n".'WHERE '.implode(' ', $parts); + } +} diff --git a/src/Stache/Query/Concerns/LogsStacheQueries.php b/src/Stache/Query/Concerns/LogsStacheQueries.php new file mode 100644 index 00000000000..d9e331eff35 --- /dev/null +++ b/src/Stache/Query/Concerns/LogsStacheQueries.php @@ -0,0 +1,41 @@ +wheres, + $this->columns, + $this->orderBys, + $this->limit, + $this->offset + )) + ->setDumpActualValues($this->logRealValues) + ->dump(); + } + + protected function emitQueryEvent($startTime, $endTime): void + { + if (! $this->loggerEnabled) { + return; + } + + event(new QueryExecuted( + $this->dumpStacheQuery(), + [], + ($endTime - $startTime) / 1000000, + new Connection(fn () => null, 'Stache') + )); + } +} diff --git a/src/Stache/Query/StacheQueryDumper.php b/src/Stache/Query/StacheQueryDumper.php new file mode 100644 index 00000000000..2b2447a560c --- /dev/null +++ b/src/Stache/Query/StacheQueryDumper.php @@ -0,0 +1,48 @@ +store = $store; + $this->wheres = $wheres; + $this->columns = $columns; + $this->orderBys = $orderBys; + $this->limit = $limit; + $this->offset = $offset; + } + + public function setDumpActualValues($dumpValues): self + { + $this->dumpActualValues = $dumpValues; + + return $this; + } + + public function dump(): string + { + $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.get_class($this->store); + $query .= $this->dumpWheres(); + $query .= $this->dumpLimits(); + $query .= $this->dumpOrderBys(); + + return $query; + } +} From 18180062cde7067895ce066d1691a013c690ad66 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 10 Mar 2024 14:49:24 -0500 Subject: [PATCH 02/32] Update stache.php --- config/stache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/stache.php b/config/stache.php index 86c75d81bff..48ede4a4387 100644 --- a/config/stache.php +++ b/config/stache.php @@ -136,7 +136,7 @@ */ 'query_logging' => [ - 'enabled' => true, + 'enabled' => false, 'dump_values' => false, ], From 4f3ea3a0545196366798b5e3a29f2a7ca9df5c70 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 10 Mar 2024 15:20:01 -0500 Subject: [PATCH 03/32] Update LogsStacheQueries.php --- src/Stache/Query/Concerns/LogsStacheQueries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stache/Query/Concerns/LogsStacheQueries.php b/src/Stache/Query/Concerns/LogsStacheQueries.php index d9e331eff35..61b9fe58650 100644 --- a/src/Stache/Query/Concerns/LogsStacheQueries.php +++ b/src/Stache/Query/Concerns/LogsStacheQueries.php @@ -14,7 +14,7 @@ trait LogsStacheQueries public function dumpStacheQuery() { return (new StacheQueryDumper( - $this, + $this->store, $this->wheres, $this->columns, $this->orderBys, From c52423098b0b7dc275ec09cf79ef03e300b4cce7 Mon Sep 17 00:00:00 2001 From: John Koster Date: Sun, 10 Mar 2024 15:25:23 -0500 Subject: [PATCH 04/32] Add extra FROM output from entry store --- src/Stache/Query/Concerns/LogsStacheQueries.php | 10 ++++++++++ src/Stache/Query/StacheQueryDumper.php | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Stache/Query/Concerns/LogsStacheQueries.php b/src/Stache/Query/Concerns/LogsStacheQueries.php index 61b9fe58650..c15432cea72 100644 --- a/src/Stache/Query/Concerns/LogsStacheQueries.php +++ b/src/Stache/Query/Concerns/LogsStacheQueries.php @@ -4,6 +4,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Events\QueryExecuted; +use Statamic\Stache\Query\EntryQueryBuilder; use Statamic\Stache\Query\StacheQueryDumper; trait LogsStacheQueries @@ -13,6 +14,14 @@ trait LogsStacheQueries public function dumpStacheQuery() { + $extraFrom = ''; + + if ($this instanceof EntryQueryBuilder) { + if (is_array($this->collections)) { + $extraFrom = implode(', ', $this->collections); + } + } + return (new StacheQueryDumper( $this->store, $this->wheres, @@ -22,6 +31,7 @@ public function dumpStacheQuery() $this->offset )) ->setDumpActualValues($this->logRealValues) + ->setExtraFromStatement($extraFrom) ->dump(); } diff --git a/src/Stache/Query/StacheQueryDumper.php b/src/Stache/Query/StacheQueryDumper.php index 2b2447a560c..220af3fc36c 100644 --- a/src/Stache/Query/StacheQueryDumper.php +++ b/src/Stache/Query/StacheQueryDumper.php @@ -16,6 +16,7 @@ class StacheQueryDumper protected $limit; protected $offset; protected $store; + protected $extraFrom = ''; protected $dumpActualValues = false; public function __construct( @@ -29,6 +30,13 @@ public function __construct( $this->offset = $offset; } + public function setExtraFromStatement($extraFrom): self + { + $this->extraFrom = $extraFrom; + + return $this; + } + public function setDumpActualValues($dumpValues): self { $this->dumpActualValues = $dumpValues; @@ -39,6 +47,11 @@ public function setDumpActualValues($dumpValues): self public function dump(): string { $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.get_class($this->store); + + if ($this->extraFrom) { + $query .= '{'.$this->extraFrom.'}'; + } + $query .= $this->dumpWheres(); $query .= $this->dumpLimits(); $query .= $this->dumpOrderBys(); From 3dae5eecf9f6a1b78a5b910792914c51aed7f524 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 Mar 2024 16:21:17 -0400 Subject: [PATCH 05/32] fix merge --- src/Stache/Query/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index d1c2b4c6973..5b2defd7902 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -25,8 +25,6 @@ public function count() private function resolveKeys() { - $startTime = hrtime(true); - $keys = $this->getFilteredKeys(); $keys = $this->orderKeys($keys); @@ -41,6 +39,8 @@ public function pluck($column, $key = null) public function get($columns = ['*']) { + $startTime = hrtime(true); + $items = $this->getItems($this->resolveKeys()); $items->each(fn ($item) => $item From dee1b0a40930c90bf45a1a4f9d553a7a74d2e902 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 Mar 2024 16:22:54 -0400 Subject: [PATCH 06/32] stache is implied by namespace --- src/Query/Builder.php | 4 ++-- .../Query/Concerns/{LogsStacheQueries.php => LogsQueries.php} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/Stache/Query/Concerns/{LogsStacheQueries.php => LogsQueries.php} (98%) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index 5da373f67a2..a463196e398 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -12,11 +12,11 @@ use Statamic\Contracts\Query\Builder as Contract; use Statamic\Extensions\Pagination\LengthAwarePaginator; use Statamic\Facades\Pattern; -use Statamic\Stache\Query\Concerns\LogsStacheQueries; +use Statamic\Stache\Query\Concerns\LogsQueries; abstract class Builder implements Contract { - use LogsStacheQueries; + use LogsQueries; protected $columns; protected $limit; diff --git a/src/Stache/Query/Concerns/LogsStacheQueries.php b/src/Stache/Query/Concerns/LogsQueries.php similarity index 98% rename from src/Stache/Query/Concerns/LogsStacheQueries.php rename to src/Stache/Query/Concerns/LogsQueries.php index c15432cea72..7d00c16347f 100644 --- a/src/Stache/Query/Concerns/LogsStacheQueries.php +++ b/src/Stache/Query/Concerns/LogsQueries.php @@ -7,7 +7,7 @@ use Statamic\Stache\Query\EntryQueryBuilder; use Statamic\Stache\Query\StacheQueryDumper; -trait LogsStacheQueries +trait LogsQueries { protected $loggerEnabled = false; protected $logRealValues = false; From b0dd16021938c1fc01ffe9ea2a36b98f2183b022 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 Mar 2024 16:38:30 -0400 Subject: [PATCH 07/32] move into its own namespace --- src/Stache/Query/Concerns/LogsQueries.php | 4 ++-- .../Query/{ => Dumper}/Concerns/DumpsQueryParts.php | 2 +- .../Query/{ => Dumper}/Concerns/DumpsQueryValues.php | 2 +- src/Stache/Query/{ => Dumper}/Concerns/DumpsWheres.php | 2 +- .../Query/{StacheQueryDumper.php => Dumper/Dumper.php} | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) rename src/Stache/Query/{ => Dumper}/Concerns/DumpsQueryParts.php (95%) rename src/Stache/Query/{ => Dumper}/Concerns/DumpsQueryValues.php (95%) rename src/Stache/Query/{ => Dumper}/Concerns/DumpsWheres.php (98%) rename src/Stache/Query/{StacheQueryDumper.php => Dumper/Dumper.php} (84%) diff --git a/src/Stache/Query/Concerns/LogsQueries.php b/src/Stache/Query/Concerns/LogsQueries.php index 7d00c16347f..b269c1cb3a7 100644 --- a/src/Stache/Query/Concerns/LogsQueries.php +++ b/src/Stache/Query/Concerns/LogsQueries.php @@ -4,8 +4,8 @@ use Illuminate\Database\Connection; use Illuminate\Database\Events\QueryExecuted; +use Statamic\Stache\Query\Dumper\Dumper; use Statamic\Stache\Query\EntryQueryBuilder; -use Statamic\Stache\Query\StacheQueryDumper; trait LogsQueries { @@ -22,7 +22,7 @@ public function dumpStacheQuery() } } - return (new StacheQueryDumper( + return (new Dumper( $this->store, $this->wheres, $this->columns, diff --git a/src/Stache/Query/Concerns/DumpsQueryParts.php b/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php similarity index 95% rename from src/Stache/Query/Concerns/DumpsQueryParts.php rename to src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php index e7586f281eb..9315c625f2c 100644 --- a/src/Stache/Query/Concerns/DumpsQueryParts.php +++ b/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php @@ -1,6 +1,6 @@ Date: Fri, 29 Mar 2024 22:33:58 -0400 Subject: [PATCH 08/32] avoid property for enabling it --- src/Stache/Query/Builder.php | 1 - src/Stache/Query/Concerns/LogsQueries.php | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index 5b2defd7902..664ce7768d9 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -14,7 +14,6 @@ abstract class Builder extends BaseBuilder public function __construct(Store $store) { $this->store = $store; - $this->loggerEnabled = config('statamic.stache.query_logging.enabled', false); $this->logRealValues = config('statamic.stache.query_logging.dump_values', false); } diff --git a/src/Stache/Query/Concerns/LogsQueries.php b/src/Stache/Query/Concerns/LogsQueries.php index b269c1cb3a7..5dd17737dfa 100644 --- a/src/Stache/Query/Concerns/LogsQueries.php +++ b/src/Stache/Query/Concerns/LogsQueries.php @@ -9,7 +9,6 @@ trait LogsQueries { - protected $loggerEnabled = false; protected $logRealValues = false; public function dumpStacheQuery() @@ -37,7 +36,7 @@ public function dumpStacheQuery() protected function emitQueryEvent($startTime, $endTime): void { - if (! $this->loggerEnabled) { + if (! config('statamic.stache.query_logging.enabled', false)) { return; } From 88e2e42db44bf158543dcfd1962566a636b6fc25 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 29 Mar 2024 22:37:42 -0400 Subject: [PATCH 09/32] Use actual bindings ... Uses a real real in-memory sqlite connection. Small price to pay to make it simpler. Remove config option as we'll now always put values into bindings. Now that the QueryExecuted event is populated with more realistic data, bindings are automatically populated within the SQL when using Ray. --- src/Stache/Query/Builder.php | 1 - src/Stache/Query/Concerns/LogsQueries.php | 23 ++++++----- .../Query/Dumper/Concerns/DumpsQueryParts.php | 11 +++++ .../Dumper/Concerns/DumpsQueryValues.php | 40 +------------------ .../Query/Dumper/Concerns/DumpsWheres.php | 2 +- src/Stache/Query/Dumper/Dumper.php | 15 +++---- 6 files changed, 33 insertions(+), 59 deletions(-) diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index 664ce7768d9..e3f2a47bb59 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -14,7 +14,6 @@ abstract class Builder extends BaseBuilder public function __construct(Store $store) { $this->store = $store; - $this->logRealValues = config('statamic.stache.query_logging.dump_values', false); } public function count() diff --git a/src/Stache/Query/Concerns/LogsQueries.php b/src/Stache/Query/Concerns/LogsQueries.php index 5dd17737dfa..bbc74d6dbdb 100644 --- a/src/Stache/Query/Concerns/LogsQueries.php +++ b/src/Stache/Query/Concerns/LogsQueries.php @@ -2,16 +2,14 @@ namespace Statamic\Stache\Query\Concerns; -use Illuminate\Database\Connection; use Illuminate\Database\Events\QueryExecuted; +use Illuminate\Support\Facades\DB; use Statamic\Stache\Query\Dumper\Dumper; use Statamic\Stache\Query\EntryQueryBuilder; trait LogsQueries { - protected $logRealValues = false; - - public function dumpStacheQuery() + public function dumpStacheQuery($bindings) { $extraFrom = ''; @@ -27,9 +25,9 @@ public function dumpStacheQuery() $this->columns, $this->orderBys, $this->limit, - $this->offset + $this->offset, + $bindings, )) - ->setDumpActualValues($this->logRealValues) ->setExtraFromStatement($extraFrom) ->dump(); } @@ -40,11 +38,18 @@ protected function emitQueryEvent($startTime, $endTime): void return; } + $bindings = collect(); + + $connection = DB::connectUsing('stache', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + event(new QueryExecuted( - $this->dumpStacheQuery(), - [], + $this->dumpStacheQuery($bindings), + $bindings->all(), ($endTime - $startTime) / 1000000, - new Connection(fn () => null, 'Stache') + $connection )); } } diff --git a/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php index 9315c625f2c..731e3b1aa0c 100644 --- a/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Stache/Query/Dumper/Concerns/DumpsQueryParts.php @@ -2,8 +2,19 @@ namespace Statamic\Stache\Query\Dumper\Concerns; +use Statamic\Stache\Stores\Store; +use Statamic\Support\Str; + trait DumpsQueryParts { + protected function dumpTableNameFromStore(Store $store): string + { + return Str::of(class_basename($store)) + ->before('Store') + ->lower() + ->toString(); + } + protected function dumpColumns(): string { $columns = '*'; diff --git a/src/Stache/Query/Dumper/Concerns/DumpsQueryValues.php b/src/Stache/Query/Dumper/Concerns/DumpsQueryValues.php index a780bf4af94..0428691e0cb 100644 --- a/src/Stache/Query/Dumper/Concerns/DumpsQueryValues.php +++ b/src/Stache/Query/Dumper/Concerns/DumpsQueryValues.php @@ -2,8 +2,6 @@ namespace Statamic\Stache\Query\Dumper\Concerns; -use Carbon\Carbon; - trait DumpsQueryValues { protected function dumpQueryArrayValues($array): string @@ -12,10 +10,6 @@ protected function dumpQueryArrayValues($array): string return '[]'; } - if (! $this->dumpActualValues) { - return implode(', ', array_fill(0, count($array), '?')); - } - return collect($array)->map(function ($value) { return $this->dumpQueryValue($value); })->implode(', '); @@ -23,38 +17,8 @@ protected function dumpQueryArrayValues($array): string protected function dumpQueryValue($value): string { - if (! $this->dumpActualValues) { - return '?'; - } - - if (is_string($value)) { - return "'$value'"; - } - - if (is_bool($value)) { - if ($value === true) { - return '1'; - } - - return '0'; - } - - if (is_null($value)) { - return 'NULL'; - } - - if ($value instanceof Carbon) { - return $value->toIso8601String(); - } - - if (is_numeric($value)) { - return (string) $value; - } - - if (is_object($value)) { - return '{object}'; - } + $this->bindings[] = $value; - return '{value}'; + return '?'; } } diff --git a/src/Stache/Query/Dumper/Concerns/DumpsWheres.php b/src/Stache/Query/Dumper/Concerns/DumpsWheres.php index bbe574f5953..919fad72eb0 100644 --- a/src/Stache/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Stache/Query/Dumper/Concerns/DumpsWheres.php @@ -98,7 +98,7 @@ protected function dumpNested($where): string return ''; } - return '('.$query->dumpStacheQuery().')'; + return '('.$query->dumpStacheQuery($this->bindings).')'; } protected function dumpDate($where) diff --git a/src/Stache/Query/Dumper/Dumper.php b/src/Stache/Query/Dumper/Dumper.php index 0f967c09087..0be71421510 100644 --- a/src/Stache/Query/Dumper/Dumper.php +++ b/src/Stache/Query/Dumper/Dumper.php @@ -17,10 +17,11 @@ class Dumper protected $offset; protected $store; protected $extraFrom = ''; - protected $dumpActualValues = false; + + private $bindings; public function __construct( - $store, $wheres, $columns, $orderBys, $limit, $offset + $store, $wheres, $columns, $orderBys, $limit, $offset, $bindings ) { $this->store = $store; $this->wheres = $wheres; @@ -28,6 +29,7 @@ public function __construct( $this->orderBys = $orderBys; $this->limit = $limit; $this->offset = $offset; + $this->bindings = $bindings; } public function setExtraFromStatement($extraFrom): self @@ -37,16 +39,9 @@ public function setExtraFromStatement($extraFrom): self return $this; } - public function setDumpActualValues($dumpValues): self - { - $this->dumpActualValues = $dumpValues; - - return $this; - } - public function dump(): string { - $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.get_class($this->store); + $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.$this->dumpTableNameFromStore($this->store); if ($this->extraFrom) { $query .= '{'.$this->extraFrom.'}'; From 131e08bb69938d6ec582f8c1ff9de7bafd1124d8 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 11:32:04 -0400 Subject: [PATCH 10/32] reuse it --- src/Stache/Query/Concerns/LogsQueries.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Stache/Query/Concerns/LogsQueries.php b/src/Stache/Query/Concerns/LogsQueries.php index bbc74d6dbdb..78d6e0b3ad0 100644 --- a/src/Stache/Query/Concerns/LogsQueries.php +++ b/src/Stache/Query/Concerns/LogsQueries.php @@ -9,6 +9,8 @@ trait LogsQueries { + private static $connection; + public function dumpStacheQuery($bindings) { $extraFrom = ''; @@ -40,7 +42,7 @@ protected function emitQueryEvent($startTime, $endTime): void $bindings = collect(); - $connection = DB::connectUsing('stache', [ + static::$connection ??= DB::connectUsing('stache', [ 'driver' => 'sqlite', 'database' => ':memory:', ]); @@ -49,7 +51,7 @@ protected function emitQueryEvent($startTime, $endTime): void $this->dumpStacheQuery($bindings), $bindings->all(), ($endTime - $startTime) / 1000000, - $connection + static::$connection )); } } From 869aae4611486a43c0b55adaaa2ccfb689ce8d4a Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 11:51:30 -0400 Subject: [PATCH 11/32] move to generic spot so it can be used in non-stache query builders --- src/Query/Builder.php | 2 +- src/{Stache => }/Query/Concerns/LogsQueries.php | 4 ++-- .../Query/Dumper/Concerns/DumpsQueryParts.php | 2 +- .../Query/Dumper/Concerns/DumpsQueryValues.php | 2 +- src/{Stache => }/Query/Dumper/Concerns/DumpsWheres.php | 2 +- src/{Stache => }/Query/Dumper/Dumper.php | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/{Stache => }/Query/Concerns/LogsQueries.php (94%) rename src/{Stache => }/Query/Dumper/Concerns/DumpsQueryParts.php (96%) rename src/{Stache => }/Query/Dumper/Concerns/DumpsQueryValues.php (89%) rename src/{Stache => }/Query/Dumper/Concerns/DumpsWheres.php (98%) rename src/{Stache => }/Query/Dumper/Dumper.php (84%) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index a463196e398..e9340520db6 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -12,7 +12,7 @@ use Statamic\Contracts\Query\Builder as Contract; use Statamic\Extensions\Pagination\LengthAwarePaginator; use Statamic\Facades\Pattern; -use Statamic\Stache\Query\Concerns\LogsQueries; +use Statamic\Query\Concerns\LogsQueries; abstract class Builder implements Contract { diff --git a/src/Stache/Query/Concerns/LogsQueries.php b/src/Query/Concerns/LogsQueries.php similarity index 94% rename from src/Stache/Query/Concerns/LogsQueries.php rename to src/Query/Concerns/LogsQueries.php index 78d6e0b3ad0..954b6df8194 100644 --- a/src/Stache/Query/Concerns/LogsQueries.php +++ b/src/Query/Concerns/LogsQueries.php @@ -1,10 +1,10 @@ Date: Mon, 1 Apr 2024 11:59:02 -0400 Subject: [PATCH 12/32] move setting --- config/stache.php | 16 ---------------- config/system.php | 13 +++++++++++++ src/Query/Concerns/LogsQueries.php | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/config/stache.php b/config/stache.php index 48ede4a4387..e0db82e05ca 100644 --- a/config/stache.php +++ b/config/stache.php @@ -124,20 +124,4 @@ 'timeout' => 30, ], - /* - |-------------------------------------------------------------------------- - | Query Logging - |-------------------------------------------------------------------------- - | - | When enabled, the Stache query builders will log queries - | like normal SQL Queries. To log actual query values, - | set the dump_values configuration option to true. - | - */ - - 'query_logging' => [ - 'enabled' => false, - 'dump_values' => false, - ], - ]; diff --git a/config/system.php b/config/system.php index 05ffa7e48f3..af588223572 100644 --- a/config/system.php +++ b/config/system.php @@ -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' => false, + ]; diff --git a/src/Query/Concerns/LogsQueries.php b/src/Query/Concerns/LogsQueries.php index 954b6df8194..12f0af3cc65 100644 --- a/src/Query/Concerns/LogsQueries.php +++ b/src/Query/Concerns/LogsQueries.php @@ -36,7 +36,7 @@ public function dumpStacheQuery($bindings) protected function emitQueryEvent($startTime, $endTime): void { - if (! config('statamic.stache.query_logging.enabled', false)) { + if (! config('statamic.system.fake_sql_queries', false)) { return; } From 5e254ec84c9d8ff90754734bdda70c7548deb047 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 12:33:36 -0400 Subject: [PATCH 13/32] tweaks ... - rename trait to fakesqueries - rework to a closure based method that does the timing etc - add to iterator builder - pass table name rather than store, and add methods for figuring out the names - add to iterator builder (e.g. search) --- src/Query/Builder.php | 4 +-- .../{LogsQueries.php => FakesQueries.php} | 36 +++++++++++-------- src/Query/Dumper/Concerns/DumpsQueryParts.php | 22 ++++++++---- src/Query/Dumper/Concerns/DumpsWheres.php | 2 +- src/Query/Dumper/Dumper.php | 8 ++--- src/Query/IteratorBuilder.php | 24 ++++++++----- src/Search/Comb/Query.php | 5 +++ src/Stache/Query/Builder.php | 20 ++++------- 8 files changed, 72 insertions(+), 49 deletions(-) rename src/Query/Concerns/{LogsQueries.php => FakesQueries.php} (57%) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index e9340520db6..a25a266402f 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -12,11 +12,11 @@ use Statamic\Contracts\Query\Builder as Contract; use Statamic\Extensions\Pagination\LengthAwarePaginator; use Statamic\Facades\Pattern; -use Statamic\Query\Concerns\LogsQueries; +use Statamic\Query\Concerns\FakesQueries; abstract class Builder implements Contract { - use LogsQueries; + use FakesQueries; protected $columns; protected $limit; diff --git a/src/Query/Concerns/LogsQueries.php b/src/Query/Concerns/FakesQueries.php similarity index 57% rename from src/Query/Concerns/LogsQueries.php rename to src/Query/Concerns/FakesQueries.php index 12f0af3cc65..eb6c20b3962 100644 --- a/src/Query/Concerns/LogsQueries.php +++ b/src/Query/Concerns/FakesQueries.php @@ -7,11 +7,9 @@ use Statamic\Query\Dumper\Dumper; use Statamic\Stache\Query\EntryQueryBuilder; -trait LogsQueries +trait FakesQueries { - private static $connection; - - public function dumpStacheQuery($bindings) + public function dumpFakeQuery($bindings): string { $extraFrom = ''; @@ -22,7 +20,7 @@ public function dumpStacheQuery($bindings) } return (new Dumper( - $this->store, + Dumper::getTableName($this), $this->wheres, $this->columns, $this->orderBys, @@ -34,24 +32,34 @@ public function dumpStacheQuery($bindings) ->dump(); } - protected function emitQueryEvent($startTime, $endTime): void + protected function withFakeQueryLogging(\Closure $callback) { if (! config('statamic.system.fake_sql_queries', false)) { - return; + return $callback(); } + $startTime = hrtime(true); + + $value = $callback(); + + $time = (hrtime(true) - $startTime) / 1000000; + $bindings = collect(); - static::$connection ??= DB::connectUsing('stache', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - ]); + if (! app()->bound($key = 'fake-query-connection')) { + app()->instance($key, DB::connectUsing('fake', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + ])); + } event(new QueryExecuted( - $this->dumpStacheQuery($bindings), + $this->dumpFakeQuery($bindings), $bindings->all(), - ($endTime - $startTime) / 1000000, - static::$connection + $time, + app($key) )); + + return $value; } } diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 326013e15e5..c9410b4d778 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -2,17 +2,27 @@ namespace Statamic\Query\Dumper\Concerns; -use Statamic\Stache\Stores\Store; +use Statamic\Contracts\Query\Builder; +use Statamic\Stache\Query\Builder as StacheQueryBuilder; use Statamic\Support\Str; trait DumpsQueryParts { - protected function dumpTableNameFromStore(Store $store): string + public static function getTableName(Builder $class) { - return Str::of(class_basename($store)) - ->before('Store') - ->lower() - ->toString(); + if (method_exists($class, 'getTableNameForFakeQuery')) { + return $class->getTableNameForFakeQuery(); + } + + if ($class instanceof StacheQueryBuilder) { + return Str::of(class_basename($class)) + ->before('QueryBuilder') + ->lower() + ->plural() + ->toString(); + } + + return get_class($class); } protected function dumpColumns(): string diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index a2d1066a36c..ec39e542a02 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -98,7 +98,7 @@ protected function dumpNested($where): string return ''; } - return '('.$query->dumpStacheQuery($this->bindings).')'; + return '('.$query->dumpFakeQuery($this->bindings).')'; } protected function dumpDate($where) diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index ea603f5a532..f6065b9f48e 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -15,15 +15,15 @@ class Dumper protected $orderBys = []; protected $limit; protected $offset; - protected $store; + protected $table; protected $extraFrom = ''; private $bindings; public function __construct( - $store, $wheres, $columns, $orderBys, $limit, $offset, $bindings + $table, $wheres, $columns, $orderBys, $limit, $offset, $bindings ) { - $this->store = $store; + $this->table = $table; $this->wheres = $wheres; $this->columns = $columns; $this->orderBys = $orderBys; @@ -41,7 +41,7 @@ public function setExtraFromStatement($extraFrom): self public function dump(): string { - $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.$this->dumpTableNameFromStore($this->store); + $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.$this->table; if ($this->extraFrom) { $query .= '{'.$this->extraFrom.'}'; diff --git a/src/Query/IteratorBuilder.php b/src/Query/IteratorBuilder.php index a917ba7b3d2..728f8873641 100644 --- a/src/Query/IteratorBuilder.php +++ b/src/Query/IteratorBuilder.php @@ -2,8 +2,12 @@ namespace Statamic\Query; +use Statamic\Query\Concerns\FakesQueries; + abstract class IteratorBuilder extends Builder { + use FakesQueries; + protected $randomize = false; public function count() @@ -18,16 +22,18 @@ protected function getCountForPagination() public function get($columns = ['*']) { - $items = $this->getFilteredItems(); - - if ($this->randomize) { - $items = $items->shuffle(); - } elseif ($orderBys = $this->orderBys) { - $sort = collect($orderBys)->map->toString()->implode('|'); - $items = $items->multisort($sort)->values(); - } + return $this->withFakeQueryLogging(function () { + $items = $this->getFilteredItems(); + + if ($this->randomize) { + $items = $items->shuffle(); + } elseif ($orderBys = $this->orderBys) { + $sort = collect($orderBys)->map->toString()->implode('|'); + $items = $items->multisort($sort)->values(); + } - return $this->limitItems($items)->values(); + return $this->limitItems($items)->values(); + }); } public function pluck($column, $key = null) diff --git a/src/Search/Comb/Query.php b/src/Search/Comb/Query.php index dcad27df30f..196c6d701cd 100644 --- a/src/Search/Comb/Query.php +++ b/src/Search/Comb/Query.php @@ -10,4 +10,9 @@ public function getSearchResults($query) { return $this->index->lookup($this->query); } + + public function getTableNameForFakeQuery() + { + return 'search_'.$this->index->name(); + } } diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index e3f2a47bb59..0dccc617853 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -37,21 +37,15 @@ public function pluck($column, $key = null) public function get($columns = ['*']) { - $startTime = hrtime(true); + return $this->withFakeQueryLogging(function () use ($columns) { + $items = $this->getItems($this->resolveKeys()); - $items = $this->getItems($this->resolveKeys()); + $items->each(fn ($item) => $item + ->selectedQueryColumns($this->columns ?? $columns) + ->selectedQueryRelations($this->with)); - $items->each(fn ($item) => $item - ->selectedQueryColumns($this->columns ?? $columns) - ->selectedQueryRelations($this->with)); - - $values = $this->collect($items)->values(); - - $endTime = hrtime(true); - - $this->emitQueryEvent($startTime, $endTime); - - return $values; + return $this->collect($items)->values(); + }); } abstract protected function getFilteredKeys(); From 5a4236c0f18729993a54f08c813ebfb4b051f956 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 12:46:38 -0400 Subject: [PATCH 14/32] use lowercase, it looks more like what laravel normally outputs --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 8 ++++---- src/Query/Dumper/Concerns/DumpsWheres.php | 18 +++++++++--------- src/Query/Dumper/Dumper.php | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index c9410b4d778..3fee73aecd8 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -42,10 +42,10 @@ protected function dumpLimits(): string return ''; } - $limit = "\n".'LIMIT '.$this->limit; + $limit = "\n".'limit '.$this->limit; if ($this->offset) { - $limit .= ' OFFSET '.$this->offset; + $limit .= ' offset '.$this->offset; } return $limit; @@ -64,9 +64,9 @@ protected function dumpOrderBys(): string continue; } - $orders[] = $orderBy->sort.' '.strtoupper($orderBy->direction); + $orders[] = $orderBy->sort.' '.$orderBy->direction; } - return "\n".'ORDER BY '.implode(', ', $orders); + return "\n".'order by '.implode(', ', $orders); } } diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index ec39e542a02..c6044203860 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -9,7 +9,7 @@ trait DumpsWheres { protected function dumpBasic($where): string { - return $where['column'].' '.strtoupper($where['operator']).' '.$this->dumpQueryValue($where['value'] ?? null); + return $where['column'].' '.$where['operator'].' '.$this->dumpQueryValue($where['value'] ?? null); } protected function dumpArrayWhere($keyword, $where): string @@ -24,22 +24,22 @@ protected function dumpSimpleOperatorWhere($where): string protected function dumpIn($where): string { - return $this->dumpArrayWhere('IN', $where); + return $this->dumpArrayWhere('in', $where); } protected function dumpNotIn($where): string { - return $this->dumpArrayWhere('NOT IN', $where); + return $this->dumpArrayWhere('not in', $where); } protected function dumpNull($where): string { - return $where['column'].' IS NULL'; + return $where['column'].' is null'; } protected function dumpNotNull($where): string { - return $where['column'].' IS NOT NULL'; + return $where['column'].' is not null'; } protected function dumpDatePartMethod($datePart, $where): string @@ -73,7 +73,7 @@ protected function dumpBetween($where): string $valueTwo = $this->dumpQueryValue($where['values'][1] ?? null); $column = $where['column']; - return $column.' BETWEEN '.$valueOne.' AND '.$valueTwo; + return $column.' between '.$valueOne.' and '.$valueTwo; } protected function dumpNotBetween($where): string @@ -82,7 +82,7 @@ protected function dumpNotBetween($where): string $valueTwo = $this->dumpQueryValue($where['values'][1] ?? null); $column = $where['column']; - return $column.' NOT BETWEEN '.$valueOne.' AND '.$valueTwo; + return $column.' not between '.$valueOne.' and '.$valueTwo; } protected function dumpColumn($where): string @@ -124,7 +124,7 @@ protected function dumpWhere($isFirst, $where): string $dumpedWhere = ''; if (! $isFirst) { - $dumpedWhere = strtoupper($where['boolean']).' '; + $dumpedWhere = $where['boolean'].' '; } $type = $where['type']; @@ -157,6 +157,6 @@ protected function dumpWheres(): string $parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]); } - return "\n".'WHERE '.implode(' ', $parts); + return "\n".'where '.implode(' ', $parts); } } diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index f6065b9f48e..2c51b421354 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -41,7 +41,7 @@ public function setExtraFromStatement($extraFrom): self public function dump(): string { - $query = 'SELECT '.$this->dumpColumns()."\n".'FROM '.$this->table; + $query = 'select '.$this->dumpColumns()."\n".'from '.$this->table; if ($this->extraFrom) { $query .= '{'.$this->extraFrom.'}'; From 7679a6ad0ae71f64358ab7bee343a4e82651da74 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:08:45 -0400 Subject: [PATCH 15/32] tweaks ... - pass the query into the dumper class instead of a bunch of arguments - use a method on the trait to get all the appropriate wheres etc for the dumper - using that new method, provide the additional where clauses for the entries query that way instead of a "extra from statement" - remove dump trait method in favor of creating a new dumper --- src/Query/Concerns/FakesQueries.php | 37 ++++++------------- src/Query/Dumper/Concerns/DumpsQueryParts.php | 2 +- src/Query/Dumper/Concerns/DumpsWheres.php | 3 +- src/Query/Dumper/Dumper.php | 30 ++++----------- src/Stache/Query/EntryQueryBuilder.php | 17 +++++++++ 5 files changed, 40 insertions(+), 49 deletions(-) diff --git a/src/Query/Concerns/FakesQueries.php b/src/Query/Concerns/FakesQueries.php index eb6c20b3962..eab8360a3dd 100644 --- a/src/Query/Concerns/FakesQueries.php +++ b/src/Query/Concerns/FakesQueries.php @@ -5,33 +5,9 @@ use Illuminate\Database\Events\QueryExecuted; use Illuminate\Support\Facades\DB; use Statamic\Query\Dumper\Dumper; -use Statamic\Stache\Query\EntryQueryBuilder; trait FakesQueries { - public function dumpFakeQuery($bindings): string - { - $extraFrom = ''; - - if ($this instanceof EntryQueryBuilder) { - if (is_array($this->collections)) { - $extraFrom = implode(', ', $this->collections); - } - } - - return (new Dumper( - Dumper::getTableName($this), - $this->wheres, - $this->columns, - $this->orderBys, - $this->limit, - $this->offset, - $bindings, - )) - ->setExtraFromStatement($extraFrom) - ->dump(); - } - protected function withFakeQueryLogging(\Closure $callback) { if (! config('statamic.system.fake_sql_queries', false)) { @@ -54,7 +30,7 @@ protected function withFakeQueryLogging(\Closure $callback) } event(new QueryExecuted( - $this->dumpFakeQuery($bindings), + (new Dumper($this, $bindings))->dump(), $bindings->all(), $time, app($key) @@ -62,4 +38,15 @@ protected function withFakeQueryLogging(\Closure $callback) return $value; } + + public function prepareForFakeQuery(): array + { + return [ + 'wheres' => $this->wheres, + 'columns' => $this->columns, + 'orderBys' => $this->orderBys, + 'limit' => $this->limit, + 'offset' => $this->offset, + ]; + } } diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 3fee73aecd8..0e5251977db 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -8,7 +8,7 @@ trait DumpsQueryParts { - public static function getTableName(Builder $class) + public function getTableName(Builder $class) { if (method_exists($class, 'getTableNameForFakeQuery')) { return $class->getTableNameForFakeQuery(); diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index c6044203860..4a2e5c8234d 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -3,6 +3,7 @@ namespace Statamic\Query\Dumper\Concerns; use Illuminate\Support\Str; +use Statamic\Query\Dumper\Dumper; use Statamic\Stache\Query\Builder; trait DumpsWheres @@ -98,7 +99,7 @@ protected function dumpNested($where): string return ''; } - return '('.$query->dumpFakeQuery($this->bindings).')'; + return '('.(new Dumper($query, $this->bindings))->dump().')'; } protected function dumpDate($where) diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index 2c51b421354..4029692df82 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -16,37 +16,23 @@ class Dumper protected $limit; protected $offset; protected $table; - protected $extraFrom = ''; - - private $bindings; public function __construct( - $table, $wheres, $columns, $orderBys, $limit, $offset, $bindings + private $query, private $bindings ) { - $this->table = $table; - $this->wheres = $wheres; - $this->columns = $columns; - $this->orderBys = $orderBys; - $this->limit = $limit; - $this->offset = $offset; - $this->bindings = $bindings; - } - - public function setExtraFromStatement($extraFrom): self - { - $this->extraFrom = $extraFrom; - - return $this; + $data = $query->prepareForFakeQuery(); + $this->table = $this->getTableName($query); + $this->wheres = $data['wheres']; + $this->columns = $data['columns']; + $this->orderBys = $data['orderBys']; + $this->limit = $data['limit']; + $this->offset = $data['offset']; } public function dump(): string { $query = 'select '.$this->dumpColumns()."\n".'from '.$this->table; - if ($this->extraFrom) { - $query .= '{'.$this->extraFrom.'}'; - } - $query .= $this->dumpWheres(); $query .= $this->dumpLimits(); $query .= $this->dumpOrderBys(); diff --git a/src/Stache/Query/EntryQueryBuilder.php b/src/Stache/Query/EntryQueryBuilder.php index e43fe045b52..fbf92cd17eb 100644 --- a/src/Stache/Query/EntryQueryBuilder.php +++ b/src/Stache/Query/EntryQueryBuilder.php @@ -5,6 +5,7 @@ use Statamic\Contracts\Entries\QueryBuilder; use Statamic\Entries\EntryCollection; use Statamic\Facades; +use Statamic\Support\Arr; class EntryQueryBuilder extends Builder implements QueryBuilder { @@ -131,4 +132,20 @@ protected function getWhereColumnKeyValuesByIndex($column) return $this->getWhereColumnKeysFromStore($collection, ['column' => $column]); }); } + + public function prepareForFakeQuery(): array + { + $data = parent::prepareForFakeQuery(); + + if (! empty($this->collections)) { + $data['wheres'] = Arr::prepend($data['wheres'], [ + 'type' => 'In', + 'column' => 'collection', + 'values' => $this->collections, + 'boolean' => 'and', + ]); + } + + return $data; + } } From dcc2825c4e7e15fbd1ac0d352d786a71a3d1df40 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:11:57 -0400 Subject: [PATCH 16/32] only dump the wheres in a nested where clause --- src/Query/Dumper/Concerns/DumpsWheres.php | 4 ++-- src/Query/Dumper/Dumper.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index 4a2e5c8234d..cbe2c862a37 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -99,7 +99,7 @@ protected function dumpNested($where): string return ''; } - return '('.(new Dumper($query, $this->bindings))->dump().')'; + return '('.(new Dumper($query, $this->bindings))->dumpWheres().')'; } protected function dumpDate($where) @@ -158,6 +158,6 @@ protected function dumpWheres(): string $parts[] = $this->dumpWhere($i === 0, $this->wheres[$i]); } - return "\n".'where '.implode(' ', $parts); + return implode(' ', $parts); } } diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index 4029692df82..f2f9695f3dd 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -33,7 +33,7 @@ public function dump(): string { $query = 'select '.$this->dumpColumns()."\n".'from '.$this->table; - $query .= $this->dumpWheres(); + $query .= "\n where ".$this->dumpWheres(); $query .= $this->dumpLimits(); $query .= $this->dumpOrderBys(); From 87bdff32733c39e11574cb447edfee2f96f03088 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:13:49 -0400 Subject: [PATCH 17/32] avoid outputting "where" when there arent any --- src/Query/Dumper/Dumper.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index f2f9695f3dd..be7dafd3db6 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -33,7 +33,10 @@ public function dump(): string { $query = 'select '.$this->dumpColumns()."\n".'from '.$this->table; - $query .= "\n where ".$this->dumpWheres(); + if (! empty($this->wheres)) { + $query .= "\n where ".$this->dumpWheres(); + } + $query .= $this->dumpLimits(); $query .= $this->dumpOrderBys(); From 575954d57b095f7c69aef53368da524e1b91dd23 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:18:49 -0400 Subject: [PATCH 18/32] one-line everything. debugbar is weird when we provide newlines. ray will format to multiline automatically. --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 4 ++-- src/Query/Dumper/Dumper.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 0e5251977db..14f8e819f62 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -42,7 +42,7 @@ protected function dumpLimits(): string return ''; } - $limit = "\n".'limit '.$this->limit; + $limit = ' limit '.$this->limit; if ($this->offset) { $limit .= ' offset '.$this->offset; @@ -67,6 +67,6 @@ protected function dumpOrderBys(): string $orders[] = $orderBy->sort.' '.$orderBy->direction; } - return "\n".'order by '.implode(', ', $orders); + return ' order by '.implode(', ', $orders); } } diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index be7dafd3db6..6c8b36e4db3 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -31,10 +31,10 @@ public function __construct( public function dump(): string { - $query = 'select '.$this->dumpColumns()."\n".'from '.$this->table; + $query = 'select '.$this->dumpColumns().' from '.$this->table; if (! empty($this->wheres)) { - $query .= "\n where ".$this->dumpWheres(); + $query .= ' where '.$this->dumpWheres(); } $query .= $this->dumpLimits(); From 1bf8fdbad3bd8512a1a60ad58f12b02ace14b320 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:25:28 -0400 Subject: [PATCH 19/32] wip --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 14f8e819f62..6120a40e182 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -8,7 +8,7 @@ trait DumpsQueryParts { - public function getTableName(Builder $class) + public function getTableName(Builder $class): string { if (method_exists($class, 'getTableNameForFakeQuery')) { return $class->getTableNameForFakeQuery(); From 609db028fb2fe7fbd79360d2db57074b9701c30c Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:25:34 -0400 Subject: [PATCH 20/32] implement for assets --- src/Assets/QueryBuilder.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Assets/QueryBuilder.php b/src/Assets/QueryBuilder.php index 40c8c83b0ef..247d6d10644 100644 --- a/src/Assets/QueryBuilder.php +++ b/src/Assets/QueryBuilder.php @@ -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 { @@ -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; + } } From 17c343dc3a1d1b05f4d95f5435e1838c5a789a97 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:32:11 -0400 Subject: [PATCH 21/32] and terms --- src/Stache/Query/TermQueryBuilder.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Stache/Query/TermQueryBuilder.php b/src/Stache/Query/TermQueryBuilder.php index 3dca2d45cf5..deda55b3770 100644 --- a/src/Stache/Query/TermQueryBuilder.php +++ b/src/Stache/Query/TermQueryBuilder.php @@ -4,6 +4,7 @@ use Statamic\Facades; use Statamic\Facades\Collection; +use Statamic\Support\Arr; use Statamic\Taxonomies\TermCollection; class TermQueryBuilder extends Builder @@ -176,4 +177,29 @@ protected function getWhereColumnKeyValuesByIndex($column) return $items; } + + public function prepareForFakeQuery(): array + { + $data = parent::prepareForFakeQuery(); + + if (! empty($this->taxonomies)) { + $data['wheres'] = Arr::prepend($data['wheres'], [ + 'type' => 'In', + 'column' => 'taxonomy', + 'values' => $this->taxonomies, + 'boolean' => 'and', + ]); + } + + if (! empty($this->collections)) { + $data['wheres'] = Arr::prepend($data['wheres'], [ + 'type' => 'In', + 'column' => 'collection', + 'values' => $this->collections, + 'boolean' => 'and', + ]); + } + + return $data; + } } From ce548f9a80516043f1af7fc212ccd10690519932 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:40:42 -0400 Subject: [PATCH 22/32] dont fake queries in iterator builder, it's too much. we use that for filtering pages etc. move it to just the search query. --- src/Query/IteratorBuilder.php | 24 +++++++++--------------- src/Search/QueryBuilder.php | 8 ++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Query/IteratorBuilder.php b/src/Query/IteratorBuilder.php index 728f8873641..a917ba7b3d2 100644 --- a/src/Query/IteratorBuilder.php +++ b/src/Query/IteratorBuilder.php @@ -2,12 +2,8 @@ namespace Statamic\Query; -use Statamic\Query\Concerns\FakesQueries; - abstract class IteratorBuilder extends Builder { - use FakesQueries; - protected $randomize = false; public function count() @@ -22,18 +18,16 @@ protected function getCountForPagination() public function get($columns = ['*']) { - return $this->withFakeQueryLogging(function () { - $items = $this->getFilteredItems(); - - if ($this->randomize) { - $items = $items->shuffle(); - } elseif ($orderBys = $this->orderBys) { - $sort = collect($orderBys)->map->toString()->implode('|'); - $items = $items->multisort($sort)->values(); - } + $items = $this->getFilteredItems(); - return $this->limitItems($items)->values(); - }); + if ($this->randomize) { + $items = $items->shuffle(); + } elseif ($orderBys = $this->orderBys) { + $sort = collect($orderBys)->map->toString()->implode('|'); + $items = $items->multisort($sort)->values(); + } + + return $this->limitItems($items)->values(); } public function pluck($column, $key = null) diff --git a/src/Search/QueryBuilder.php b/src/Search/QueryBuilder.php index 100f7acf835..ab4f954a5a6 100644 --- a/src/Search/QueryBuilder.php +++ b/src/Search/QueryBuilder.php @@ -4,12 +4,15 @@ use Statamic\Contracts\Search\Result; use Statamic\Data\DataCollection; +use Statamic\Query\Concerns\FakesQueries; use Statamic\Query\IteratorBuilder as BaseQueryBuilder; use Statamic\Search\Searchables\Providers; use Statamic\Support\Str; abstract class QueryBuilder extends BaseQueryBuilder { + use FakesQueries; + protected $query; protected $index; protected $withData = true; @@ -40,6 +43,11 @@ public function withoutData() return $this; } + public function get($columns = ['*']) + { + return $this->withFakeQueryLogging(fn () => parent::get($columns)); + } + public function getBaseItems() { $results = $this->getSearchResults($this->query); From 4adb4daa29f449e8dbea8b3252456998c7db7cd9 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:40:58 -0400 Subject: [PATCH 23/32] move table name up a level --- src/Search/Comb/Query.php | 5 ----- src/Search/QueryBuilder.php | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Search/Comb/Query.php b/src/Search/Comb/Query.php index 196c6d701cd..dcad27df30f 100644 --- a/src/Search/Comb/Query.php +++ b/src/Search/Comb/Query.php @@ -10,9 +10,4 @@ public function getSearchResults($query) { return $this->index->lookup($this->query); } - - public function getTableNameForFakeQuery() - { - return 'search_'.$this->index->name(); - } } diff --git a/src/Search/QueryBuilder.php b/src/Search/QueryBuilder.php index ab4f954a5a6..f2f09492dfc 100644 --- a/src/Search/QueryBuilder.php +++ b/src/Search/QueryBuilder.php @@ -81,4 +81,9 @@ protected function collect($items = []) { return new DataCollection($items); } + + public function getTableNameForFakeQuery() + { + return 'search_'.$this->index->name(); + } } From d85ab554177244ae00af293ef03c1f25e4aeecb3 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 13:41:35 -0400 Subject: [PATCH 24/32] fix nested where not showing in search queries since they arent stache builders. removed the condition entirely. not sure when there would be no builder. --- src/Query/Dumper/Concerns/DumpsWheres.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index cbe2c862a37..26578a1b8c1 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -4,7 +4,6 @@ use Illuminate\Support\Str; use Statamic\Query\Dumper\Dumper; -use Statamic\Stache\Query\Builder; trait DumpsWheres { @@ -95,10 +94,6 @@ protected function dumpNested($where): string { $query = $where['query'] ?? null; - if (! $query instanceof Builder) { - return ''; - } - return '('.(new Dumper($query, $this->bindings))->dumpWheres().')'; } From 5d5817cc951ee019afb3895b7c7f46dc4e384920 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:00:46 -0400 Subject: [PATCH 25/32] match was wrong. we wish it was that fast. --- src/Query/Concerns/FakesQueries.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Query/Concerns/FakesQueries.php b/src/Query/Concerns/FakesQueries.php index eab8360a3dd..e6d18a1992d 100644 --- a/src/Query/Concerns/FakesQueries.php +++ b/src/Query/Concerns/FakesQueries.php @@ -14,11 +14,11 @@ protected function withFakeQueryLogging(\Closure $callback) return $callback(); } - $startTime = hrtime(true); + $startTime = microtime(true); $value = $callback(); - $time = (hrtime(true) - $startTime) / 1000000; + $time = round((microtime(true) - $startTime) * 1000, 2); $bindings = collect(); From fe32412831b473d167c129582f5f0bf97abb6829 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:17:36 -0400 Subject: [PATCH 26/32] add onceWithColumns ported from laravel ... this allows to avoid the `$this->columns ?? $columns` situation. `$this->columns` will always be what you need. also fixes `$query->get(['col1', 'col2'])` not showing proper `select`ed columns within our fake queries --- src/Query/Builder.php | 15 +++++++++++++++ src/Stache/Query/Builder.php | 6 +++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Query/Builder.php b/src/Query/Builder.php index a25a266402f..bb93bd9d14e 100644 --- a/src/Query/Builder.php +++ b/src/Query/Builder.php @@ -573,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) diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index 0dccc617853..bc039a6463d 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -37,15 +37,15 @@ public function pluck($column, $key = null) public function get($columns = ['*']) { - return $this->withFakeQueryLogging(function () use ($columns) { + return $this->onceWithColumns($columns, fn () => $this->withFakeQueryLogging(function () { $items = $this->getItems($this->resolveKeys()); $items->each(fn ($item) => $item - ->selectedQueryColumns($this->columns ?? $columns) + ->selectedQueryColumns($this->columns) ->selectedQueryRelations($this->with)); return $this->collect($items)->values(); - }); + })); } abstract protected function getFilteredKeys(); From d489e6d0872747e647639f1c02302325a864d7dc Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:17:52 -0400 Subject: [PATCH 27/32] apply fake queries to pluck --- src/Stache/Query/Builder.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Stache/Query/Builder.php b/src/Stache/Query/Builder.php index bc039a6463d..dc9dbbbafc8 100644 --- a/src/Stache/Query/Builder.php +++ b/src/Stache/Query/Builder.php @@ -32,7 +32,11 @@ private function resolveKeys() public function pluck($column, $key = null) { - return $this->store->getItemValues($this->resolveKeys(), $column, $key); + return $this->onceWithColumns(array_filter([$column, $key]), function () use ($column, $key) { + return $this->withFakeQueryLogging(function () use ($column, $key) { + return $this->store->getItemValues($this->resolveKeys(), $column, $key); + }); + }); } public function get($columns = ['*']) From b9a55791c65282d34ea9b320724965d26edbe10f Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:32:20 -0400 Subject: [PATCH 28/32] nitpick --- src/Query/Concerns/FakesQueries.php | 16 +++-------- src/Query/Dumper/Concerns/DumpsWheres.php | 4 ++- src/Query/Dumper/Dumper.php | 33 ++++++++++++++++++++--- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Query/Concerns/FakesQueries.php b/src/Query/Concerns/FakesQueries.php index e6d18a1992d..26c0dc5bee6 100644 --- a/src/Query/Concerns/FakesQueries.php +++ b/src/Query/Concerns/FakesQueries.php @@ -3,7 +3,6 @@ namespace Statamic\Query\Concerns; use Illuminate\Database\Events\QueryExecuted; -use Illuminate\Support\Facades\DB; use Statamic\Query\Dumper\Dumper; trait FakesQueries @@ -20,20 +19,11 @@ protected function withFakeQueryLogging(\Closure $callback) $time = round((microtime(true) - $startTime) * 1000, 2); - $bindings = collect(); - - if (! app()->bound($key = 'fake-query-connection')) { - app()->instance($key, DB::connectUsing('fake', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - ])); - } - event(new QueryExecuted( - (new Dumper($this, $bindings))->dump(), - $bindings->all(), + ($sql = new Dumper($this))->dump(), + $sql->bindings()->all(), $time, - app($key) + $sql->connection() )); return $value; diff --git a/src/Query/Dumper/Concerns/DumpsWheres.php b/src/Query/Dumper/Concerns/DumpsWheres.php index 26578a1b8c1..18d9d25ef83 100644 --- a/src/Query/Dumper/Concerns/DumpsWheres.php +++ b/src/Query/Dumper/Concerns/DumpsWheres.php @@ -94,7 +94,9 @@ protected function dumpNested($where): string { $query = $where['query'] ?? null; - return '('.(new Dumper($query, $this->bindings))->dumpWheres().')'; + $sql = (new Dumper($query))->withBindings($this->bindings)->dumpWheres(); + + return "($sql)"; } protected function dumpDate($where) diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index 6c8b36e4db3..a8c1232cfdf 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -2,6 +2,8 @@ namespace Statamic\Query\Dumper; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; use Statamic\Query\Dumper\Concerns\DumpsQueryParts; use Statamic\Query\Dumper\Concerns\DumpsQueryValues; use Statamic\Query\Dumper\Concerns\DumpsWheres; @@ -16,10 +18,10 @@ class Dumper protected $limit; protected $offset; protected $table; + private Collection $bindings; - public function __construct( - private $query, private $bindings - ) { + public function __construct(private $query) + { $data = $query->prepareForFakeQuery(); $this->table = $this->getTableName($query); $this->wheres = $data['wheres']; @@ -27,6 +29,31 @@ public function __construct( $this->orderBys = $data['orderBys']; $this->limit = $data['limit']; $this->offset = $data['offset']; + $this->bindings = collect(); + } + + public function bindings(): Collection + { + return $this->bindings; + } + + public function withBindings(Collection $bindings): self + { + $this->bindings = $bindings; + + return $this; + } + + public function connection() + { + if (! app()->bound($key = 'fake-query-connection')) { + app()->instance($key, DB::connectUsing('fake', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + ])); + } + + return app($key); } public function dump(): string From 9bbb62c79b39958a621f54954c9249a2daf04d26 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:36:22 -0400 Subject: [PATCH 29/32] nitpick --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 2 +- src/Query/Dumper/Dumper.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 6120a40e182..aa8af7c5a64 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -8,7 +8,7 @@ trait DumpsQueryParts { - public function getTableName(Builder $class): string + protected function getTableName(Builder $class): string { if (method_exists($class, 'getTableNameForFakeQuery')) { return $class->getTableNameForFakeQuery(); diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index a8c1232cfdf..cdd1db26dbd 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -12,12 +12,12 @@ class Dumper { use DumpsQueryParts, DumpsQueryValues, DumpsWheres; - protected $wheres = []; - protected $columns = []; - protected $orderBys = []; - protected $limit; - protected $offset; - protected $table; + private $wheres = []; + private $columns = []; + private $orderBys = []; + private $limit; + private $offset; + private $table; private Collection $bindings; public function __construct(private $query) From 2975ace03ef6491eb7f9b3e7ffe4325bf1854e73 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:40:32 -0400 Subject: [PATCH 30/32] nitpick --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 8 +------- src/Query/Dumper/Dumper.php | 14 +++++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index aa8af7c5a64..64d44a93721 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -27,13 +27,7 @@ protected function getTableName(Builder $class): string protected function dumpColumns(): string { - $columns = '*'; - - if ($this->columns != null) { - $columns = implode(', ', $this->columns); - } - - return $columns; + return implode(', ', $this->columns); } protected function dumpLimits(): string diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index cdd1db26dbd..ba0962ce917 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -12,12 +12,12 @@ class Dumper { use DumpsQueryParts, DumpsQueryValues, DumpsWheres; - private $wheres = []; - private $columns = []; - private $orderBys = []; - private $limit; - private $offset; - private $table; + private array $wheres; + private array $columns; + private array $orderBys; + private ?int $limit; + private ?int $offset; + private string $table; private Collection $bindings; public function __construct(private $query) @@ -25,7 +25,7 @@ public function __construct(private $query) $data = $query->prepareForFakeQuery(); $this->table = $this->getTableName($query); $this->wheres = $data['wheres']; - $this->columns = $data['columns']; + $this->columns = $data['columns'] ?? ['*']; $this->orderBys = $data['orderBys']; $this->limit = $data['limit']; $this->offset = $data['offset']; From e191b872676d40c042ff4f55794e1d6bd6a100c4 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 15:42:10 -0400 Subject: [PATCH 31/32] nitpick --- src/Query/Dumper/Concerns/DumpsQueryParts.php | 21 ------------------- src/Query/Dumper/Dumper.php | 20 ++++++++++++++++++ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Query/Dumper/Concerns/DumpsQueryParts.php b/src/Query/Dumper/Concerns/DumpsQueryParts.php index 64d44a93721..dbc412f5a79 100644 --- a/src/Query/Dumper/Concerns/DumpsQueryParts.php +++ b/src/Query/Dumper/Concerns/DumpsQueryParts.php @@ -2,29 +2,8 @@ namespace Statamic\Query\Dumper\Concerns; -use Statamic\Contracts\Query\Builder; -use Statamic\Stache\Query\Builder as StacheQueryBuilder; -use Statamic\Support\Str; - trait DumpsQueryParts { - protected function getTableName(Builder $class): string - { - if (method_exists($class, 'getTableNameForFakeQuery')) { - return $class->getTableNameForFakeQuery(); - } - - if ($class instanceof StacheQueryBuilder) { - return Str::of(class_basename($class)) - ->before('QueryBuilder') - ->lower() - ->plural() - ->toString(); - } - - return get_class($class); - } - protected function dumpColumns(): string { return implode(', ', $this->columns); diff --git a/src/Query/Dumper/Dumper.php b/src/Query/Dumper/Dumper.php index ba0962ce917..e1432ab46d7 100644 --- a/src/Query/Dumper/Dumper.php +++ b/src/Query/Dumper/Dumper.php @@ -4,9 +4,12 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use Statamic\Contracts\Query\Builder; use Statamic\Query\Dumper\Concerns\DumpsQueryParts; use Statamic\Query\Dumper\Concerns\DumpsQueryValues; use Statamic\Query\Dumper\Concerns\DumpsWheres; +use Statamic\Stache\Query\Builder as StacheQueryBuilder; +use Statamic\Support\Str; class Dumper { @@ -69,4 +72,21 @@ public function dump(): string return $query; } + + private function getTableName(Builder $class): string + { + if (method_exists($class, 'getTableNameForFakeQuery')) { + return $class->getTableNameForFakeQuery(); + } + + if ($class instanceof StacheQueryBuilder) { + return Str::of(class_basename($class)) + ->before('QueryBuilder') + ->lower() + ->plural() + ->toString(); + } + + return get_class($class); + } } From 2c1ef7d6c54c367e31192aa35b3621160bf1a4ac Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 1 Apr 2024 16:16:18 -0400 Subject: [PATCH 32/32] just mirror the debug setting --- config/system.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/system.php b/config/system.php index af588223572..4f4911b566f 100644 --- a/config/system.php +++ b/config/system.php @@ -176,6 +176,6 @@ | */ - 'fake_sql_queries' => false, + 'fake_sql_queries' => config('app.debug'), ];