Исправление Grammar
CI Tests / test (push) Has been cancelled

This commit is contained in:
Dmitry Rashupkin
2026-04-29 15:32:55 +03:00
parent 14345b2622
commit 45c30e8980
2 changed files with 43 additions and 11 deletions
+6 -5
View File
@@ -11,6 +11,7 @@ use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\Paginator;
use PhpClickHouseLaravel\Exceptions\QueryException;
use Tinderbox\ClickhouseBuilder\Query\BaseBuilder;
use Tinderbox\ClickhouseBuilder\Query\Expression;
class Builder extends BaseBuilder
{
@@ -127,11 +128,11 @@ class Builder extends BaseBuilder
* Добавляет сырое SQL-выражение в SELECT без автоматического экранирования ``.
* Необходимо для агрегатных функций: count(), avg(), uniq(), toStartOfDay() и т.д.
*/
public function selectRaw(string $expression, array $bindings = []): self
{
// Expression сообщает Grammar'у: "не трогай эту строку кавычками"
return $this->select(new Expression($expression));
}
public function selectRaw(string $expression, array $bindings = []): self
{
// Expression сообщает Grammar'у: "не трогай эту строку кавычками"
return $this->select(new Expression($expression));
}
/**
* Получает первую строку результата как ассоциативный массив или null.
* Аналог Eloquent::first()
+37 -6
View File
@@ -2,23 +2,54 @@
namespace PhpClickHouseLaravel;
use Tinderbox\ClickhouseBuilder\Query\Expression;
use Tinderbox\ClickhouseBuilder\Query\Grammar as BaseGrammar;
class Grammar extends \Tinderbox\ClickhouseBuilder\Query\Grammar
class Grammar extends BaseGrammar
{
public function __construct()
{
$this->selectComponents[] = 'settings';
// Регистрируем компонент SETTINGS, если его ещё нет
if (!in_array('settings', $this->selectComponents, true)) {
$this->selectComponents[] = 'settings';
}
}
/**
* Компиляция SETTINGS компонента
*/
public function compileSettingsComponent($_, array $settings): string
{
if (empty($settings)) {
return "";
return '';
}
$strAr = [];
$parts = [];
foreach ($settings as $k => $v) {
$strAr[] = is_int($v) ? "$k=$v" : "$k='$v'";
$parts[] = is_int($v) ? "{$k}={$v}" : "{$k}='{$v}'";
}
return 'SETTINGS ' . implode(', ', $strAr);
return 'SETTINGS ' . implode(', ', $parts);
}
/**
* 🔑 Ключевое исправление: предотвращает экранирование обратными кавычками ``
* для сырых SQL-выражений и агрегатных функций.
*/
public function wrap($value, $prefixAlias = false): string
{
// 1. Если передан объект Expression (сырой SQL), возвращаем как есть
if ($value instanceof Expression) {
return $value->getValue();
}
// 2. Фоллбэк: если строка содержит SQL-функции или алиасы, не экранируем
if (is_string($value)) {
$lower = strtolower($value);
if (str_contains($value, '(') || str_contains($lower, ' as ')) {
return $value;
}
}
// 3. Для обычных колонок используем стандартное экранирование
return parent::wrap($value, $prefixAlias);
}
}