‪TYPO3CMS  9.5
SchemaMigratorTest.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
3 
5 
6 /*
7  * This file is part of the TYPO3 CMS project.
8  *
9  * It is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License, either version 2
11  * of the License, or any later version.
12  *
13  * For the full copyright and license information, please read the
14  * LICENSE.txt file that was distributed with this source code.
15  *
16  * The TYPO3 project - inspiring people to share!
17  */
18 
19 use Doctrine\DBAL\Schema\AbstractSchemaManager;
20 use Doctrine\DBAL\Schema\Table;
21 use Doctrine\DBAL\Types\BigIntType;
22 use Doctrine\DBAL\Types\TextType;
27 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
28 
32 class ‪SchemaMigratorTest extends FunctionalTestCase
33 {
37  protected ‪$sqlReader;
38 
42  protected ‪$connectionPool;
43 
47  protected ‪$schemaManager;
48 
52  protected ‪$subject;
53 
57  protected ‪$tableName = 'a_test_table';
58 
62  protected function ‪setUp()
63  {
64  parent::setUp();
65  $this->subject = GeneralUtility::makeInstance(SchemaMigrator::class);
66  $this->sqlReader = GeneralUtility::makeInstance(SqlReader::class);
67  $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
68  $this->schemaManager = $this->connectionPool->getConnectionForTable($this->tableName)->getSchemaManager();
69  $this->‪prepareTestTable();
70  }
71 
75  protected function ‪tearDown()
76  {
77  parent::tearDown();
78 
79  if ($this->schemaManager->tablesExist([$this->tableName])) {
80  $this->schemaManager->dropTable($this->tableName);
81  }
82  if ($this->schemaManager->tablesExist(['zzz_deleted_' . $this->tableName])) {
83  $this->schemaManager->dropTable('zzz_deleted_' . $this->tableName);
84  }
85  if ($this->schemaManager->tablesExist(['another_test_table'])) {
86  $this->schemaManager->dropTable('another_test_table');
87  }
88  }
89 
93  public function ‪createNewTable()
94  {
95  if ($this->schemaManager->tablesExist([$this->tableName])) {
96  $this->schemaManager->dropTable($this->tableName);
97  }
98 
99  $statements = $this->‪readFixtureFile('newTable');
100  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
101 
102  $this->subject->migrate(
103  $statements,
104  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
105  );
106 
107  $this->assertCount(6, $this->‪getTableDetails()->getColumns());
108  }
109 
113  public function ‪createNewTableIfNotExists()
114  {
115  $statements = $this->‪readFixtureFile('ifNotExists');
116  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
117 
118  $this->subject->migrate(
119  $statements,
120  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
121  );
122 
123  $this->assertTrue($this->schemaManager->tablesExist(['another_test_table']));
124  }
125 
129  public function ‪addNewColumns()
130  {
131  $statements = $this->‪readFixtureFile('addColumnsToTable');
132  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
133 
134  $this->subject->migrate(
135  $statements,
136  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
137  );
138 
139  $this->assertCount(7, $this->‪getTableDetails()->getColumns());
140  $this->assertTrue($this->‪getTableDetails()->hasColumn('title'));
141  $this->assertTrue($this->‪getTableDetails()->hasColumn('description'));
142  }
143 
147  public function ‪changeExistingColumn()
148  {
149  $statements = $this->‪readFixtureFile('changeExistingColumn');
150  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
151 
152  $this->assertEquals(50, $this->‪getTableDetails()->getColumn('title')->getLength());
153  $this->assertEmpty($this->‪getTableDetails()->getColumn('title')->getDefault());
154 
155  $this->subject->migrate(
156  $statements,
157  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
158  );
159 
160  $this->assertEquals(100, $this->‪getTableDetails()->getColumn('title')->getLength());
161  $this->assertEquals('Title', $this->‪getTableDetails()->getColumn('title')->getDefault());
162  }
163 
173  public function ‪notNullWithoutDefaultValue()
174  {
175  $statements = $this->‪readFixtureFile('notNullWithoutDefaultValue');
176  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
177 
178  $this->subject->migrate(
179  $statements,
180  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
181  );
182 
183  $this->assertTrue($this->‪getTableDetails()->getColumn('aTestField')->getNotnull());
184  }
185 
189  public function ‪defaultNullWithoutNotNull()
190  {
191  $statements = $this->‪readFixtureFile('defaultNullWithoutNotNull');
192  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
193 
194  $this->subject->migrate(
195  $statements,
196  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
197  );
198 
199  $this->assertFalse($this->‪getTableDetails()->getColumn('aTestField')->getNotnull());
200  $this->assertNull($this->‪getTableDetails()->getColumn('aTestField')->getDefault());
201  }
202 
209  public function ‪renameUnusedField()
210  {
211  $statements = $this->‪readFixtureFile('unusedColumn');
212  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
213 
214  $this->subject->migrate(
215  $statements,
216  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
217  );
218 
219  $this->assertFalse($this->‪getTableDetails()->hasColumn('hidden'));
220  $this->assertTrue($this->‪getTableDetails()->hasColumn('zzz_deleted_hidden'));
221  }
222 
226  public function ‪renameUnusedTable()
227  {
228  $statements = $this->‪readFixtureFile('unusedTable');
229  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
230 
231  $this->subject->migrate(
232  $statements,
233  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change_table']
234  );
235 
236  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
237  $this->assertContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
238  }
239 
247  public function ‪dropUnusedField()
248  {
249  $connection = $this->connectionPool->getConnectionForTable($this->tableName);
250  $fromSchema = $this->schemaManager->createSchema();
251  $toSchema = clone $fromSchema;
252  $toSchema->getTable($this->tableName)->addColumn('zzz_deleted_testfield', 'integer', ['notnull' => false]);
253  $statements = $fromSchema->getMigrateToSql(
254  $toSchema,
255  $connection->getDatabasePlatform()
256  );
257  $connection->executeUpdate($statements[0]);
258  $this->assertTrue($this->‪getTableDetails()->hasColumn('zzz_deleted_testfield'));
259 
260  $statements = $this->‪readFixtureFile('newTable');
261  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
262  $this->subject->migrate(
263  $statements,
264  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['drop']
265  );
266 
267  $this->assertFalse($this->‪getTableDetails()->hasColumn('zzz_deleted_testfield'));
268  }
269 
273  public function ‪dropUnusedTable()
274  {
275  $this->schemaManager->renameTable($this->tableName, 'zzz_deleted_' . $this->tableName);
276  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
277  $this->assertContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
278 
279  $statements = $this->‪readFixtureFile('newTable');
280  $updateSuggestions = $this->subject->getUpdateSuggestions($statements, true);
281  $this->subject->migrate(
282  $statements,
283  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['drop_table']
284  );
285 
286  $this->assertNotContains($this->tableName, $this->schemaManager->listTableNames());
287  $this->assertNotContains('zzz_deleted_' . $this->tableName, $this->schemaManager->listTableNames());
288  }
289 
296  {
297  $statements = $this->‪readFixtureFile('addCreateChange');
298  $this->subject->install($statements, true);
299 
300  $this->assertContains('another_test_table', $this->schemaManager->listTableNames());
301  $this->assertTrue($this->‪getTableDetails()->hasColumn('title'));
302  $this->assertTrue($this->‪getTableDetails()->hasIndex('title'));
303  $this->assertTrue($this->‪getTableDetails()->getIndex('title')->isUnique());
304  $this->assertNotInstanceOf(BigIntType::class, $this->‪getTableDetails()->getColumn('pid')->getType());
305  }
306 
315  {
316  $statements = $this->‪readFixtureFile('addIndexOnChangedColumn');
317  $this->subject->install($statements, true);
318 
319  $this->assertNotInstanceOf(TextType::class, $this->‪getTableDetails()->getColumn('title')->getType());
320  $this->assertFalse($this->‪getTableDetails()->hasIndex('title'));
321  }
322 
326  public function ‪changeExistingIndex()
327  {
328  // recreate the table with the indexes applied
329  // this is needed for e.g. postgres
330  if ($this->schemaManager->tablesExist([$this->tableName])) {
331  $this->schemaManager->dropTable($this->tableName);
332  }
333  $this->‪prepareTestTable(false);
334 
335  $statements = $this->‪readFixtureFile('changeExistingIndex');
336  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
337 
338  $this->subject->migrate(
339  $statements,
340  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
341  );
342 
343  $indexesAfterChange = $this->schemaManager->listTableIndexes($this->tableName);
344 
345  // indexes could be sorted differently thus we filter for index named "parent" only and
346  // use that as index to retrieve the modified columns of that index
347  $parentIndex = array_values(
348  array_filter(
349  array_keys($indexesAfterChange),
350  function ($key) {
351  return strpos($key, 'parent') !== false;
352  }
353  )
354  );
355 
356  $expectedColumnsOfChangedIndex = [
357  'pid',
358  'deleted'
359  ];
360  $this->assertEquals($expectedColumnsOfChangedIndex, $indexesAfterChange[$parentIndex[0]]->getColumns());
361  }
362 
369  public function ‪installCanPerformChangeOperations()
370  {
371  $statements = $this->‪readFixtureFile('addCreateChange');
372  $this->subject->install($statements);
373 
374  $this->assertContains('another_test_table', $this->schemaManager->listTableNames());
375  $this->assertTrue($this->‪getTableDetails()->hasColumn('title'));
376  $this->assertTrue($this->‪getTableDetails()->hasIndex('title'));
377  $this->assertTrue($this->‪getTableDetails()->getIndex('title')->isUnique());
378  $this->assertInstanceOf(BigIntType::class, $this->‪getTableDetails()->getColumn('pid')->getType());
379  }
380 
387  public function ‪importStaticDataInsertsRecords()
388  {
389  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', 'importStaticData.sql']));
390  $connection = $this->connectionPool->getConnectionForTable($this->tableName);
391  $statements = $this->sqlReader->getInsertStatementArray($sqlCode);
392  $this->subject->importStaticData($statements);
393 
394  $this->assertEquals(2, $connection->count('*', $this->tableName, []));
395  }
396 
401  {
402  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', 'importStaticData.sql']));
403  $statements = $this->sqlReader->getStatementArray($sqlCode);
404  $this->subject->importStaticData($statements);
405 
406  $this->assertNotContains('another_test_table', $this->schemaManager->listTableNames());
407  }
408 
415  public function ‪changeTableEngine()
416  {
417  $statements = $this->‪readFixtureFile('alterTableEngine');
418  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
419 
420  $index = array_keys($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change'])[0];
421  $this->assertStringEndsWith(
422  'ENGINE = MyISAM',
423  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change'][$index]
424  );
425 
426  $this->subject->migrate(
427  $statements,
428  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
429  );
430 
431  $updateSuggestions = $this->subject->getUpdateSuggestions($statements);
432  $this->assertEmpty($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
433  $this->assertEmpty($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
434  }
435 
441  protected function ‪prepareTestTable(bool $createOnly = true)
442  {
443  $statements = $this->‪readFixtureFile('newTable');
444  $this->subject->install($statements, $createOnly);
445  }
446 
452  protected function ‪getTableDetails(): Table
453  {
454  return $this->schemaManager->listTableDetails($this->tableName);
455  }
456 
463  protected function ‪readFixtureFile(string $fixtureName): array
464  {
465  $sqlCode = file_get_contents(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'Fixtures', $fixtureName]) . '.sql');
466 
467  return $this->sqlReader->getCreateTableStatementArray($sqlCode);
468  }
469 }
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeExistingIndex
‪changeExistingIndex()
Definition: SchemaMigratorTest.php:321
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\defaultNullWithoutNotNull
‪defaultNullWithoutNotNull()
Definition: SchemaMigratorTest.php:184
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\readFixtureFile
‪array readFixtureFile(string $fixtureName)
Definition: SchemaMigratorTest.php:458
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\addNewColumns
‪addNewColumns()
Definition: SchemaMigratorTest.php:124
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeTableEngine
‪changeTableEngine()
Definition: SchemaMigratorTest.php:410
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\importStaticDataIgnoresTableDefinitions
‪importStaticDataIgnoresTableDefinitions()
Definition: SchemaMigratorTest.php:395
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\createNewTable
‪createNewTable()
Definition: SchemaMigratorTest.php:88
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\installDoesNotAddIndexOnChangedColumn
‪installDoesNotAddIndexOnChangedColumn()
Definition: SchemaMigratorTest.php:309
‪TYPO3\CMS\Core\Database\Schema\SqlReader
Definition: SqlReader.php:29
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$subject
‪TYPO3 CMS Core Database Schema SchemaMigrator $subject
Definition: SchemaMigratorTest.php:48
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:48
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator
Definition: SchemaMigrator.php:35
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\installPerformsOnlyAddAndCreateOperations
‪installPerformsOnlyAddAndCreateOperations()
Definition: SchemaMigratorTest.php:290
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\dropUnusedTable
‪dropUnusedTable()
Definition: SchemaMigratorTest.php:268
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\dropUnusedField
‪dropUnusedField()
Definition: SchemaMigratorTest.php:242
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$schemaManager
‪AbstractSchemaManager $schemaManager
Definition: SchemaMigratorTest.php:44
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\installCanPerformChangeOperations
‪installCanPerformChangeOperations()
Definition: SchemaMigratorTest.php:364
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\createNewTableIfNotExists
‪createNewTableIfNotExists()
Definition: SchemaMigratorTest.php:108
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeExistingColumn
‪changeExistingColumn()
Definition: SchemaMigratorTest.php:142
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\renameUnusedTable
‪renameUnusedTable()
Definition: SchemaMigratorTest.php:221
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\getTableDetails
‪Doctrine DBAL Schema Table getTableDetails()
Definition: SchemaMigratorTest.php:447
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\prepareTestTable
‪prepareTestTable(bool $createOnly=true)
Definition: SchemaMigratorTest.php:436
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$sqlReader
‪SqlReader $sqlReader
Definition: SchemaMigratorTest.php:36
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$tableName
‪string $tableName
Definition: SchemaMigratorTest.php:52
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$connectionPool
‪ConnectionPool $connectionPool
Definition: SchemaMigratorTest.php:40
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\tearDown
‪tearDown()
Definition: SchemaMigratorTest.php:70
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:44
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema
Definition: SchemaMigratorTest.php:4
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\renameUnusedField
‪renameUnusedField()
Definition: SchemaMigratorTest.php:204
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\setUp
‪setUp()
Definition: SchemaMigratorTest.php:57
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\notNullWithoutDefaultValue
‪notNullWithoutDefaultValue()
Definition: SchemaMigratorTest.php:168
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest
Definition: SchemaMigratorTest.php:33
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\importStaticDataInsertsRecords
‪importStaticDataInsertsRecords()
Definition: SchemaMigratorTest.php:382