Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Sharding / SQLAzure / SQLAzureFederationsSynchronizer.php
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\Schema\Schema;
23 use Doctrine\DBAL\Connection;
24 use Doctrine\DBAL\Types\Type;
25
26 use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer;
27 use Doctrine\DBAL\Sharding\SingleDatabaseSynchronizer;
28
29 /**
30  * SQL Azure Schema Synchronizer
31  *
32  * Will iterate over all shards when performing schema operations. This is done
33  * by partitioning the passed schema into subschemas for the federation and the
34  * global database and then applying the operations step by step using the
35  * {@see \Doctrine\DBAL\Sharding\SingleDatabaseSynchronizer}.
36  *
37  * @author Benjamin Eberlei <kontakt@beberlei.de>
38  */
39 class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer
40 {
41     const FEDERATION_TABLE_FEDERATED   = 'azure.federated';
42     const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName';
43
44
45     /**
46      * @var SQLAzureShardManager
47      */
48     private $shardManager;
49
50     /**
51      * @var SchemaSynchronizer
52      */
53     private $synchronizer;
54
55     public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null)
56     {
57         parent::__construct($conn);
58         $this->shardManager = $shardManager;
59         $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn);
60     }
61
62     /**
63      * Get the SQL statements that can be executed to create the schema.
64      *
65      * @param Schema $createSchema
66      * @return array
67      */
68     public function getCreateSchema(Schema $createSchema)
69     {
70         $sql = array();
71
72         list($global, $federation) = $this->partitionSchema($createSchema);
73
74         $globalSql = $this->synchronizer->getCreateSchema($global);
75         if ($globalSql) {
76             $sql[] = "-- Create Root Federation\n" .
77                      "USE FEDERATION ROOT WITH RESET;";
78             $sql = array_merge($sql, $globalSql);
79         }
80
81         $federationSql = $this->synchronizer->getCreateSchema($federation);
82
83         if ($federationSql) {
84             $defaultValue = $this->getFederationTypeDefaultValue();
85
86             $sql[] = $this->getCreateFederationStatement();
87             $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;";
88             $sql = array_merge($sql, $federationSql);
89         }
90
91         return $sql;
92     }
93
94     /**
95      * Get the SQL Statements to update given schema with the underlying db.
96      *
97      * @param Schema $toSchema
98      * @param bool $noDrops
99      * @return array
100      */
101     public function getUpdateSchema(Schema $toSchema, $noDrops = false)
102     {
103         return $this->work($toSchema, function($synchronizer, $schema) use ($noDrops) {
104             return $synchronizer->getUpdateSchema($schema, $noDrops);
105         });
106     }
107
108     /**
109      * Get the SQL Statements to drop the given schema from underlying db.
110      *
111      * @param Schema $dropSchema
112      * @return array
113      */
114     public function getDropSchema(Schema $dropSchema)
115     {
116         return $this->work($dropSchema, function($synchronizer, $schema) {
117             return $synchronizer->getDropSchema($schema);
118         });
119     }
120
121     /**
122      * Create the Schema
123      *
124      * @param Schema $createSchema
125      * @return void
126      */
127     public function createSchema(Schema $createSchema)
128     {
129         $this->processSql($this->getCreateSchema($createSchema));
130     }
131
132     /**
133      * Update the Schema to new schema version.
134      *
135      * @param Schema $toSchema
136      * @return void
137      */
138     public function updateSchema(Schema $toSchema, $noDrops = false)
139     {
140         $this->processSql($this->getUpdateSchema($toSchema, $noDrops));
141     }
142
143     /**
144      * Drop the given database schema from the underlying db.
145      *
146      * @param Schema $dropSchema
147      * @return void
148      */
149     public function dropSchema(Schema $dropSchema)
150     {
151         $this->processSqlSafely($this->getDropSchema($dropSchema));
152     }
153
154     /**
155      * Get the SQL statements to drop all schema assets from underlying db.
156      *
157      * @return array
158      */
159     public function getDropAllSchema()
160     {
161         $this->shardManager->selectGlobal();
162         $globalSql = $this->synchronizer->getDropAllSchema();
163
164         if ($globalSql) {
165             $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;";
166             $sql = array_merge($sql, $globalSql);
167         }
168
169         $shards = $this->shardManager->getShards();
170         foreach ($shards as $shard) {
171             $this->shardManager->selectShard($shard['rangeLow']);
172
173             $federationSql = $this->synchronizer->getDropAllSchema();
174             if ($federationSql) {
175                 $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" .
176                          "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;";
177                 $sql = array_merge($sql, $federationSql);
178             }
179         }
180
181         $sql[] = "USE FEDERATION ROOT WITH RESET;";
182         $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName();
183
184         return $sql;
185     }
186
187     /**
188      * Drop all assets from the underyling db.
189      *
190      * @return void
191      */
192     public function dropAllSchema()
193     {
194         $this->processSqlSafely($this->getDropAllSchema());
195     }
196
197     private function partitionSchema(Schema $schema)
198     {
199         return array(
200             $this->extractSchemaFederation($schema, false),
201             $this->extractSchemaFederation($schema, true),
202         );
203     }
204
205     private function extractSchemaFederation(Schema $schema, $isFederation)
206     {
207         $partionedSchema = clone $schema;
208
209         foreach ($partionedSchema->getTables() as $table) {
210             if ($isFederation) {
211                 $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey());
212             }
213
214             if ( $table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) {
215                 $partionedSchema->dropTable($table->getName());
216             } else {
217                 foreach ($table->getForeignKeys() as $fk) {
218                     $foreignTable = $schema->getTable($fk->getForeignTableName());
219                     if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) {
220                         throw new \RuntimeException("Cannot have foreign key between global/federation.");
221                     }
222                 }
223             }
224         }
225
226         return $partionedSchema;
227     }
228
229     /**
230      * Work on the Global/Federation based on currently existing shards and
231      * perform the given operation on the underyling schema synchronizer given
232      * the different partioned schema instances.
233      *
234      * @param Schema $schema
235      * @param Closure $operation
236      * @return array
237      */
238     private function work(Schema $schema, \Closure $operation)
239     {
240         list($global, $federation) = $this->partitionSchema($schema);
241         $sql = array();
242
243         $this->shardManager->selectGlobal();
244         $globalSql = $operation($this->synchronizer, $global);
245
246         if ($globalSql) {
247             $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;";
248             $sql   = array_merge($sql, $globalSql);
249         }
250
251         $shards = $this->shardManager->getShards();
252
253         foreach ($shards as $shard) {
254             $this->shardManager->selectShard($shard['rangeLow']);
255
256             $federationSql = $operation($this->synchronizer, $federation);
257             if ($federationSql) {
258                 $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" .
259                          "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;";
260                 $sql   = array_merge($sql, $federationSql);
261             }
262         }
263
264         return $sql;
265     }
266
267     private function getFederationTypeDefaultValue()
268     {
269         $federationType = Type::getType($this->shardManager->getDistributionType());
270
271         switch ($federationType->getName()) {
272             case Type::GUID:
273                 $defaultValue = '00000000-0000-0000-0000-000000000000';
274                 break;
275             case Type::INTEGER:
276             case Type::SMALLINT:
277             case Type::BIGINT:
278                 $defaultValue = '0';
279                 break;
280             default:
281                 $defaultValue = '';
282                 break;
283         }
284         return $defaultValue;
285     }
286
287     private function getCreateFederationStatement()
288     {
289         $federationType = Type::getType($this->shardManager->getDistributionType());
290         $federationTypeSql = $federationType->getSqlDeclaration(array(), $this->conn->getDatabasePlatform());
291
292         return "--Create Federation\n" .
293                "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ."  RANGE)";
294     }
295 }
296