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