diff --git a/src/Database/SqlPreprocessor.php b/src/Database/SqlPreprocessor.php index d87f6dfbc..ad3ef7f43 100644 --- a/src/Database/SqlPreprocessor.php +++ b/src/Database/SqlPreprocessor.php @@ -33,6 +33,15 @@ class SqlPreprocessor 'GROUP BY' => 'order', ]; + private const PARAMETRIC_COMMANDS = [ + 'SELECT' => 1, + 'INSERT' => 1, + 'UPDATE' => 1, + 'DELETE' => 1, + 'REPLACE' => 1, + 'EXPLAIN' => 1, + ]; + /** @var Connection */ private $connection; @@ -48,6 +57,9 @@ class SqlPreprocessor /** @var int */ private $counter; + /** @var bool */ + private $useParams; + /** @var string values|set|and|order */ private $arrayMode; @@ -62,13 +74,14 @@ public function __construct(Connection $connection) /** * @return array of [sql, params] */ - public function process(array $params): array + public function process(array $params, bool $useParams = false): array { $this->params = $params; $this->counter = 0; $prev = -1; $this->remaining = []; $this->arrayMode = null; + $this->useParams = $useParams; $res = []; while ($this->counter < count($params)) { @@ -83,7 +96,7 @@ public function process(array $params): array $this->arrayMode = null; $res[] = Nette\Utils\Strings::replace( $param, - '~\'[^\']*+\'|"[^"]*+"|\?[a-z]*|^\s*+(?:INSERT|REPLACE)\b|\b(?:SET|WHERE|HAVING|ORDER BY|GROUP BY|KEY UPDATE)(?=\s*\z|\s*\?)|/\*.*?\*/|--[^\n]*~si', + '~\'[^\']*+\'|"[^"]*+"|\?[a-z]*|^\s*+(?:SELECT|INSERT|UPDATE|DELETE|REPLACE|EXPLAIN)\b|\b(?:SET|WHERE|HAVING|ORDER BY|GROUP BY|KEY UPDATE)(?=\s*\z|\s*\?)|/\*.*?\*/|--[^\n]*~si', [$this, 'callback'] ); } else { @@ -110,7 +123,8 @@ public function callback(array $m): string } else { // command $cmd = ltrim(strtoupper($m)); - $this->arrayMode = self::ARRAY_MODES[$cmd]; + $this->arrayMode = self::ARRAY_MODES[$cmd] ?? null; + $this->useParams = isset(self::PARAMETRIC_COMMANDS[$cmd]) || $this->useParams; return $m; } } @@ -120,8 +134,15 @@ private function formatValue($value, string $mode = null): string { if (!$mode || $mode === 'auto') { if (is_scalar($value) || is_resource($value)) { - $this->remaining[] = $value; - return '?'; + if ($this->useParams) { + $this->remaining[] = $value; + return '?'; + } else { + if (is_resource($value)) { + $value = stream_get_contents($value); + } + return $this->connection->quote($value); + } } elseif ($value === null) { return 'NULL'; @@ -132,7 +153,7 @@ private function formatValue($value, string $mode = null): string } elseif ($value instanceof SqlLiteral) { $prep = clone $this; - [$res, $params] = $prep->process(array_merge([$value->__toString()], $value->getParameters())); + [$res, $params] = $prep->process(array_merge([$value->__toString()], $value->getParameters()), $this->useParams); $this->remaining = array_merge($this->remaining, $params); return $res; diff --git a/tests/Database/SqlPreprocessor.phpt b/tests/Database/SqlPreprocessor.phpt index 38b0c0f58..90d277812 100644 --- a/tests/Database/SqlPreprocessor.phpt +++ b/tests/Database/SqlPreprocessor.phpt @@ -309,8 +309,8 @@ test(function () use ($preprocessor) { // insert [$sql, $params] = $preprocessor->process(['/* comment */ INSERT INTO author', ['name' => 'Catelyn Stark'], ]); - Assert::same(reformat('/* comment */ INSERT INTO author [name]=?'), $sql); // autodetection not used - Assert::same(['Catelyn Stark'], $params); + Assert::same(reformat("/* comment */ INSERT INTO author [name]='Catelyn Stark'"), $sql); // autodetection not used + Assert::same([], $params); });