Failed Conditions
Pull Request — 2.6 (#7882)
by
unknown
06:45
created

MultiTableDeleteExecutor   A

Complexity

Total Complexity 7

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 7
eloc 46
dl 0
loc 108
ccs 0
cts 53
cp 0
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A execute() 0 25 3
A __construct() 0 52 4
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Query\Exec;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Types\Type;
24
use Doctrine\ORM\Query\AST;
25
use Doctrine\ORM\Utility\PersisterHelper;
26
use Throwable;
27
28
/**
29
 * Executes the SQL statements for bulk DQL DELETE statements on classes in
30
 * Class Table Inheritance (JOINED).
31
 *
32
 * @author      Roman Borschel <[email protected]>
33
 * @license     http://www.opensource.org/licenses/mit-license.php MIT
34
 * @link        http://www.doctrine-project.org
35
 * @since       2.0
36
 */
37
class MultiTableDeleteExecutor extends AbstractSqlExecutor
38
{
39
    /**
40
     * @var string
41
     */
42
    private $_createTempTableSql;
43
44
    /**
45
     * @var string
46
     */
47
    private $_dropTempTableSql;
48
49
    /**
50
     * @var string
51
     */
52
    private $_insertSql;
53
54
    /**
55
     * Initializes a new <tt>MultiTableDeleteExecutor</tt>.
56
     *
57
     * Internal note: Any SQL construction and preparation takes place in the constructor for
58
     *                best performance. With a query cache the executor will be cached.
59
     *
60
     * @param \Doctrine\ORM\Query\AST\Node  $AST       The root AST node of the DQL query.
61
     * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST.
62
     */
63
    public function __construct(AST\Node $AST, $sqlWalker)
64
    {
65
        $em             = $sqlWalker->getEntityManager();
66
        $conn           = $em->getConnection();
67
        $platform       = $conn->getDatabasePlatform();
68
        $quoteStrategy  = $em->getConfiguration()->getQuoteStrategy();
69
70
        $primaryClass       = $em->getClassMetadata($AST->deleteClause->abstractSchemaName);
71
        $primaryDqlAlias    = $AST->deleteClause->aliasIdentificationVariable;
72
        $rootClass          = $em->getClassMetadata($primaryClass->rootEntityName);
73
74
        $tempTable      = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());
75
        $idColumnNames  = $rootClass->getIdentifierColumnNames();
76
        $idColumnList   = implode(', ', $idColumnNames);
77
78
        // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
79
        $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias);
80
81
        $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
82
                . ' SELECT t0.' . implode(', t0.', $idColumnNames);
83
84
        $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias);
85
        $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]);
86
        $this->_insertSql .= $sqlWalker->walkFromClause($fromClause);
87
88
        // Append WHERE clause, if there is one.
89
        if ($AST->whereClause) {
90
            $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);
91
        }
92
93
        // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect)
94
        $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;
95
96
        // 3. Create and store DELETE statements
97
        $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses);
98
        foreach (array_reverse($classNames) as $className) {
99
            $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform);
100
            $this->_sqlStatements[] = 'DELETE FROM ' . $tableName
101
                    . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
102
        }
103
104
        // 4. Store DDL for temporary identifier table.
105
        $columnDefinitions = [];
106
        foreach ($idColumnNames as $idColumnName) {
107
            $columnDefinitions[$idColumnName] = [
108
                'notnull' => true,
109
                'type'    => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
110
            ];
111
        }
112
        $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
113
                . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')';
114
        $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
115
    }
116
117
    /**
118
     * {@inheritDoc}
119
     */
120
    public function execute(Connection $conn, array $params, array $types)
121
    {
122
        // Create temporary id table
123
        $conn->executeUpdate($this->_createTempTableSql);
124
125
        try {
126
            // Insert identifiers
127
            $numDeleted = $conn->executeUpdate($this->_insertSql, $params, $types);
128
129
            // Execute DELETE statements
130
            foreach ($this->_sqlStatements as $sql) {
131
                $conn->executeUpdate($sql);
132
            }
133
        } catch (Throwable $exception) {
134
            // FAILURE! Drop temporary table to avoid possible collisions
135
            $conn->executeUpdate($this->_dropTempTableSql);
136
137
            // Re-throw exception
138
            throw $exception;
139
        }
140
141
        // Drop temporary table
142
        $conn->executeUpdate($this->_dropTempTableSql);
143
144
        return $numDeleted;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $numDeleted returns the type integer which is incompatible with the return type mandated by Doctrine\ORM\Query\Exec\...tSqlExecutor::execute() of Doctrine\DBAL\Driver\Statement.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
145
    }
146
}
147