diff --git a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php index d349d9ad80..e26c1ee303 100644 --- a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php +++ b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php @@ -203,7 +203,17 @@ abstract class BaseQueryComposer implements QueryComposer protected function quoteColumn(string $column): string { - return $column; + $list = explode('.', $column); + + $list = array_map(function ($item) { + if ($this->sanitize($item) === $item) { + return $item; + } + + return $this->quoteIdentifier($item); + }, $list); + + return implode('.', $list); } protected function getSeed(?string $entityType): Entity @@ -1266,23 +1276,21 @@ abstract class BaseQueryComposer implements QueryComposer $relName = null; $entityType = $entity->getEntityType(); - if (strpos($argument, '.')) { + if (strpos($argument, '.') && !str_starts_with($argument, '#')) { [$relName, $attribute] = explode('.', $argument); } - if (!empty($relName)) { - /** @noinspection PhpDeprecationInspection */ + if ($relName) { $relName = $this->sanitize($relName); } $isAlias = false; - if (!empty($attribute)) { + if ($attribute !== '') { $isAlias = str_starts_with($attribute, '#'); - /** @noinspection PhpDeprecationInspection */ $attribute = $isAlias ? - $this->sanitizeSelectAlias($attribute) : + $this->sanitizeSelectAliasStrict($attribute) : $this->sanitize($attribute); } @@ -2997,6 +3005,17 @@ abstract class BaseQueryComposer implements QueryComposer return preg_replace('/[^A-Za-z0-9_]+/', '', $string) ?? ''; } + private function sanitizeSelectAliasStrict(string $string): string + { + $string = preg_replace('/[^A-Za-z0-9_\-]+/', '', $string) ?? ''; + + if (strlen($string) > $this->aliasMaxLength) { + $string = substr($string, 0, $this->aliasMaxLength); + } + + return $string; + } + /** * Sanitize an alias for a SELECT statement. */ diff --git a/application/Espo/ORM/QueryComposer/PostgresqlQueryComposer.php b/application/Espo/ORM/QueryComposer/PostgresqlQueryComposer.php index b0e00e5384..9e5cded9d2 100644 --- a/application/Espo/ORM/QueryComposer/PostgresqlQueryComposer.php +++ b/application/Espo/ORM/QueryComposer/PostgresqlQueryComposer.php @@ -90,7 +90,7 @@ class PostgresqlQueryComposer extends BaseQueryComposer protected function quoteColumn(string $column): string { $list = explode('.', $column); - $list = array_map(fn ($item) => '"' . $item . '"', $list); + $list = array_map(fn ($item) => $this->quoteIdentifier($item), $list); return implode('.', $list); } diff --git a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php index 364abbdd2e..c4ea5a7792 100644 --- a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php +++ b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php @@ -3642,7 +3642,6 @@ class MysqlQueryComposerTest extends TestCase $this->assertEquals($expectedSql, $sql); } - public function testWithRecursive1(): void { $query = (new SelectBuilder())