PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.35.1
MailPoet – Newsletters, Email Marketing, and Automation v5.35.1
5.36.1 5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor-prefixed / doctrine / orm / src / Tools / Pagination / LimitSubqueryOutputWalker.php
mailpoet / vendor-prefixed / doctrine / orm / src / Tools / Pagination Last commit date
Exception 9 months ago CountOutputWalker.php 9 months ago CountWalker.php 9 months ago LimitSubqueryOutputWalker.php 9 months ago LimitSubqueryWalker.php 9 months ago Paginator.php 9 months ago RootTypeWalker.php 9 months ago RowNumberOverFunction.php 9 months ago WhereInWalker.php 9 months ago index.php 9 months ago
LimitSubqueryOutputWalker.php
369 lines
1 <?php
2 declare (strict_types=1);
3 namespace MailPoetVendor\Doctrine\ORM\Tools\Pagination;
4 if (!defined('ABSPATH')) exit;
5 use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform;
6 use MailPoetVendor\Doctrine\DBAL\Platforms\DB2Platform;
7 use MailPoetVendor\Doctrine\DBAL\Platforms\OraclePlatform;
8 use MailPoetVendor\Doctrine\DBAL\Platforms\PostgreSQLPlatform;
9 use MailPoetVendor\Doctrine\DBAL\Platforms\SQLAnywherePlatform;
10 use MailPoetVendor\Doctrine\DBAL\Platforms\SQLServerPlatform;
11 use MailPoetVendor\Doctrine\ORM\EntityManagerInterface;
12 use MailPoetVendor\Doctrine\ORM\Mapping\QuoteStrategy;
13 use MailPoetVendor\Doctrine\ORM\OptimisticLockException;
14 use MailPoetVendor\Doctrine\ORM\Query;
15 use MailPoetVendor\Doctrine\ORM\Query\AST\DeleteStatement;
16 use MailPoetVendor\Doctrine\ORM\Query\AST\OrderByClause;
17 use MailPoetVendor\Doctrine\ORM\Query\AST\PathExpression;
18 use MailPoetVendor\Doctrine\ORM\Query\AST\SelectExpression;
19 use MailPoetVendor\Doctrine\ORM\Query\AST\SelectStatement;
20 use MailPoetVendor\Doctrine\ORM\Query\AST\UpdateStatement;
21 use MailPoetVendor\Doctrine\ORM\Query\Exec\SingleSelectSqlFinalizer;
22 use MailPoetVendor\Doctrine\ORM\Query\Exec\SqlFinalizer;
23 use MailPoetVendor\Doctrine\ORM\Query\Parser;
24 use MailPoetVendor\Doctrine\ORM\Query\ParserResult;
25 use MailPoetVendor\Doctrine\ORM\Query\QueryException;
26 use MailPoetVendor\Doctrine\ORM\Query\ResultSetMapping;
27 use MailPoetVendor\Doctrine\ORM\Query\SqlOutputWalker;
28 use LogicException;
29 use RuntimeException;
30 use function array_diff;
31 use function array_keys;
32 use function assert;
33 use function count;
34 use function implode;
35 use function in_array;
36 use function is_string;
37 use function method_exists;
38 use function preg_replace;
39 use function reset;
40 use function sprintf;
41 use function strrpos;
42 use function substr;
43 class LimitSubqueryOutputWalker extends SqlOutputWalker
44 {
45 private const ORDER_BY_PATH_EXPRESSION = '/(?<![a-z0-9_])%s\\.%s(?![a-z0-9_])/i';
46 private $platform;
47 private $rsm;
48 private $firstResult;
49 private $maxResults;
50 private $em;
51 private $quoteStrategy;
52 private $orderByPathExpressions = [];
53 private $inSubSelect = \false;
54 public function __construct($query, $parserResult, array $queryComponents)
55 {
56 $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
57 $this->rsm = $parserResult->getResultSetMapping();
58 $cloneQuery = clone $query;
59 $cloneQuery->setParameters(clone $query->getParameters());
60 $cloneQuery->setCacheable(\false);
61 foreach ($query->getHints() as $name => $value) {
62 $cloneQuery->setHint($name, $value);
63 }
64 // Reset limit and offset
65 $this->firstResult = $cloneQuery->getFirstResult();
66 $this->maxResults = $cloneQuery->getMaxResults();
67 $cloneQuery->setFirstResult(0)->setMaxResults(null);
68 $this->em = $cloneQuery->getEntityManager();
69 $this->quoteStrategy = $this->em->getConfiguration()->getQuoteStrategy();
70 parent::__construct($cloneQuery, $parserResult, $queryComponents);
71 }
72 private function platformSupportsRowNumber() : bool
73 {
74 return $this->platform instanceof PostgreSQLPlatform || $this->platform instanceof SQLServerPlatform || $this->platform instanceof OraclePlatform || $this->platform instanceof SQLAnywherePlatform || $this->platform instanceof DB2Platform || method_exists($this->platform, 'supportsRowNumberFunction') && $this->platform->supportsRowNumberFunction();
75 }
76 private function rebuildOrderByForRowNumber(SelectStatement $AST) : void
77 {
78 $orderByClause = $AST->orderByClause;
79 $selectAliasToExpressionMap = [];
80 // Get any aliases that are available for select expressions.
81 foreach ($AST->selectClause->selectExpressions as $selectExpression) {
82 if ($selectExpression->fieldIdentificationVariable !== null) {
83 $selectAliasToExpressionMap[$selectExpression->fieldIdentificationVariable] = $selectExpression->expression;
84 }
85 }
86 // Rebuild string orderby expressions to use the select expression they're referencing
87 foreach ($orderByClause->orderByItems as $orderByItem) {
88 if (is_string($orderByItem->expression) && isset($selectAliasToExpressionMap[$orderByItem->expression])) {
89 $orderByItem->expression = $selectAliasToExpressionMap[$orderByItem->expression];
90 }
91 }
92 $func = new RowNumberOverFunction('dctrn_rownum');
93 $func->orderByClause = $AST->orderByClause;
94 $AST->selectClause->selectExpressions[] = new SelectExpression($func, 'dctrn_rownum', \true);
95 // No need for an order by clause, we'll order by rownum in the outer query.
96 $AST->orderByClause = null;
97 }
98 public function walkSelectStatement(SelectStatement $AST)
99 {
100 $sqlFinalizer = $this->getFinalizer($AST);
101 $query = $this->getQuery();
102 $abstractSqlExecutor = $sqlFinalizer->createExecutor($query);
103 return $abstractSqlExecutor->getSqlStatements();
104 }
105 public function getFinalizer($AST) : SqlFinalizer
106 {
107 if (!$AST instanceof SelectStatement) {
108 throw new LogicException(self::class . ' is to be used on SelectStatements only');
109 }
110 if ($this->platformSupportsRowNumber()) {
111 $sql = $this->createSqlWithRowNumber($AST);
112 } else {
113 $sql = $this->createSqlWithoutRowNumber($AST);
114 }
115 return new SingleSelectSqlFinalizer($sql);
116 }
117 public function walkSelectStatementWithRowNumber(SelectStatement $AST)
118 {
119 // Apply the limit and offset.
120 return $this->platform->modifyLimitQuery($this->createSqlWithRowNumber($AST), $this->maxResults, $this->firstResult);
121 }
122 private function createSqlWithRowNumber(SelectStatement $AST) : string
123 {
124 $hasOrderBy = \false;
125 $outerOrderBy = ' ORDER BY dctrn_minrownum ASC';
126 $orderGroupBy = '';
127 if ($AST->orderByClause instanceof OrderByClause) {
128 $hasOrderBy = \true;
129 $this->rebuildOrderByForRowNumber($AST);
130 }
131 $innerSql = $this->getInnerSQL($AST);
132 $sqlIdentifier = $this->getSQLIdentifier($AST);
133 if ($hasOrderBy) {
134 $orderGroupBy = ' GROUP BY ' . implode(', ', $sqlIdentifier);
135 $sqlIdentifier[] = 'MIN(' . $this->walkResultVariable('dctrn_rownum') . ') AS dctrn_minrownum';
136 }
137 // Build the counter query
138 $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), $innerSql);
139 if ($hasOrderBy) {
140 $sql .= $orderGroupBy . $outerOrderBy;
141 }
142 // Add the columns to the ResultSetMapping. It's not really nice but
143 // it works. Preferably I'd clear the RSM or simply create a new one
144 // but that is not possible from inside the output walker, so we dirty
145 // up the one we have.
146 foreach ($sqlIdentifier as $property => $alias) {
147 $this->rsm->addScalarResult($alias, $property);
148 }
149 return $sql;
150 }
151 public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, $addMissingItemsFromOrderByToSelect = \true)
152 {
153 // Apply the limit and offset.
154 return $this->platform->modifyLimitQuery($this->createSqlWithoutRowNumber($AST, $addMissingItemsFromOrderByToSelect), $this->maxResults, $this->firstResult);
155 }
156 private function createSqlWithoutRowNumber(SelectStatement $AST, bool $addMissingItemsFromOrderByToSelect = \true) : string
157 {
158 // We don't want to call this recursively!
159 if ($AST->orderByClause instanceof OrderByClause && $addMissingItemsFromOrderByToSelect) {
160 // In the case of ordering a query by columns from joined tables, we
161 // must add those columns to the select clause of the query BEFORE
162 // the SQL is generated.
163 $this->addMissingItemsFromOrderByToSelect($AST);
164 }
165 // Remove order by clause from the inner query
166 // It will be re-appended in the outer select generated by this method
167 $orderByClause = $AST->orderByClause;
168 $AST->orderByClause = null;
169 $innerSql = $this->getInnerSQL($AST);
170 $sqlIdentifier = $this->getSQLIdentifier($AST);
171 // Build the counter query
172 $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), $innerSql);
173 // https://github.com/doctrine/orm/issues/2630
174 $sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause);
175 // Add the columns to the ResultSetMapping. It's not really nice but
176 // it works. Preferably I'd clear the RSM or simply create a new one
177 // but that is not possible from inside the output walker, so we dirty
178 // up the one we have.
179 foreach ($sqlIdentifier as $property => $alias) {
180 $this->rsm->addScalarResult($alias, $property);
181 }
182 // Restore orderByClause
183 $AST->orderByClause = $orderByClause;
184 return $sql;
185 }
186 private function addMissingItemsFromOrderByToSelect(SelectStatement $AST) : void
187 {
188 $this->orderByPathExpressions = [];
189 // We need to do this in another walker because otherwise we'll end up
190 // polluting the state of this one.
191 $walker = clone $this;
192 // This will populate $orderByPathExpressions via
193 // LimitSubqueryOutputWalker::walkPathExpression, which will be called
194 // as the select statement is walked. We'll end up with an array of all
195 // path expressions referenced in the query.
196 $walker->walkSelectStatementWithoutRowNumber($AST, \false);
197 $orderByPathExpressions = $walker->getOrderByPathExpressions();
198 // Get a map of referenced identifiers to field names.
199 $selects = [];
200 foreach ($orderByPathExpressions as $pathExpression) {
201 assert($pathExpression->field !== null);
202 $idVar = $pathExpression->identificationVariable;
203 $field = $pathExpression->field;
204 if (!isset($selects[$idVar])) {
205 $selects[$idVar] = [];
206 }
207 $selects[$idVar][$field] = \true;
208 }
209 // Loop the select clause of the AST and exclude items from $select
210 // that are already being selected in the query.
211 foreach ($AST->selectClause->selectExpressions as $selectExpression) {
212 if ($selectExpression instanceof SelectExpression) {
213 $idVar = $selectExpression->expression;
214 if (!is_string($idVar)) {
215 continue;
216 }
217 $field = $selectExpression->fieldIdentificationVariable;
218 if ($field === null) {
219 // No need to add this select, as we're already fetching the whole object.
220 unset($selects[$idVar]);
221 } else {
222 unset($selects[$idVar][$field]);
223 }
224 }
225 }
226 // Add select items which were not excluded to the AST's select clause.
227 foreach ($selects as $idVar => $fields) {
228 $AST->selectClause->selectExpressions[] = new SelectExpression($idVar, null, \true);
229 }
230 }
231 private function preserveSqlOrdering(array $sqlIdentifier, string $innerSql, string $sql, ?OrderByClause $orderByClause) : string
232 {
233 // If the sql statement has an order by clause, we need to wrap it in a new select distinct statement
234 if (!$orderByClause) {
235 return $sql;
236 }
237 // now only select distinct identifier
238 return sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', implode(', ', $sqlIdentifier), $this->recreateInnerSql($orderByClause, $sqlIdentifier, $innerSql));
239 }
240 private function recreateInnerSql(OrderByClause $orderByClause, array $identifiers, string $innerSql) : string
241 {
242 [$searchPatterns, $replacements] = $this->generateSqlAliasReplacements();
243 $orderByItems = [];
244 foreach ($orderByClause->orderByItems as $orderByItem) {
245 // Walk order by item to get string representation of it and
246 // replace path expressions in the order by clause with their column alias
247 $orderByItemString = preg_replace($searchPatterns, $replacements, $this->walkOrderByItem($orderByItem));
248 $orderByItems[] = $orderByItemString;
249 $identifier = substr($orderByItemString, 0, strrpos($orderByItemString, ' '));
250 if (!in_array($identifier, $identifiers, \true)) {
251 $identifiers[] = $identifier;
252 }
253 }
254 return $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result_inner ORDER BY %s', implode(', ', $identifiers), $innerSql, implode(', ', $orderByItems));
255 }
256 private function generateSqlAliasReplacements() : array
257 {
258 $aliasMap = $searchPatterns = $replacements = $metadataList = [];
259 // Generate DQL alias -> SQL table alias mapping
260 foreach (array_keys($this->rsm->aliasMap) as $dqlAlias) {
261 $metadataList[$dqlAlias] = $class = $this->getMetadataForDqlAlias($dqlAlias);
262 $aliasMap[$dqlAlias] = $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
263 }
264 // Generate search patterns for each field's path expression in the order by clause
265 foreach ($this->rsm->fieldMappings as $fieldAlias => $fieldName) {
266 $dqlAliasForFieldAlias = $this->rsm->columnOwnerMap[$fieldAlias];
267 $class = $metadataList[$dqlAliasForFieldAlias];
268 // If the field is from a joined child table, we won't be ordering on it.
269 if (!isset($class->fieldMappings[$fieldName])) {
270 continue;
271 }
272 $fieldMapping = $class->fieldMappings[$fieldName];
273 // Get the proper column name as will appear in the select list
274 $columnName = $this->quoteStrategy->getColumnName($fieldName, $metadataList[$dqlAliasForFieldAlias], $this->em->getConnection()->getDatabasePlatform());
275 // Get the SQL table alias for the entity and field
276 $sqlTableAliasForFieldAlias = $aliasMap[$dqlAliasForFieldAlias];
277 if (isset($fieldMapping['declared']) && $fieldMapping['declared'] !== $class->name) {
278 // Field was declared in a parent class, so we need to get the proper SQL table alias
279 // for the joined parent table.
280 $otherClassMetadata = $this->em->getClassMetadata($fieldMapping['declared']);
281 if (!$otherClassMetadata->isMappedSuperclass) {
282 $sqlTableAliasForFieldAlias = $this->getSQLTableAlias($otherClassMetadata->getTableName(), $dqlAliasForFieldAlias);
283 }
284 }
285 // Compose search and replace patterns
286 $searchPatterns[] = sprintf(self::ORDER_BY_PATH_EXPRESSION, $sqlTableAliasForFieldAlias, $columnName);
287 $replacements[] = $fieldAlias;
288 }
289 return [$searchPatterns, $replacements];
290 }
291 public function getOrderByPathExpressions()
292 {
293 return $this->orderByPathExpressions;
294 }
295 private function getInnerSQL(SelectStatement $AST) : string
296 {
297 // Set every select expression as visible(hidden = false) to
298 // make $AST have scalar mappings properly - this is relevant for referencing selected
299 // fields from outside the subquery, for example in the ORDER BY segment
300 $hiddens = [];
301 foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
302 $hiddens[$idx] = $expr->hiddenAliasResultVariable;
303 $expr->hiddenAliasResultVariable = \false;
304 }
305 $innerSql = parent::walkSelectStatement($AST);
306 // Restore hiddens
307 foreach ($AST->selectClause->selectExpressions as $idx => $expr) {
308 $expr->hiddenAliasResultVariable = $hiddens[$idx];
309 }
310 return $innerSql;
311 }
312 private function getSQLIdentifier(SelectStatement $AST) : array
313 {
314 // Find out the SQL alias of the identifier column of the root entity.
315 // It may be possible to make this work with multiple root entities but that
316 // would probably require issuing multiple queries or doing a UNION SELECT.
317 // So for now, it's not supported.
318 // Get the root entity and alias from the AST fromClause.
319 $from = $AST->fromClause->identificationVariableDeclarations;
320 if (count($from) !== 1) {
321 throw new RuntimeException('Cannot count query which selects two FROM components, cannot make distinction');
322 }
323 $fromRoot = reset($from);
324 $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;
325 $rootClass = $this->getMetadataForDqlAlias($rootAlias);
326 $rootIdentifier = $rootClass->identifier;
327 // For every identifier, find out the SQL alias by combing through the ResultSetMapping
328 $sqlIdentifier = [];
329 foreach ($rootIdentifier as $property) {
330 if (isset($rootClass->fieldMappings[$property])) {
331 foreach (array_keys($this->rsm->fieldMappings, $property, \true) as $alias) {
332 if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {
333 $sqlIdentifier[$property] = $alias;
334 }
335 }
336 }
337 if (isset($rootClass->associationMappings[$property])) {
338 $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name'];
339 foreach (array_keys($this->rsm->metaMappings, $joinColumn, \true) as $alias) {
340 if ($this->rsm->columnOwnerMap[$alias] === $rootAlias) {
341 $sqlIdentifier[$property] = $alias;
342 }
343 }
344 }
345 }
346 if (count($sqlIdentifier) === 0) {
347 throw new RuntimeException('The Paginator does not support Queries which only yield ScalarResults.');
348 }
349 if (count($rootIdentifier) !== count($sqlIdentifier)) {
350 throw new RuntimeException(sprintf('Not all identifier properties can be found in the ResultSetMapping: %s', implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier)))));
351 }
352 return $sqlIdentifier;
353 }
354 public function walkPathExpression($pathExpr)
355 {
356 if (!$this->inSubSelect && !$this->platformSupportsRowNumber() && !in_array($pathExpr, $this->orderByPathExpressions, \true)) {
357 $this->orderByPathExpressions[] = $pathExpr;
358 }
359 return parent::walkPathExpression($pathExpr);
360 }
361 public function walkSubSelect($subselect)
362 {
363 $this->inSubSelect = \true;
364 $sql = parent::walkSubselect($subselect);
365 $this->inSubSelect = \false;
366 return $sql;
367 }
368 }
369