Failed Conditions
Push — master ( a5d3e3...20bdb9 )
by Marco
13s
created

SQLAzureShardManager::__construct()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 13
cts 13
cp 1
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 1
crap 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\DBAL\Sharding\SQLAzure;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Sharding\ShardingException;
24
use Doctrine\DBAL\Sharding\ShardManager;
25
use Doctrine\DBAL\Types\Type;
26
27
/**
28
 * Sharding using the SQL Azure Federations support.
29
 *
30
 * @author Benjamin Eberlei <[email protected]>
31
 */
32
class SQLAzureShardManager implements ShardManager
33
{
34
    /**
35
     * @var string
36
     */
37
    private $federationName;
38
39
    /**
40
     * @var boolean
41
     */
42
    private $filteringEnabled;
43
44
    /**
45
     * @var string
46
     */
47
    private $distributionKey;
48
49
    /**
50
     * @var string
51
     */
52
    private $distributionType;
53
54
    /**
55
     * @var \Doctrine\DBAL\Connection
56
     */
57
    private $conn;
58
59
    /**
60
     * @var string|null
61
     */
62
    private $currentDistributionValue;
63
64
    /**
65
     * @param \Doctrine\DBAL\Connection $conn
66
     *
67
     * @throws \Doctrine\DBAL\Sharding\ShardingException
68
     */
69 8
    public function __construct(Connection $conn)
70
    {
71 8
        $this->conn = $conn;
72 8
        $params = $conn->getParams();
73
74 8
        if ( ! isset($params['sharding']['federationName'])) {
75 1
            throw ShardingException::missingDefaultFederationName();
76
        }
77
78 7
        if ( ! isset($params['sharding']['distributionKey'])) {
79 1
            throw ShardingException::missingDefaultDistributionKey();
80
        }
81
82 6
        if ( ! isset($params['sharding']['distributionType'])) {
83 1
            throw ShardingException::missingDistributionType();
84
        }
85
86 5
        $this->federationName = $params['sharding']['federationName'];
87 5
        $this->distributionKey = $params['sharding']['distributionKey'];
88 5
        $this->distributionType = $params['sharding']['distributionType'];
89 5
        $this->filteringEnabled = (bool) ($params['sharding']['filteringEnabled'] ?? false);
90 5
    }
91
92
    /**
93
     * Gets the name of the federation.
94
     *
95
     * @return string
96
     */
97
    public function getFederationName()
98
    {
99
        return $this->federationName;
100
    }
101
102
    /**
103
     * Gets the distribution key.
104
     *
105
     * @return string
106
     */
107
    public function getDistributionKey()
108
    {
109
        return $this->distributionKey;
110
    }
111
112
    /**
113
     * Gets the Doctrine Type name used for the distribution.
114
     *
115
     * @return string
116
     */
117
    public function getDistributionType()
118
    {
119
        return $this->distributionType;
120
    }
121
122
    /**
123
     * Sets Enabled/Disable filtering on the fly.
124
     *
125
     * @param boolean $flag
126
     *
127
     * @return void
128
     */
129
    public function setFilteringEnabled($flag)
130
    {
131
        $this->filteringEnabled = (bool) $flag;
132
    }
133
134
    /**
135
     * {@inheritDoc}
136
     */
137 2
    public function selectGlobal()
138
    {
139 2
        if ($this->conn->isTransactionActive()) {
140 1
            throw ShardingException::activeTransaction();
141
        }
142
143 1
        $sql = "USE FEDERATION ROOT WITH RESET";
144 1
        $this->conn->exec($sql);
145 1
        $this->currentDistributionValue = null;
146 1
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 2
    public function selectShard($distributionValue)
152
    {
153 2
        if ($this->conn->isTransactionActive()) {
154 1
            throw ShardingException::activeTransaction();
155
        }
156
157 1
        if ($distributionValue === null || is_bool($distributionValue) || !is_scalar($distributionValue)) {
158 1
            throw ShardingException::noShardDistributionValue();
159
        }
160
161
        $platform = $this->conn->getDatabasePlatform();
162
        $sql = sprintf(
163
            "USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;",
164
            $platform->quoteIdentifier($this->federationName),
165
            $platform->quoteIdentifier($this->distributionKey),
166
            $this->conn->quote($distributionValue),
167
            ($this->filteringEnabled ? 'ON' : 'OFF')
168
        );
169
170
        $this->conn->exec($sql);
171
        $this->currentDistributionValue = $distributionValue;
172
    }
173
174
    /**
175
     * {@inheritDoc}
176
     */
177 1
    public function getCurrentDistributionValue()
178
    {
179 1
        return $this->currentDistributionValue;
180
    }
181
182
    /**
183
     * {@inheritDoc}
184
     */
185
    public function getShards()
186
    {
187
        $sql = "SELECT member_id as id,
188
                      distribution_name as distribution_key,
189
                      CAST(range_low AS CHAR) AS rangeLow,
190
                      CAST(range_high AS CHAR) AS rangeHigh
191
                      FROM sys.federation_member_distributions d
192
                      INNER JOIN sys.federations f ON f.federation_id = d.federation_id
193
                      WHERE f.name = " . $this->conn->quote($this->federationName);
194
195
        return $this->conn->fetchAll($sql);
196
    }
197
198
     /**
199
      * {@inheritDoc}
200
      */
201
    public function queryAll($sql, array $params = [], array $types = [])
202
    {
203
        $shards = $this->getShards();
204
        if (!$shards) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shards of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
205
            throw new \RuntimeException("No shards found for " . $this->federationName);
206
        }
207
208
        $result = [];
209
        $oldDistribution = $this->getCurrentDistributionValue();
210
211
        foreach ($shards as $shard) {
212
            $this->selectShard($shard['rangeLow']);
213
            foreach ($this->conn->fetchAll($sql, $params, $types) as $row) {
214
                $result[] = $row;
215
            }
216
        }
217
218
        if ($oldDistribution === null) {
219
            $this->selectGlobal();
220
        } else {
221
            $this->selectShard($oldDistribution);
222
        }
223
224
        return $result;
225
    }
226
227
    /**
228
     * Splits Federation at a given distribution value.
229
     *
230
     * @param mixed $splitDistributionValue
231
     *
232
     * @return void
233
     */
234
    public function splitFederation($splitDistributionValue)
235
    {
236
        $type = Type::getType($this->distributionType);
237
238
        $sql = "ALTER FEDERATION " . $this->getFederationName() . " " .
239
               "SPLIT AT (" . $this->getDistributionKey() . " = " .
240
               $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ")";
241
        $this->conn->exec($sql);
242
    }
243
}
244