Passed
Push — master ( c531f4...18d92e )
by Gaetano
11:50
created

SQLExecutor::query()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 41
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 7
eloc 24
c 1
b 1
f 0
nc 9
nop 1
dl 0
loc 41
ccs 0
cts 0
cp 0
crap 56
rs 8.6026
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
6
use Kaliop\eZMigrationBundle\API\EmbeddedReferenceResolverBagInterface;
7
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
8
9
class SQLExecutor extends AbstractExecutor
10
{
11
    use IgnorableStepExecutorTrait;
0 ignored issues
show
Bug introduced by
The trait Kaliop\eZMigrationBundle...orableStepExecutorTrait requires the property $dsl which is not provided by Kaliop\eZMigrationBundle\Core\Executor\SQLExecutor.
Loading history...
12
    use NonScalarReferenceSetterTrait;
0 ignored issues
show
Bug introduced by
The trait Kaliop\eZMigrationBundle...larReferenceSetterTrait requires the property $dsl which is not provided by Kaliop\eZMigrationBundle\Core\Executor\SQLExecutor.
Loading history...
13
14
    protected $scalarReferences = array('count');
15
16
    /**
17
     * @var DatabaseHandler $connection
18
     */
19
    protected $dbHandler;
20
21
    protected $supportedStepTypes = array('sql');
22
    protected $supportedActions = array('exec', 'query');
23
24
    /** @var EmbeddedReferenceResolverBagInterface $referenceResolver */
25
    protected $referenceResolver;
26
27 96
    protected $queryRequiresFetching = false;
28
29 96
    /**
30 96
     * @param DatabaseHandler $dbHandler
31 96
     * @param EmbeddedReferenceResolverBagInterface $referenceResolver
32
     */
33
    public function __construct(DatabaseHandler $dbHandler, EmbeddedReferenceResolverBagInterface $referenceResolver)
34
    {
35
        $this->dbHandler = $dbHandler;
36
        $this->referenceResolver = $referenceResolver;
37
    }
38 3
39
    /**
40 3
     * @param MigrationStep $step
41
     * @return integer
42 3
     * @throws \Exception if migration step is not for this type of db
43
     */
44 3
    public function execute(MigrationStep $step)
45
    {
46 3
        parent::execute($step);
47
48 3
        // BC
49
        if (!isset($step->dsl['mode'])) {
50 3
            $action = 'exec';
51
        } else {
52
            $action = $step->dsl['mode'];
53 3
        }
54
55
        if (!in_array($action, $this->supportedActions)) {
56 3
            throw new \Exception("Invalid step definition: value '$action' is not allowed for 'mode'");
57
        }
58 2
59
        $this->skipStepIfNeeded($step);
60 2
61
        return $this->$action($step);
62
    }
63 2
64
    protected function exec($step)
65 2
    {
66 1
        $conn = $this->dbHandler->getConnection();
67
        // @see http://doctrine-orm.readthedocs.io/projects/doctrine-dbal/en/latest/reference/platforms.html
68
        $dbType = strtolower(preg_replace('/([0-9]+|Platform)/', '', $conn->getDatabasePlatform()->getName()));
69 1
70 1
        if (!isset($step->dsl[$dbType])) {
71 1
            throw new \Exception("Current database type '$dbType' is not supported by the SQL migration");
72 1
        }
73 1
        $sql = $step->dsl[$dbType];
74
75
        if (isset($step->dsl['resolve_references']) && $step->dsl['resolve_references']) {
76
            $sql = $this->referenceResolver->resolveEmbeddedReferences($sql);
77
        }
78 1
79 1
        $result = $conn->exec($sql);
80
81
        $this->setExecReferences($result, $step);
82 1
83
        return $result;
84 1
    }
85
86
    protected function query($step)
87
    {
88
        $conn = $this->dbHandler->getConnection();
89
        // @see http://doctrine-orm.readthedocs.io/projects/doctrine-dbal/en/latest/reference/platforms.html
90
        $dbType = strtolower(preg_replace('/([0-9]+|Platform)/', '', $conn->getDatabasePlatform()->getName()));
91
92
        if (!isset($step->dsl[$dbType])) {
93
            throw new \Exception("Current database type '$dbType' is not supported by the SQL migration");
94
        }
95
        $sql = $step->dsl[$dbType];
96
97
        if (isset($step->dsl['resolve_references']) && $step->dsl['resolve_references']) {
98
            $sql = $this->referenceResolver->resolveEmbeddedReferences($sql);
99
        }
100
101
        $singleResult = ($this->getResultsType($step) == self::$RESULT_TYPE_SINGLE);
102
        /** @var \Doctrine\DBAL\Driver\Statement $stmt */
103
        $stmt = $conn->query($sql);
104
        if ($singleResult) {
105
            // fetch only twice, to insure that we get only 1 row. This can save ram compared to fetching all rows
106
            $result = $stmt->fetch();
107
            if ($result === false) {
108
                throw new \Exception('Found no results but expect one');
109
            }
110
            if ($stmt->fetch() !== false) {
111
                $stmt->closeCursor();
112
                throw new \Exception('Found two (or more) results but expect only one');
113
            }
114
            $stmt->closeCursor();
115
            $result = array($result);
116
        } else {
117
            // fetch all rows
118
            $result = $stmt->fetchAll();
119
            $stmt->closeCursor();
120
121
            $this->validateResultsCount($result, $step);
122
        }
123
124
        $this->setQueryReferences($result, $step, $singleResult);
125
126
        return $result;
127
    }
128
129
    protected function setExecReferences($result, $step)
130
    {
131
        if (!array_key_exists('references', $step->dsl)) {
132
            return false;
133
        }
134
135
        foreach ($step->dsl['references'] as $reference) {
136
            switch ($reference['attribute']) {
137
                case 'affected_rows':
138
                    $value = $result;
139
                    break;
140
                default:
141
                    throw new \InvalidArgumentException('Sql Executor does not support setting references for attribute ' . $reference['attribute']);
142
            }
143
144
            $overwrite = false;
145
            if (isset($reference['overwrite'])) {
146
                $overwrite = $reference['overwrite'];
147
            }
148
            $this->referenceResolver->addReference($reference['identifier'], $value, $overwrite);
149
        }
150
    }
151
152
    protected function setQueryReferences($result, $step, $singleResult)
153
    {
154
        if (!array_key_exists('references', $step->dsl)) {
155
            return false;
156
        }
157
158
        foreach ($step->dsl['references'] as $reference) {
159
            switch ($reference['attribute']) {
160
                case 'count':
161
                    $value = count($result);
162
                    break;
163
                default:
164
                    if (strpos($reference['attribute'], 'results.') !== 0) {
165
                        throw new \InvalidArgumentException('Sql Executor does not support setting references for attribute ' . $reference['attribute']);
166
                    }
167
                    if (count($result)) {
168
                        $colName = substr($reference['attribute'], 8);
169
                        if (!isset($result[0][$colName])) {
170
                            throw new \InvalidArgumentException('Sql Executor does not support setting references for attribute ' . $reference['attribute']);
171
                        }
172
                        $value = array_column($result, $colName);
173
                        if ($singleResult) {
174
                            $value = reset($value);
175
                        }
176
                    } else {
177
                        // we should validate the requested column name, but we can't...
178
                        $value = array();
179
                    }
180
            }
181
182
            $overwrite = false;
183
            if (isset($reference['overwrite'])) {
184
                $overwrite = $reference['overwrite'];
185
            }
186
            $this->referenceResolver->addReference($reference['identifier'], $value, $overwrite);
187
        }
188
    }
189
190
    /**
191
     * @param array $referenceDefinition
192
     * @return bool
193
     */
194
    protected function isScalarReference($referenceDefinition)
195
    {
196
        return in_array($referenceDefinition['attribute'], $this->scalarReferences);
197
    }
198
}
199