123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- <?php
- /*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This software consists of voluntary contributions made by many individuals
- * and is licensed under the MIT license. For more information, see
- * <http://www.doctrine-project.org>.
- */
- namespace Doctrine\DBAL\Sharding\SQLAzure;
- use Doctrine\DBAL\Schema\Schema;
- use Doctrine\DBAL\Connection;
- use Doctrine\DBAL\Types\Type;
- use Doctrine\DBAL\Schema\Synchronizer\AbstractSchemaSynchronizer;
- use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer;
- use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer;
- /**
- * SQL Azure Schema Synchronizer.
- *
- * Will iterate over all shards when performing schema operations. This is done
- * by partitioning the passed schema into subschemas for the federation and the
- * global database and then applying the operations step by step using the
- * {@see \Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer}.
- *
- * @author Benjamin Eberlei <kontakt@beberlei.de>
- */
- class SQLAzureFederationsSynchronizer extends AbstractSchemaSynchronizer
- {
- const FEDERATION_TABLE_FEDERATED = 'azure.federated';
- const FEDERATION_DISTRIBUTION_NAME = 'azure.federatedOnDistributionName';
- /**
- * @var \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager
- */
- private $shardManager;
- /**
- * @var \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer
- */
- private $synchronizer;
- /**
- * @param \Doctrine\DBAL\Connection $conn
- * @param \Doctrine\DBAL\Sharding\SQLAzure\SQLAzureShardManager $shardManager
- * @param \Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer|null $sync
- */
- public function __construct(Connection $conn, SQLAzureShardManager $shardManager, SchemaSynchronizer $sync = null)
- {
- parent::__construct($conn);
- $this->shardManager = $shardManager;
- $this->synchronizer = $sync ?: new SingleDatabaseSynchronizer($conn);
- }
- /**
- * {@inheritdoc}
- */
- public function getCreateSchema(Schema $createSchema)
- {
- $sql = array();
- list($global, $federation) = $this->partitionSchema($createSchema);
- $globalSql = $this->synchronizer->getCreateSchema($global);
- if ($globalSql) {
- $sql[] = "-- Create Root Federation\n" .
- "USE FEDERATION ROOT WITH RESET;";
- $sql = array_merge($sql, $globalSql);
- }
- $federationSql = $this->synchronizer->getCreateSchema($federation);
- if ($federationSql) {
- $defaultValue = $this->getFederationTypeDefaultValue();
- $sql[] = $this->getCreateFederationStatement();
- $sql[] = "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $defaultValue . ") WITH RESET, FILTERING = OFF;";
- $sql = array_merge($sql, $federationSql);
- }
- return $sql;
- }
- /**
- * {@inheritdoc}
- */
- public function getUpdateSchema(Schema $toSchema, $noDrops = false)
- {
- return $this->work($toSchema, function($synchronizer, $schema) use ($noDrops) {
- return $synchronizer->getUpdateSchema($schema, $noDrops);
- });
- }
- /**
- * {@inheritdoc}
- */
- public function getDropSchema(Schema $dropSchema)
- {
- return $this->work($dropSchema, function($synchronizer, $schema) {
- return $synchronizer->getDropSchema($schema);
- });
- }
- /**
- * {@inheritdoc}
- */
- public function createSchema(Schema $createSchema)
- {
- $this->processSql($this->getCreateSchema($createSchema));
- }
- /**
- * {@inheritdoc}
- */
- public function updateSchema(Schema $toSchema, $noDrops = false)
- {
- $this->processSql($this->getUpdateSchema($toSchema, $noDrops));
- }
- /**
- * {@inheritdoc}
- */
- public function dropSchema(Schema $dropSchema)
- {
- $this->processSqlSafely($this->getDropSchema($dropSchema));
- }
- /**
- * {@inheritdoc}
- */
- public function getDropAllSchema()
- {
- $this->shardManager->selectGlobal();
- $globalSql = $this->synchronizer->getDropAllSchema();
- if ($globalSql) {
- $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;";
- $sql = array_merge($sql, $globalSql);
- }
- $shards = $this->shardManager->getShards();
- foreach ($shards as $shard) {
- $this->shardManager->selectShard($shard['rangeLow']);
- $federationSql = $this->synchronizer->getDropAllSchema();
- if ($federationSql) {
- $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" .
- "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;";
- $sql = array_merge($sql, $federationSql);
- }
- }
- $sql[] = "USE FEDERATION ROOT WITH RESET;";
- $sql[] = "DROP FEDERATION " . $this->shardManager->getFederationName();
- return $sql;
- }
- /**
- * {@inheritdoc}
- */
- public function dropAllSchema()
- {
- $this->processSqlSafely($this->getDropAllSchema());
- }
- /**
- * @param \Doctrine\DBAL\Schema\Schema $schema
- *
- * @return array
- */
- private function partitionSchema(Schema $schema)
- {
- return array(
- $this->extractSchemaFederation($schema, false),
- $this->extractSchemaFederation($schema, true),
- );
- }
- /**
- * @param \Doctrine\DBAL\Schema\Schema $schema
- * @param boolean $isFederation
- *
- * @return \Doctrine\DBAL\Schema\Schema
- *
- * @throws \RuntimeException
- */
- private function extractSchemaFederation(Schema $schema, $isFederation)
- {
- $partitionedSchema = clone $schema;
- foreach ($partitionedSchema->getTables() as $table) {
- if ($isFederation) {
- $table->addOption(self::FEDERATION_DISTRIBUTION_NAME, $this->shardManager->getDistributionKey());
- }
- if ( $table->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) {
- $partitionedSchema->dropTable($table->getName());
- } else {
- foreach ($table->getForeignKeys() as $fk) {
- $foreignTable = $schema->getTable($fk->getForeignTableName());
- if ($foreignTable->hasOption(self::FEDERATION_TABLE_FEDERATED) !== $isFederation) {
- throw new \RuntimeException("Cannot have foreign key between global/federation.");
- }
- }
- }
- }
- return $partitionedSchema;
- }
- /**
- * Work on the Global/Federation based on currently existing shards and
- * perform the given operation on the underlying schema synchronizer given
- * the different partitioned schema instances.
- *
- * @param \Doctrine\DBAL\Schema\Schema $schema
- * @param \Closure $operation
- *
- * @return array
- */
- private function work(Schema $schema, \Closure $operation)
- {
- list($global, $federation) = $this->partitionSchema($schema);
- $sql = array();
- $this->shardManager->selectGlobal();
- $globalSql = $operation($this->synchronizer, $global);
- if ($globalSql) {
- $sql[] = "-- Work on Root Federation\nUSE FEDERATION ROOT WITH RESET;";
- $sql = array_merge($sql, $globalSql);
- }
- $shards = $this->shardManager->getShards();
- foreach ($shards as $shard) {
- $this->shardManager->selectShard($shard['rangeLow']);
- $federationSql = $operation($this->synchronizer, $federation);
- if ($federationSql) {
- $sql[] = "-- Work on Federation ID " . $shard['id'] . "\n" .
- "USE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " = " . $shard['rangeLow'].") WITH RESET, FILTERING = OFF;";
- $sql = array_merge($sql, $federationSql);
- }
- }
- return $sql;
- }
- /**
- * @return string
- */
- private function getFederationTypeDefaultValue()
- {
- $federationType = Type::getType($this->shardManager->getDistributionType());
- switch ($federationType->getName()) {
- case Type::GUID:
- $defaultValue = '00000000-0000-0000-0000-000000000000';
- break;
- case Type::INTEGER:
- case Type::SMALLINT:
- case Type::BIGINT:
- $defaultValue = '0';
- break;
- default:
- $defaultValue = '';
- break;
- }
- return $defaultValue;
- }
- /**
- * @return string
- */
- private function getCreateFederationStatement()
- {
- $federationType = Type::getType($this->shardManager->getDistributionType());
- $federationTypeSql = $federationType->getSqlDeclaration(array(), $this->conn->getDatabasePlatform());
- return "--Create Federation\n" .
- "CREATE FEDERATION " . $this->shardManager->getFederationName() . " (" . $this->shardManager->getDistributionKey() . " " . $federationTypeSql ." RANGE)";
- }
- }
|