Failed Conditions
Pull Request — develop (#3367)
by Benjamin
10:59
created

SQLAzureShardManager   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Test Coverage

Coverage 40.58%

Importance

Changes 0
Metric Value
wmc 24
eloc 62
dl 0
loc 194
ccs 28
cts 69
cp 0.4058
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getDistributionType() 0 3 1
A getFederationName() 0 3 1
A getShards() 0 11 1
A getDistributionKey() 0 3 1
A selectShard() 0 21 6
A queryAll() 0 24 5
A setFilteringEnabled() 0 3 1
A getCurrentDistributionValue() 0 3 1
A selectGlobal() 0 9 2
A __construct() 0 21 4
A splitFederation() 0 6 1
1
<?php
2
3
namespace Doctrine\DBAL\Sharding\SQLAzure;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Sharding\ShardingException;
7
use Doctrine\DBAL\Sharding\ShardManager;
8
use RuntimeException;
9
use function is_bool;
10
use function is_scalar;
11
use function sprintf;
12
13
/**
14
 * Sharding using the SQL Azure Federations support.
15
 */
16
class SQLAzureShardManager implements ShardManager
17
{
18
    /** @var string */
19
    private $federationName;
20
21
    /** @var bool */
22
    private $filteringEnabled;
23
24
    /** @var string */
25
    private $distributionKey;
26
27
    /** @var string */
28
    private $distributionType;
29
30
    /** @var Connection */
31
    private $conn;
32
33
    /** @var string|null */
34
    private $currentDistributionValue;
35
36
    /**
37
     * @throws ShardingException
38
     */
39 192
    public function __construct(Connection $conn)
40
    {
41 192
        $this->conn = $conn;
42 192
        $params     = $conn->getParams();
43
44 192
        if (! isset($params['sharding']['federationName'])) {
45 24
            throw ShardingException::missingDefaultFederationName();
46
        }
47
48 168
        if (! isset($params['sharding']['distributionKey'])) {
49 24
            throw ShardingException::missingDefaultDistributionKey();
50
        }
51
52 144
        if (! isset($params['sharding']['distributionType'])) {
53 24
            throw ShardingException::missingDistributionType();
54
        }
55
56 120
        $this->federationName   = $params['sharding']['federationName'];
57 120
        $this->distributionKey  = $params['sharding']['distributionKey'];
58 120
        $this->distributionType = $params['sharding']['distributionType'];
59 120
        $this->filteringEnabled = (bool) ($params['sharding']['filteringEnabled'] ?? false);
60 120
    }
61
62
    /**
63
     * Gets the name of the federation.
64
     *
65
     * @return string
66
     */
67
    public function getFederationName()
68
    {
69
        return $this->federationName;
70
    }
71
72
    /**
73
     * Gets the distribution key.
74
     *
75
     * @return string
76
     */
77
    public function getDistributionKey()
78
    {
79
        return $this->distributionKey;
80
    }
81
82
    /**
83
     * Gets the Doctrine Type name used for the distribution.
84
     *
85
     * @return string
86
     */
87
    public function getDistributionType()
88
    {
89
        return $this->distributionType;
90
    }
91
92
    /**
93
     * Sets Enabled/Disable filtering on the fly.
94
     *
95
     * @param bool $flag
96
     *
97
     * @return void
98
     */
99
    public function setFilteringEnabled($flag)
100
    {
101
        $this->filteringEnabled = (bool) $flag;
102
    }
103
104
    /**
105
     * {@inheritDoc}
106
     */
107 48
    public function selectGlobal()
108
    {
109 48
        if ($this->conn->isTransactionActive()) {
110 24
            throw ShardingException::activeTransaction();
111
        }
112
113 24
        $sql = 'USE FEDERATION ROOT WITH RESET';
114 24
        $this->conn->exec($sql);
115 24
        $this->currentDistributionValue = null;
116 24
    }
117
118
    /**
119
     * {@inheritDoc}
120
     */
121 48
    public function selectShard($distributionValue)
122
    {
123 48
        if ($this->conn->isTransactionActive()) {
124 24
            throw ShardingException::activeTransaction();
125
        }
126
127 24
        if ($distributionValue === null || is_bool($distributionValue) || ! is_scalar($distributionValue)) {
128 24
            throw ShardingException::noShardDistributionValue();
129
        }
130
131
        $platform = $this->conn->getDatabasePlatform();
132
        $sql      = sprintf(
133
            'USE FEDERATION %s (%s = %s) WITH RESET, FILTERING = %s;',
134
            $platform->quoteIdentifier($this->federationName),
135
            $platform->quoteIdentifier($this->distributionKey),
136
            $this->conn->quote($distributionValue),
137
            ($this->filteringEnabled ? 'ON' : 'OFF')
138
        );
139
140
        $this->conn->exec($sql);
141
        $this->currentDistributionValue = $distributionValue;
142
    }
143
144
    /**
145
     * {@inheritDoc}
146
     */
147 24
    public function getCurrentDistributionValue()
148
    {
149 24
        return $this->currentDistributionValue;
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155
    public function getShards()
156
    {
157
        $sql = 'SELECT member_id as id,
158
                      distribution_name as distribution_key,
159
                      CAST(range_low AS CHAR) AS rangeLow,
160
                      CAST(range_high AS CHAR) AS rangeHigh
161
                      FROM sys.federation_member_distributions d
162
                      INNER JOIN sys.federations f ON f.federation_id = d.federation_id
163
                      WHERE f.name = ' . $this->conn->quote($this->federationName);
164
165
        return $this->conn->fetchAll($sql);
166
    }
167
168
     /**
169
      * {@inheritDoc}
170
      */
171
    public function queryAll($sql, array $params = [], array $types = [])
172
    {
173
        $shards = $this->getShards();
174
        if (! $shards) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shards of type array<mixed,mixed> 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...
175
            throw new RuntimeException('No shards found for ' . $this->federationName);
176
        }
177
178
        $result          = [];
179
        $oldDistribution = $this->getCurrentDistributionValue();
180
181
        foreach ($shards as $shard) {
182
            $this->selectShard($shard['rangeLow']);
183
            foreach ($this->conn->fetchAll($sql, $params, $types) as $row) {
184
                $result[] = $row;
185
            }
186
        }
187
188
        if ($oldDistribution === null) {
189
            $this->selectGlobal();
190
        } else {
191
            $this->selectShard($oldDistribution);
192
        }
193
194
        return $result;
195
    }
196
197
    /**
198
     * Splits Federation at a given distribution value.
199
     *
200
     * @param string $splitDistributionValue
201
     *
202
     * @return void
203
     */
204
    public function splitFederation($splitDistributionValue)
205
    {
206
        $sql = 'ALTER FEDERATION ' . $this->getFederationName() . ' ' .
207
               'SPLIT AT (' . $this->getDistributionKey() . ' = ' .
208
               $this->conn->quote($splitDistributionValue) . ')';
209
        $this->conn->exec($sql);
210
    }
211
}
212