diff options
Diffstat (limited to 'vendor/doctrine/orm/src/Query/Exec')
5 files changed, 445 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Query/Exec/AbstractSqlExecutor.php b/vendor/doctrine/orm/src/Query/Exec/AbstractSqlExecutor.php new file mode 100644 index 0000000..101bf26 --- /dev/null +++ b/vendor/doctrine/orm/src/Query/Exec/AbstractSqlExecutor.php | |||
@@ -0,0 +1,61 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\ORM\Query\Exec; | ||
6 | |||
7 | use Doctrine\DBAL\ArrayParameterType; | ||
8 | use Doctrine\DBAL\Cache\QueryCacheProfile; | ||
9 | use Doctrine\DBAL\Connection; | ||
10 | use Doctrine\DBAL\ParameterType; | ||
11 | use Doctrine\DBAL\Result; | ||
12 | use Doctrine\DBAL\Types\Type; | ||
13 | |||
14 | /** | ||
15 | * Base class for SQL statement executors. | ||
16 | * | ||
17 | * @link http://www.doctrine-project.org | ||
18 | * | ||
19 | * @todo Rename: AbstractSQLExecutor | ||
20 | * @psalm-type WrapperParameterType = string|Type|ParameterType::*|ArrayParameterType::* | ||
21 | * @psalm-type WrapperParameterTypeArray = array<int<0, max>, WrapperParameterType>|array<string, WrapperParameterType> | ||
22 | */ | ||
23 | abstract class AbstractSqlExecutor | ||
24 | { | ||
25 | /** @var list<string>|string */ | ||
26 | protected array|string $sqlStatements; | ||
27 | |||
28 | protected QueryCacheProfile|null $queryCacheProfile = null; | ||
29 | |||
30 | /** | ||
31 | * Gets the SQL statements that are executed by the executor. | ||
32 | * | ||
33 | * @return list<string>|string All the SQL update statements. | ||
34 | */ | ||
35 | public function getSqlStatements(): array|string | ||
36 | { | ||
37 | return $this->sqlStatements; | ||
38 | } | ||
39 | |||
40 | public function setQueryCacheProfile(QueryCacheProfile $qcp): void | ||
41 | { | ||
42 | $this->queryCacheProfile = $qcp; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * Do not use query cache | ||
47 | */ | ||
48 | public function removeQueryCacheProfile(): void | ||
49 | { | ||
50 | $this->queryCacheProfile = null; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * Executes all sql statements. | ||
55 | * | ||
56 | * @param Connection $conn The database connection that is used to execute the queries. | ||
57 | * @param list<mixed>|array<string, mixed> $params The parameters. | ||
58 | * @psalm-param WrapperParameterTypeArray $types The parameter types. | ||
59 | */ | ||
60 | abstract public function execute(Connection $conn, array $params, array $types): Result|int; | ||
61 | } | ||
diff --git a/vendor/doctrine/orm/src/Query/Exec/MultiTableDeleteExecutor.php b/vendor/doctrine/orm/src/Query/Exec/MultiTableDeleteExecutor.php new file mode 100644 index 0000000..6096462 --- /dev/null +++ b/vendor/doctrine/orm/src/Query/Exec/MultiTableDeleteExecutor.php | |||
@@ -0,0 +1,131 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\ORM\Query\Exec; | ||
6 | |||
7 | use Doctrine\DBAL\Connection; | ||
8 | use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; | ||
9 | use Doctrine\DBAL\Types\Type; | ||
10 | use Doctrine\ORM\Query\AST; | ||
11 | use Doctrine\ORM\Query\AST\DeleteStatement; | ||
12 | use Doctrine\ORM\Query\SqlWalker; | ||
13 | use Doctrine\ORM\Utility\PersisterHelper; | ||
14 | use Throwable; | ||
15 | |||
16 | use function array_reverse; | ||
17 | use function implode; | ||
18 | |||
19 | /** | ||
20 | * Executes the SQL statements for bulk DQL DELETE statements on classes in | ||
21 | * Class Table Inheritance (JOINED). | ||
22 | * | ||
23 | * @link http://www.doctrine-project.org | ||
24 | */ | ||
25 | class MultiTableDeleteExecutor extends AbstractSqlExecutor | ||
26 | { | ||
27 | private readonly string $createTempTableSql; | ||
28 | private readonly string $dropTempTableSql; | ||
29 | private readonly string $insertSql; | ||
30 | |||
31 | /** | ||
32 | * Initializes a new <tt>MultiTableDeleteExecutor</tt>. | ||
33 | * | ||
34 | * Internal note: Any SQL construction and preparation takes place in the constructor for | ||
35 | * best performance. With a query cache the executor will be cached. | ||
36 | * | ||
37 | * @param DeleteStatement $AST The root AST node of the DQL query. | ||
38 | * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. | ||
39 | */ | ||
40 | public function __construct(AST\Node $AST, SqlWalker $sqlWalker) | ||
41 | { | ||
42 | $em = $sqlWalker->getEntityManager(); | ||
43 | $conn = $em->getConnection(); | ||
44 | $platform = $conn->getDatabasePlatform(); | ||
45 | $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); | ||
46 | |||
47 | if ($conn instanceof PrimaryReadReplicaConnection) { | ||
48 | $conn->ensureConnectedToPrimary(); | ||
49 | } | ||
50 | |||
51 | $primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName); | ||
52 | $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; | ||
53 | $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); | ||
54 | |||
55 | $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); | ||
56 | $idColumnNames = $rootClass->getIdentifierColumnNames(); | ||
57 | $idColumnList = implode(', ', $idColumnNames); | ||
58 | |||
59 | // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() | ||
60 | $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); | ||
61 | |||
62 | $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' | ||
63 | . ' SELECT t0.' . implode(', t0.', $idColumnNames); | ||
64 | |||
65 | $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); | ||
66 | $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); | ||
67 | $insertSql .= $sqlWalker->walkFromClause($fromClause); | ||
68 | |||
69 | // Append WHERE clause, if there is one. | ||
70 | if ($AST->whereClause) { | ||
71 | $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); | ||
72 | } | ||
73 | |||
74 | $this->insertSql = $insertSql; | ||
75 | |||
76 | // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) | ||
77 | $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; | ||
78 | |||
79 | // 3. Create and store DELETE statements | ||
80 | $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses]; | ||
81 | foreach (array_reverse($classNames) as $className) { | ||
82 | $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform); | ||
83 | $this->sqlStatements[] = 'DELETE FROM ' . $tableName | ||
84 | . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; | ||
85 | } | ||
86 | |||
87 | // 4. Store DDL for temporary identifier table. | ||
88 | $columnDefinitions = []; | ||
89 | foreach ($idColumnNames as $idColumnName) { | ||
90 | $columnDefinitions[$idColumnName] = [ | ||
91 | 'name' => $idColumnName, | ||
92 | 'notnull' => true, | ||
93 | 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)), | ||
94 | ]; | ||
95 | } | ||
96 | |||
97 | $this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' | ||
98 | . $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))'; | ||
99 | $this->dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * {@inheritDoc} | ||
104 | */ | ||
105 | public function execute(Connection $conn, array $params, array $types): int | ||
106 | { | ||
107 | // Create temporary id table | ||
108 | $conn->executeStatement($this->createTempTableSql); | ||
109 | |||
110 | try { | ||
111 | // Insert identifiers | ||
112 | $numDeleted = $conn->executeStatement($this->insertSql, $params, $types); | ||
113 | |||
114 | // Execute DELETE statements | ||
115 | foreach ($this->sqlStatements as $sql) { | ||
116 | $conn->executeStatement($sql); | ||
117 | } | ||
118 | } catch (Throwable $exception) { | ||
119 | // FAILURE! Drop temporary table to avoid possible collisions | ||
120 | $conn->executeStatement($this->dropTempTableSql); | ||
121 | |||
122 | // Re-throw exception | ||
123 | throw $exception; | ||
124 | } | ||
125 | |||
126 | // Drop temporary table | ||
127 | $conn->executeStatement($this->dropTempTableSql); | ||
128 | |||
129 | return $numDeleted; | ||
130 | } | ||
131 | } | ||
diff --git a/vendor/doctrine/orm/src/Query/Exec/MultiTableUpdateExecutor.php b/vendor/doctrine/orm/src/Query/Exec/MultiTableUpdateExecutor.php new file mode 100644 index 0000000..dab1b61 --- /dev/null +++ b/vendor/doctrine/orm/src/Query/Exec/MultiTableUpdateExecutor.php | |||
@@ -0,0 +1,180 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\ORM\Query\Exec; | ||
6 | |||
7 | use Doctrine\DBAL\Connection; | ||
8 | use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; | ||
9 | use Doctrine\DBAL\Types\Type; | ||
10 | use Doctrine\ORM\Query\AST; | ||
11 | use Doctrine\ORM\Query\AST\UpdateStatement; | ||
12 | use Doctrine\ORM\Query\ParameterTypeInferer; | ||
13 | use Doctrine\ORM\Query\SqlWalker; | ||
14 | use Doctrine\ORM\Utility\PersisterHelper; | ||
15 | |||
16 | use function array_reverse; | ||
17 | use function array_slice; | ||
18 | use function implode; | ||
19 | |||
20 | /** | ||
21 | * Executes the SQL statements for bulk DQL UPDATE statements on classes in | ||
22 | * Class Table Inheritance (JOINED). | ||
23 | */ | ||
24 | class MultiTableUpdateExecutor extends AbstractSqlExecutor | ||
25 | { | ||
26 | private readonly string $createTempTableSql; | ||
27 | private readonly string $dropTempTableSql; | ||
28 | private readonly string $insertSql; | ||
29 | |||
30 | /** @var mixed[] */ | ||
31 | private array $sqlParameters = []; | ||
32 | private int $numParametersInUpdateClause = 0; | ||
33 | |||
34 | /** | ||
35 | * Initializes a new <tt>MultiTableUpdateExecutor</tt>. | ||
36 | * | ||
37 | * Internal note: Any SQL construction and preparation takes place in the constructor for | ||
38 | * best performance. With a query cache the executor will be cached. | ||
39 | * | ||
40 | * @param UpdateStatement $AST The root AST node of the DQL query. | ||
41 | * @param SqlWalker $sqlWalker The walker used for SQL generation from the AST. | ||
42 | */ | ||
43 | public function __construct(AST\Node $AST, SqlWalker $sqlWalker) | ||
44 | { | ||
45 | $em = $sqlWalker->getEntityManager(); | ||
46 | $conn = $em->getConnection(); | ||
47 | $platform = $conn->getDatabasePlatform(); | ||
48 | $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); | ||
49 | $this->sqlStatements = []; | ||
50 | |||
51 | if ($conn instanceof PrimaryReadReplicaConnection) { | ||
52 | $conn->ensureConnectedToPrimary(); | ||
53 | } | ||
54 | |||
55 | $updateClause = $AST->updateClause; | ||
56 | $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); | ||
57 | $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); | ||
58 | |||
59 | $updateItems = $updateClause->updateItems; | ||
60 | |||
61 | $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); | ||
62 | $idColumnNames = $rootClass->getIdentifierColumnNames(); | ||
63 | $idColumnList = implode(', ', $idColumnNames); | ||
64 | |||
65 | // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() | ||
66 | $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); | ||
67 | |||
68 | $insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' | ||
69 | . ' SELECT t0.' . implode(', t0.', $idColumnNames); | ||
70 | |||
71 | $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); | ||
72 | $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); | ||
73 | |||
74 | $insertSql .= $sqlWalker->walkFromClause($fromClause); | ||
75 | |||
76 | // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) | ||
77 | $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; | ||
78 | |||
79 | // 3. Create and store UPDATE statements | ||
80 | $classNames = [...$primaryClass->parentClasses, ...[$primaryClass->name], ...$primaryClass->subClasses]; | ||
81 | |||
82 | foreach (array_reverse($classNames) as $className) { | ||
83 | $affected = false; | ||
84 | $class = $em->getClassMetadata($className); | ||
85 | $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET '; | ||
86 | |||
87 | $sqlParameters = []; | ||
88 | foreach ($updateItems as $updateItem) { | ||
89 | $field = $updateItem->pathExpression->field; | ||
90 | |||
91 | if ( | ||
92 | (isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]->inherited)) || | ||
93 | (isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]->inherited)) | ||
94 | ) { | ||
95 | $newValue = $updateItem->newValue; | ||
96 | |||
97 | if (! $affected) { | ||
98 | $affected = true; | ||
99 | } else { | ||
100 | $updateSql .= ', '; | ||
101 | } | ||
102 | |||
103 | $updateSql .= $sqlWalker->walkUpdateItem($updateItem); | ||
104 | |||
105 | if ($newValue instanceof AST\InputParameter) { | ||
106 | $sqlParameters[] = $newValue->name; | ||
107 | |||
108 | ++$this->numParametersInUpdateClause; | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if ($affected) { | ||
114 | $this->sqlParameters[] = $sqlParameters; | ||
115 | $this->sqlStatements[] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // Append WHERE clause to insertSql, if there is one. | ||
120 | if ($AST->whereClause) { | ||
121 | $insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); | ||
122 | } | ||
123 | |||
124 | $this->insertSql = $insertSql; | ||
125 | |||
126 | // 4. Store DDL for temporary identifier table. | ||
127 | $columnDefinitions = []; | ||
128 | |||
129 | foreach ($idColumnNames as $idColumnName) { | ||
130 | $columnDefinitions[$idColumnName] = [ | ||
131 | 'name' => $idColumnName, | ||
132 | 'notnull' => true, | ||
133 | 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)), | ||
134 | ]; | ||
135 | } | ||
136 | |||
137 | $this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' | ||
138 | . $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))'; | ||
139 | |||
140 | $this->dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * {@inheritDoc} | ||
145 | */ | ||
146 | public function execute(Connection $conn, array $params, array $types): int | ||
147 | { | ||
148 | // Create temporary id table | ||
149 | $conn->executeStatement($this->createTempTableSql); | ||
150 | |||
151 | try { | ||
152 | // Insert identifiers. Parameters from the update clause are cut off. | ||
153 | $numUpdated = $conn->executeStatement( | ||
154 | $this->insertSql, | ||
155 | array_slice($params, $this->numParametersInUpdateClause), | ||
156 | array_slice($types, $this->numParametersInUpdateClause), | ||
157 | ); | ||
158 | |||
159 | // Execute UPDATE statements | ||
160 | foreach ($this->sqlStatements as $key => $statement) { | ||
161 | $paramValues = []; | ||
162 | $paramTypes = []; | ||
163 | |||
164 | if (isset($this->sqlParameters[$key])) { | ||
165 | foreach ($this->sqlParameters[$key] as $parameterKey => $parameterName) { | ||
166 | $paramValues[] = $params[$parameterKey]; | ||
167 | $paramTypes[] = $types[$parameterKey] ?? ParameterTypeInferer::inferType($params[$parameterKey]); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | $conn->executeStatement($statement, $paramValues, $paramTypes); | ||
172 | } | ||
173 | } finally { | ||
174 | // Drop temporary table | ||
175 | $conn->executeStatement($this->dropTempTableSql); | ||
176 | } | ||
177 | |||
178 | return $numUpdated; | ||
179 | } | ||
180 | } | ||
diff --git a/vendor/doctrine/orm/src/Query/Exec/SingleSelectExecutor.php b/vendor/doctrine/orm/src/Query/Exec/SingleSelectExecutor.php new file mode 100644 index 0000000..5445edb --- /dev/null +++ b/vendor/doctrine/orm/src/Query/Exec/SingleSelectExecutor.php | |||
@@ -0,0 +1,31 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\ORM\Query\Exec; | ||
6 | |||
7 | use Doctrine\DBAL\Connection; | ||
8 | use Doctrine\DBAL\Result; | ||
9 | use Doctrine\ORM\Query\AST\SelectStatement; | ||
10 | use Doctrine\ORM\Query\SqlWalker; | ||
11 | |||
12 | /** | ||
13 | * Executor that executes the SQL statement for simple DQL SELECT statements. | ||
14 | * | ||
15 | * @link www.doctrine-project.org | ||
16 | */ | ||
17 | class SingleSelectExecutor extends AbstractSqlExecutor | ||
18 | { | ||
19 | public function __construct(SelectStatement $AST, SqlWalker $sqlWalker) | ||
20 | { | ||
21 | $this->sqlStatements = $sqlWalker->walkSelectStatement($AST); | ||
22 | } | ||
23 | |||
24 | /** | ||
25 | * {@inheritDoc} | ||
26 | */ | ||
27 | public function execute(Connection $conn, array $params, array $types): Result | ||
28 | { | ||
29 | return $conn->executeQuery($this->sqlStatements, $params, $types, $this->queryCacheProfile); | ||
30 | } | ||
31 | } | ||
diff --git a/vendor/doctrine/orm/src/Query/Exec/SingleTableDeleteUpdateExecutor.php b/vendor/doctrine/orm/src/Query/Exec/SingleTableDeleteUpdateExecutor.php new file mode 100644 index 0000000..66696db --- /dev/null +++ b/vendor/doctrine/orm/src/Query/Exec/SingleTableDeleteUpdateExecutor.php | |||
@@ -0,0 +1,42 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\ORM\Query\Exec; | ||
6 | |||
7 | use Doctrine\DBAL\Connection; | ||
8 | use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; | ||
9 | use Doctrine\ORM\Query\AST; | ||
10 | use Doctrine\ORM\Query\SqlWalker; | ||
11 | |||
12 | /** | ||
13 | * Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes | ||
14 | * that are mapped to a single table. | ||
15 | * | ||
16 | * @link www.doctrine-project.org | ||
17 | * | ||
18 | * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. | ||
19 | */ | ||
20 | class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor | ||
21 | { | ||
22 | public function __construct(AST\Node $AST, SqlWalker $sqlWalker) | ||
23 | { | ||
24 | if ($AST instanceof AST\UpdateStatement) { | ||
25 | $this->sqlStatements = $sqlWalker->walkUpdateStatement($AST); | ||
26 | } elseif ($AST instanceof AST\DeleteStatement) { | ||
27 | $this->sqlStatements = $sqlWalker->walkDeleteStatement($AST); | ||
28 | } | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * {@inheritDoc} | ||
33 | */ | ||
34 | public function execute(Connection $conn, array $params, array $types): int | ||
35 | { | ||
36 | if ($conn instanceof PrimaryReadReplicaConnection) { | ||
37 | $conn->ensureConnectedToPrimary(); | ||
38 | } | ||
39 | |||
40 | return $conn->executeStatement($this->sqlStatements, $params, $types); | ||
41 | } | ||
42 | } | ||