Passed
Pull Request — develop (#3453)
by Evgeniy
19:01
created

Schema::renameTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\Platforms\AbstractPlatform;
6
use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
7
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
8
use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor;
9
use Doctrine\DBAL\Schema\Visitor\Visitor;
10
use function array_keys;
11
use function strpos;
12
use function strtolower;
13
14
/**
15
 * Object representation of a database schema.
16
 *
17
 * Different vendors have very inconsistent naming with regard to the concept
18
 * of a "schema". Doctrine understands a schema as the entity that conceptually
19
 * wraps a set of database objects such as tables, sequences, indexes and
20
 * foreign keys that belong to each other into a namespace. A Doctrine Schema
21
 * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more
22
 * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL.
23
 *
24
 * Every asset in the doctrine schema has a name. A name consists of either a
25
 * namespace.local name pair or just a local unqualified name.
26
 *
27
 * The abstraction layer that covers a PostgreSQL schema is the namespace of an
28
 * database object (asset). A schema can have a name, which will be used as
29
 * default namespace for the unqualified database objects that are created in
30
 * the schema.
31
 *
32
 * In the case of MySQL where cross-database queries are allowed this leads to
33
 * databases being "misinterpreted" as namespaces. This is intentional, however
34
 * the CREATE/DROP SQL visitors will just filter this queries and do not
35
 * execute them. Only the queries for the currently connected database are
36
 * executed.
37
 */
38
class Schema extends AbstractAsset
39
{
40
    /**
41
     * The namespaces in this schema.
42
     *
43
     * @var string[]
44
     */
45
    private $namespaces = [];
46
47
    /** @var Table[] */
48
    protected $_tables = [];
49
50
    /** @var Sequence[] */
51
    protected $_sequences = [];
52
53
    /** @var SchemaConfig */
54
    protected $_schemaConfig = false;
55
56
    /**
57
     * @param Table[]    $tables
58
     * @param Sequence[] $sequences
59
     * @param string[]   $namespaces
60
     */
61 2026
    public function __construct(
62
        array $tables = [],
63
        array $sequences = [],
64
        ?SchemaConfig $schemaConfig = null,
65
        array $namespaces = []
66
    ) {
67 2026
        if ($schemaConfig === null) {
68 1751
            $schemaConfig = new SchemaConfig();
69
        }
70 2026
        $this->_schemaConfig = $schemaConfig;
71 2026
        $this->_setName($schemaConfig->getName() ?: 'public');
72
73 2026
        foreach ($namespaces as $namespace) {
74 12
            $this->createNamespace($namespace);
75
        }
76
77 2026
        foreach ($tables as $table) {
78 579
            $this->_addTable($table);
79
        }
80
81 1997
        foreach ($sequences as $sequence) {
82 129
            $this->_addSequence($sequence);
83
        }
84 1968
    }
85
86
    /**
87
     * @return bool
88
     */
89
    public function hasExplicitForeignKeyIndexes()
90
    {
91
        return $this->_schemaConfig->hasExplicitForeignKeyIndexes();
92
    }
93
94
    /**
95
     * @return void
96
     *
97
     * @throws SchemaException
98
     */
99 1591
    protected function _addTable(Table $table)
100
    {
101 1591
        $namespaceName = $table->getNamespaceName();
102 1591
        $tableName     = $table->getFullQualifiedName($this->getName());
103
104 1591
        if (isset($this->_tables[$tableName])) {
105 29
            throw SchemaException::tableAlreadyExists($tableName);
106
        }
107
108 1591
        if ($namespaceName !== null
109 1591
            && ! $table->isInDefaultNamespace($this->getName())
110 1591
            && ! $this->hasNamespace($namespaceName)) {
111 232
            $this->createNamespace($namespaceName);
112
        }
113
114 1591
        $this->_tables[$tableName] = $table;
115 1591
        $table->setSchemaConfig($this->_schemaConfig);
116 1591
    }
117
118
    /**
119
     * @return void
120
     *
121
     * @throws SchemaException
122
     */
123 506
    protected function _addSequence(Sequence $sequence)
124
    {
125 506
        $namespaceName = $sequence->getNamespaceName();
126 506
        $seqName       = $sequence->getFullQualifiedName($this->getName());
127
128 506
        if (isset($this->_sequences[$seqName])) {
129 29
            throw SchemaException::sequenceAlreadyExists($seqName);
130
        }
131
132 506
        if ($namespaceName !== null
133 506
            && ! $sequence->isInDefaultNamespace($this->getName())
134 506
            && ! $this->hasNamespace($namespaceName)) {
135 29
            $this->createNamespace($namespaceName);
136
        }
137
138 506
        $this->_sequences[$seqName] = $sequence;
139 506
    }
140
141
    /**
142
     * Returns the namespaces of this schema.
143
     *
144
     * @return string[] A list of namespace names.
145
     */
146 811
    public function getNamespaces()
147
    {
148 811
        return $this->namespaces;
149
    }
150
151
    /**
152
     * Gets all tables of this schema.
153
     *
154
     * @return Table[]
155
     */
156 898
    public function getTables()
157
    {
158 898
        return $this->_tables;
159
    }
160
161
    /**
162
     * @param string $tableName
163
     *
164
     * @return Table
165
     *
166
     * @throws SchemaException
167
     */
168 985
    public function getTable($tableName)
169
    {
170 985
        $tableName = $this->getFullQualifiedAssetName($tableName);
171 985
        if (! isset($this->_tables[$tableName])) {
172 29
            throw SchemaException::tableDoesNotExist($tableName);
173
        }
174
175 956
        return $this->_tables[$tableName];
176
    }
177
178
    /**
179
     * @param string $name
180
     *
181
     * @return string
182
     */
183 1362
    private function getFullQualifiedAssetName($name)
184
    {
185 1362
        $name = $this->getUnquotedAssetName($name);
186
187 1362
        if (strpos($name, '.') === false) {
188 1246
            $name = $this->getName() . '.' . $name;
189
        }
190
191 1362
        return strtolower($name);
192
    }
193
194
    /**
195
     * Returns the unquoted representation of a given asset name.
196
     *
197
     * @param string $assetName Quoted or unquoted representation of an asset name.
198
     *
199
     * @return string
200
     */
201 1507
    private function getUnquotedAssetName($assetName)
202
    {
203 1507
        if ($this->isIdentifierQuoted($assetName)) {
204 58
            return $this->trimQuotes($assetName);
205
        }
206
207 1478
        return $assetName;
208
    }
209
210
    /**
211
     * Does this schema have a namespace with the given name?
212
     *
213
     * @param string $namespaceName
214
     *
215
     * @return bool
216
     */
217 298
    public function hasNamespace($namespaceName)
218
    {
219 298
        $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
220
221 298
        return isset($this->namespaces[$namespaceName]);
222
    }
223
224
    /**
225
     * Does this schema have a table with the given name?
226
     *
227
     * @param string $tableName
228
     *
229
     * @return bool
230
     */
231 956
    public function hasTable($tableName)
232
    {
233 956
        $tableName = $this->getFullQualifiedAssetName($tableName);
234
235 956
        return isset($this->_tables[$tableName]);
236
    }
237
238
    /**
239
     * Gets all table names, prefixed with a schema name, even the default one if present.
240
     *
241
     * @return string[]
242
     */
243
    public function getTableNames()
244
    {
245
        return array_keys($this->_tables);
246
    }
247
248
    /**
249
     * @param string $sequenceName
250
     *
251
     * @return bool
252
     */
253 377
    public function hasSequence($sequenceName)
254
    {
255 377
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
256
257 377
        return isset($this->_sequences[$sequenceName]);
258
    }
259
260
    /**
261
     * @param string $sequenceName
262
     *
263
     * @return Sequence
264
     *
265
     * @throws SchemaException
266
     */
267 261
    public function getSequence($sequenceName)
268
    {
269 261
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
270 261
        if (! $this->hasSequence($sequenceName)) {
271 29
            throw SchemaException::sequenceDoesNotExist($sequenceName);
272
        }
273
274 232
        return $this->_sequences[$sequenceName];
275
    }
276
277
    /**
278
     * @return Sequence[]
279
     */
280 869
    public function getSequences()
281
    {
282 869
        return $this->_sequences;
283
    }
284
285
    /**
286
     * Creates a new namespace.
287
     *
288
     * @param string $namespaceName The name of the namespace to create.
289
     *
290
     * @return \Doctrine\DBAL\Schema\Schema This schema instance.
291
     *
292
     * @throws SchemaException
293
     */
294 331
    public function createNamespace($namespaceName)
295
    {
296 331
        $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
297
298 331
        if (isset($this->namespaces[$unquotedNamespaceName])) {
299 29
            throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
300
        }
301
302 331
        $this->namespaces[$unquotedNamespaceName] = $namespaceName;
303
304 331
        return $this;
305
    }
306
307
    /**
308
     * Creates a new table.
309
     *
310
     * @param string $tableName
311
     *
312
     * @return Table
313
     */
314 1084
    public function createTable($tableName)
315
    {
316 1084
        $table = new Table($tableName);
317 1084
        $this->_addTable($table);
318
319 1084
        foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
320
            $table->addOption($name, $value);
321
        }
322
323 1084
        return $table;
324
    }
325
326
    /**
327
     * Renames a table.
328
     *
329
     * @param string $oldTableName
330
     * @param string $newTableName
331
     *
332
     * @return \Doctrine\DBAL\Schema\Schema
333
     */
334 29
    public function renameTable($oldTableName, $newTableName)
335
    {
336 29
        $table = $this->getTable($oldTableName);
337 29
        $table->_setName($newTableName);
338
339 29
        $this->dropTable($oldTableName);
340 29
        $this->_addTable($table);
341
342 29
        return $this;
343
    }
344
345
    /**
346
     * Drops a table from the schema.
347
     *
348
     * @param string $tableName
349
     *
350
     * @return \Doctrine\DBAL\Schema\Schema
351
     */
352 145
    public function dropTable($tableName)
353
    {
354 145
        $tableName = $this->getFullQualifiedAssetName($tableName);
355 145
        $this->getTable($tableName);
356 145
        unset($this->_tables[$tableName]);
357
358 145
        return $this;
359
    }
360
361
    /**
362
     * Creates a new sequence.
363
     *
364
     * @param string $sequenceName
365
     * @param int    $allocationSize
366
     * @param int    $initialValue
367
     *
368
     * @return Sequence
369
     */
370 377
    public function createSequence($sequenceName, $allocationSize = 1, $initialValue = 1)
371
    {
372 377
        $seq = new Sequence($sequenceName, $allocationSize, $initialValue);
373 377
        $this->_addSequence($seq);
374
375 377
        return $seq;
376
    }
377
378
    /**
379
     * @param string $sequenceName
380
     *
381
     * @return \Doctrine\DBAL\Schema\Schema
382
     */
383 29
    public function dropSequence($sequenceName)
384
    {
385 29
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
386 29
        unset($this->_sequences[$sequenceName]);
387
388 29
        return $this;
389
    }
390
391
    /**
392
     * Returns an array of necessary SQL queries to create the schema on the given platform.
393
     *
394
     * @return string[]
395
     */
396 331
    public function toSql(AbstractPlatform $platform)
397
    {
398 331
        $sqlCollector = new CreateSchemaSqlCollector($platform);
399 331
        $this->visit($sqlCollector);
400
401 331
        return $sqlCollector->getQueries();
402
    }
403
404
    /**
405
     * Return an array of necessary SQL queries to drop the schema on the given platform.
406
     *
407
     * @return string[]
408
     */
409 29
    public function toDropSql(AbstractPlatform $platform)
410
    {
411 29
        $dropSqlCollector = new DropSchemaSqlCollector($platform);
412 29
        $this->visit($dropSqlCollector);
413
414 29
        return $dropSqlCollector->getQueries();
415
    }
416
417
    /**
418
     * @return string[]
419
     */
420 14
    public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform)
