+6
-5
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user