Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
71 changes: 69 additions & 2 deletions src/Sql/AbstractSql.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,23 @@
use PhpDb\Sql\Platform\PlatformDecoratorInterface;

use function array_key_first;
use function array_map;
use function count;
use function explode;
use function get_object_vars;
use function implode;
use function is_array;
use function is_string;
use function preg_split;
use function rtrim;
use function str_replace;
use function strtolower;
use function strtoupper;
use function vsprintf;

use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;

/**
* @api
*
Expand Down Expand Up @@ -106,7 +113,7 @@
}
}

return rtrim(implode(' ', $sqls), characters: "\n ,");

Check warning on line 116 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "UnwrapRtrim": @@ @@ } } - return rtrim(implode(' ', $sqls), characters: "\n ,"); + return implode(' ', $sqls); } /**
}

/**
Expand All @@ -114,7 +121,7 @@
* @param SqlParameters $parameters
* @throws Exception\RuntimeException
*/
protected function createSqlFromSpecificationAndParameters(array|string $specifications, array $parameters): string

Check warning on line 124 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "ProtectedVisibility": @@ @@ * @PARAM SqlParameters $parameters * @throws Exception\RuntimeException */ - protected function createSqlFromSpecificationAndParameters(array|string $specifications, array $parameters): string + private function createSqlFromSpecificationAndParameters(array|string $specifications, array $parameters): string { if (is_string($specifications)) { return vsprintf($specifications, $this->flattenParameterList($parameters));
{
if (is_string($specifications)) {
return vsprintf($specifications, $this->flattenParameterList($parameters));
Expand All @@ -136,16 +143,16 @@
* @param list<ArgumentInterface> $arguments
* @return list<ArgumentInterface>
*/
protected function flattenExpressionValues(array $arguments): array

Check warning on line 146 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "ProtectedVisibility": @@ @@ * @PARAM list<ArgumentInterface> $arguments * @return list<ArgumentInterface> */ - protected function flattenExpressionValues(array $arguments): array + private function flattenExpressionValues(array $arguments): array { $hasValues = false; foreach ($arguments as $argument) {
{
$hasValues = false;

Check warning on line 148 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "FalseValue": @@ @@ */ protected function flattenExpressionValues(array $arguments): array { - $hasValues = false; + $hasValues = true; foreach ($arguments as $argument) { if (! $argument instanceof Values) { continue;
foreach ($arguments as $argument) {
if (! $argument instanceof Values) {

Check warning on line 150 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "LogicalNot": @@ @@ { $hasValues = false; foreach ($arguments as $argument) { - if (! $argument instanceof Values) { + if ($argument instanceof Values) { continue; }

Check warning on line 150 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "InstanceOf_": @@ @@ { $hasValues = false; foreach ($arguments as $argument) { - if (! $argument instanceof Values) { + if ($argument instanceof Values) { continue; }
continue;
}

$hasValues = true;
break;

Check warning on line 155 in src/Sql/AbstractSql.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "Break_": @@ @@ } $hasValues = true; - break; + continue; } if (! $hasValues) {
}

if (! $hasValues) {
Expand Down Expand Up @@ -247,7 +254,7 @@
$parameterContainer,
)
: $platform->quoteValue((string) $argument->getValue()),
$argument instanceof Identifier => $platform->quoteIdentifierInFragment($argument->getValue()),
$argument instanceof Identifier => $this->quoteIdentifierArgument($argument->getValue(), $platform),
$argument instanceof Literal => $argument->getValue(),
$argument instanceof Identifiers => $this->processIdentifiersArgument($argument, $platform),
$argument instanceof SelectArgument => $this->processExpressionOrSelect(
Expand Down Expand Up @@ -330,7 +337,7 @@
$processedIdentifiers = [];

foreach ($identifiers as $identifier) {
$processedIdentifiers[] = $platform->quoteIdentifierInFragment($identifier);
$processedIdentifiers[] = $this->quoteIdentifierArgument($identifier, $platform);
}

return implode(', ', $processedIdentifiers);
Expand Down Expand Up @@ -555,6 +562,66 @@
throw Exception\RuntimeException::forUnsupportedParameterCount();
}

/**
* Quote an identifier argument, keeping fragment quoting wherever it renders valid SQL
*
* Fragment quoting leaves spaces, dots, `*` and `AS` bare and quotes every other piece the
* platform splits out, so a name holding any other delimiter renders as adjacent quoted
* pieces, and names separated only by spaces render as a bare sequence of identifiers. Both
* are invalid, so such a name is quoted whole as an identifier chain instead, split only on
* the qualifier dot with a `*` part left bare.
*/
private function quoteIdentifierArgument(string $identifier, PlatformInterface $platform): string
{
$isValidFragment = true;
$previousIsName = false;
$afterSpace = false;

foreach ((array) preg_split(
'/([ .*])/',
$identifier,
-1,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY,
) as $part) {
if (' ' === $part) {
$afterSpace = true;
continue;
}

$isName = '.' !== $part && '*' !== $part && 'as' !== strtolower((string) $part);
if (
$isName
&& (
(
$previousIsName
&& $afterSpace
)
|| $platform->quoteIdentifierInFragment((string) $part) !== $platform->quoteIdentifier(
(string) $part,
)
)
) {
$isValidFragment = false;
break;
}

$previousIsName = $isName;
$afterSpace = false;
}

if ($isValidFragment) {
return $platform->quoteIdentifierInFragment($identifier);
}

return implode(
$platform->getIdentifierSeparator(),
array_map(
static fn(string $part): string => '*' === $part ? $part : $platform->quoteIdentifier($part),
explode('.', $identifier),
),
);
}

private function quoteJoinTableIdentifier(TableIdentifier $identifier, PlatformInterface $platform): string
{
[$table, $schema] = $identifier->getTableAndSchema();
Expand Down
Loading
Loading