summaryrefslogtreecommitdiff
path: root/vendor/doctrine/orm/src/Query/Exec
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/doctrine/orm/src/Query/Exec')
-rw-r--r--vendor/doctrine/orm/src/Query/Exec/AbstractSqlExecutor.php61
-rw-r--r--vendor/doctrine/orm/src/Query/Exec/MultiTableDeleteExecutor.php131
-rw-r--r--vendor/doctrine/orm/src/Query/Exec/MultiTableUpdateExecutor.php180
-rw-r--r--vendor/doctrine/orm/src/Query/Exec/SingleSelectExecutor.php31
-rw-r--r--vendor/doctrine/orm/src/Query/Exec/SingleTableDeleteUpdateExecutor.php42
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
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query\Exec;
6
7use Doctrine\DBAL\ArrayParameterType;
8use Doctrine\DBAL\Cache\QueryCacheProfile;
9use Doctrine\DBAL\Connection;
10use Doctrine\DBAL\ParameterType;
11use Doctrine\DBAL\Result;
12use 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 */
23abstract 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
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query\Exec;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
9use Doctrine\DBAL\Types\Type;
10use Doctrine\ORM\Query\AST;
11use Doctrine\ORM\Query\AST\DeleteStatement;
12use Doctrine\ORM\Query\SqlWalker;
13use Doctrine\ORM\Utility\PersisterHelper;
14use Throwable;
15
16use function array_reverse;
17use 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 */
25class 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
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query\Exec;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
9use Doctrine\DBAL\Types\Type;
10use Doctrine\ORM\Query\AST;
11use Doctrine\ORM\Query\AST\UpdateStatement;
12use Doctrine\ORM\Query\ParameterTypeInferer;
13use Doctrine\ORM\Query\SqlWalker;
14use Doctrine\ORM\Utility\PersisterHelper;
15
16use function array_reverse;
17use function array_slice;
18use function implode;
19
20/**
21 * Executes the SQL statements for bulk DQL UPDATE statements on classes in
22 * Class Table Inheritance (JOINED).
23 */
24class 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
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query\Exec;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Result;
9use Doctrine\ORM\Query\AST\SelectStatement;
10use 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 */
17class 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
3declare(strict_types=1);
4
5namespace Doctrine\ORM\Query\Exec;
6
7use Doctrine\DBAL\Connection;
8use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
9use Doctrine\ORM\Query\AST;
10use 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 */
20class 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}