421
    {
422 14
        $comparator = new Comparator();
423 14
        $schemaDiff = $comparator->compare($this, $toSchema);
424
425 14
        return $schemaDiff->toSql($platform);
426
    }
427
428
    /**
429
     * @return string[]
430
     */
431
    public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform)
432
    {
433
        $comparator = new Comparator();
434
        $schemaDiff = $comparator->compare($fromSchema, $this);
435
436
        return $schemaDiff->toSql($platform);
437
    }
438
439
    /**
440
     * @return void
441
     */
442 505
    public function visit(Visitor $visitor)
443
    {
444 505
        $visitor->acceptSchema($this);
445
446 505
        if ($visitor instanceof NamespaceVisitor) {
447 418
            foreach ($this->namespaces as $namespace) {
448 116
                $visitor->acceptNamespace($namespace);
449
            }
450
        }
451
452 505
        foreach ($this->_tables as $table) {
453 505
            $table->visit($visitor);
454
        }
455
456 505
        foreach ($this->_sequences as $sequence) {
457 116
            $sequence->visit($visitor);
458
        }
459 505
    }
460
461
    /**
462
     * Cloning a Schema triggers a deep clone of all related assets.
463
     *
464
     * @return void
465
     */
466 72
    public function __clone()
467
    {
468 72
        foreach ($this->_tables as $k => $table) {
469 43
            $this->_tables[$k] = clone $table;
470
        }
471 72
        foreach ($this->_sequences as $k => $sequence) {
472 58
            $this->_sequences[$k] = clone $sequence;
473
        }
474 72
    }
475
}
476