‪TYPO3CMS  ‪main
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\Column;
22 use Doctrine\DBAL\Schema\Table;
23 use Doctrine\DBAL\Types\BigIntType;
24 use Doctrine\DBAL\Types\TextType;
25 use Doctrine\DBAL\Types\Type;
26 use PHPUnit\Framework\Attributes\DataProvider;
27 use PHPUnit\Framework\Attributes\Group;
28 use PHPUnit\Framework\Attributes\Test;
34 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
35 
36 final class ‪SchemaMigratorTest extends FunctionalTestCase
37 {
40  private AbstractSchemaManager ‪$schemaManager;
41 
42  protected function ‪setUp(): void
43  {
44  parent::setUp();
45  $this->sqlReader = $this->get(SqlReader::class);
46  $this->connectionPool = $this->get(ConnectionPool::class);
47  $this->schemaManager = $this->connectionPool->getConnectionByName('Default')->createSchemaManager();
48  }
49 
50  protected function ‪tearDown(): void
51  {
52  parent::tearDown();
53  // Clean up for next test
54  if ($this->schemaManager->tablesExist(['a_test_table'])) {
55  $this->schemaManager->dropTable('a_test_table');
56  }
57  if ($this->schemaManager->tablesExist(['zzz_deleted_a_test_table'])) {
58  $this->schemaManager->dropTable('zzz_deleted_a_test_table');
59  }
60  if ($this->schemaManager->tablesExist(['another_test_table'])) {
61  $this->schemaManager->dropTable('another_test_table');
62  }
63  if ($this->schemaManager->tablesExist(['a_textfield_test_table'])) {
64  $this->schemaManager->dropTable('a_textfield_test_table');
65  }
66  }
67 
71  private function ‪prepareTestTable(‪SchemaMigrator $schemaMigrator): void
72  {
73  $sqlCode = file_get_contents(__DIR__ . '/../Fixtures/newTable.sql');
74  $schemaMigrator->install($this->sqlReader->getCreateTableStatementArray($sqlCode));
75  }
76 
80  private function ‪getTableDetails(): Table
81  {
82  return $this->schemaManager->introspectTable('a_test_table');
83  }
84 
85  #[Test]
87  {
88  $column1 = new Column('testfield', Type::getType('string'), ['length' => 100]);
89  $column2 = new Column('testfield', Type::getType('string'), ['length' => 200]);
90  $column3 = new Column('testfield', Type::getType('string'), ['length' => 220]);
91  $table1 = new Table('a_test_table', [$column1]);
92  $table2 = new Table('a_test_table', [$column2]);
93  $table3 = new Table('a_test_table', [$column3]);
94  $subject = $this->get(SchemaMigrator::class);
95  $mergeTableDefinitionsMethod = new \ReflectionMethod(
96  SchemaMigrator::class,
97  'mergeTableDefinitions'
98  );
99  $mergedTables = $mergeTableDefinitionsMethod->invoke($subject, [$table1, $table2, $table3]);
100  self::assertIsArray($mergedTables);
101  self::assertCount(1, $mergedTables);
102  self::assertArrayHasKey('a_test_table', $mergedTables);
103 
104  $firstTable = $mergedTables['a_test_table'];
105  self::assertInstanceOf(Table::class, $firstTable);
106  self::assertTrue($firstTable->hasColumn('testfield'));
107  self::assertSame($column3, $firstTable->getColumn('testfield'));
108  }
109 
110  #[Test]
111  public function ‪createNewTable(): void
112  {
113  $subject = $this->get(SchemaMigrator::class);
114  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/newTable.sql'));
115  $updateSuggestions = $subject->getUpdateSuggestions($statements);
116  $result = $subject->migrate(
117  $statements,
118  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
119  );
120  self::assertCount(6, $this->‪getTableDetails()->getColumns());
121  }
122 
123  #[Test]
124  public function ‪createNewTableIfNotExists(): void
125  {
126  $subject = $this->get(SchemaMigrator::class);
127  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/ifNotExists.sql'));
128  $updateSuggestions = $subject->getUpdateSuggestions($statements);
129  $subject->migrate(
130  $statements,
131  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
132  );
133  self::assertTrue($this->schemaManager->tablesExist(['another_test_table']));
134  }
135 
136  #[Test]
137  public function ‪addNewColumns(): void
138  {
139  $subject = $this->get(SchemaMigrator::class);
140  $this->‪prepareTestTable($subject);
141  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/addColumnsToTable.sql'));
142  $updateSuggestions = $subject->getUpdateSuggestions($statements);
143  $subject->migrate(
144  $statements,
145  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
146  );
147  self::assertCount(7, $this->‪getTableDetails()->getColumns());
148  self::assertTrue($this->‪getTableDetails()->hasColumn('title'));
149  self::assertTrue($this->‪getTableDetails()->hasColumn('description'));
150  }
151 
152  #[Test]
153  public function ‪changeExistingColumn(): void
154  {
155  $subject = $this->get(SchemaMigrator::class);
156  $this->‪prepareTestTable($subject);
157  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/changeExistingColumn.sql'));
158  $updateSuggestions = $subject->getUpdateSuggestions($statements);
159  self::assertEquals(50, $this->‪getTableDetails()->getColumn('title')->getLength());
160  self::assertEmpty($this->‪getTableDetails()->getColumn('title')->getDefault());
161  $subject->migrate(
162  $statements,
163  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
164  );
165  self::assertEquals(100, $this->‪getTableDetails()->getColumn('title')->getLength());
166  self::assertEquals('Title', $this->‪getTableDetails()->getColumn('title')->getDefault());
167  }
168 
169  #[Test]
170  public function ‪notNullWithoutDefaultValue(): void
171  {
172  $subject = $this->get(SchemaMigrator::class);
173  $this->‪prepareTestTable($subject);
174  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/notNullWithoutDefaultValue.sql'));
175  $updateSuggestions = $subject->getUpdateSuggestions($statements);
176  $subject->migrate(
177  $statements,
178  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
179  );
180  self::assertTrue($this->‪getTableDetails()->getColumn('aTestField')->getNotnull());
181  }
182 
183  #[Test]
184  public function ‪defaultNullWithoutNotNull(): void
185  {
186  $subject = $this->get(SchemaMigrator::class);
187  $this->‪prepareTestTable($subject);
188  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/defaultNullWithoutNotNull.sql'));
189  $updateSuggestions = $subject->getUpdateSuggestions($statements);
190  $subject->migrate(
191  $statements,
192  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['add']
193  );
194  self::assertFalse($this->‪getTableDetails()->getColumn('aTestField')->getNotnull());
195  self::assertNull($this->‪getTableDetails()->getColumn('aTestField')->getDefault());
196  }
197 
198  #[Test]
199  public function ‪renameUnusedField(): void
200  {
201  $subject = $this->get(SchemaMigrator::class);
202  $this->‪prepareTestTable($subject);
203  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/unusedColumn.sql'));
204  $updateSuggestions = $subject->getUpdateSuggestions($statements, true);
205  $subject->migrate(
206  $statements,
207  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
208  );
209  self::assertFalse($this->‪getTableDetails()->hasColumn('hidden'));
210  self::assertTrue($this->‪getTableDetails()->hasColumn('zzz_deleted_hidden'));
211  }
212 
213  #[Test]
214  public function ‪renameUnusedTable(): void
215  {
216  $subject = $this->get(SchemaMigrator::class);
217  $this->‪prepareTestTable($subject);
218  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/unusedTable.sql'));
219  $updateSuggestions = $subject->getUpdateSuggestions($statements, true);
220  $subject->migrate(
221  $statements,
222  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change_table']
223  );
224  self::assertNotContains('a_test_table', $this->schemaManager->listTableNames());
225  self::assertContains('zzz_deleted_a_test_table', $this->schemaManager->listTableNames());
226  }
227 
228  #[Test]
229  public function ‪dropUnusedField(): void
230  {
231  $subject = $this->get(SchemaMigrator::class);
232  $this->‪prepareTestTable($subject);
233  $connection = $this->connectionPool->getConnectionForTable('a_test_table');
234  $fromSchema = $this->schemaManager->introspectSchema();
235  $tableDiff = new ‪TableDiff(
236  oldTable: $fromSchema->getTable('a_test_table'),
237  addedColumns: ['zzz_deleted_testfield' => new Column('zzz_deleted_testfield', Type::getType('integer'), ['notnull' => false])],
238  modifiedColumns: [],
239  droppedColumns: [],
240  renamedColumns: [],
241  addedIndexes: [],
242  modifiedIndexes: [],
243  droppedIndexes: [],
244  renamedIndexes: [],
245  addedForeignKeys: [],
246  modifiedForeignKeys: [],
247  droppedForeignKeys: [],
248  tableOptions: [],
249  );
250  $schemaDiff = new ‪SchemaDiff(
251  createdSchemas: [],
252  droppedSchemas: [],
253  createdTables: [],
254  alteredTables: ['a_test_table' => $tableDiff],
255  droppedTables: [],
256  createdSequences: [],
257  alteredSequences: [],
258  droppedSequences: [],
259  );
260  foreach ($connection->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff) as $statement) {
261  $connection->executeStatement($statement);
262  }
263  self::assertTrue($this->‪getTableDetails()->hasColumn('zzz_deleted_testfield'));
264  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/newTable.sql'));
265  $updateSuggestions = $subject->getUpdateSuggestions($statements, true);
266  $subject->migrate(
267  $statements,
268  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['drop']
269  );
270  self::assertFalse($this->‪getTableDetails()->hasColumn('zzz_deleted_testfield'));
271  }
272 
273  #[Test]
274  public function ‪dropUnusedTable(): void
275  {
276  $subject = $this->get(SchemaMigrator::class);
277  $this->‪prepareTestTable($subject);
278  $this->schemaManager->renameTable('a_test_table', 'zzz_deleted_a_test_table');
279  self::assertNotContains('a_test_table', $this->schemaManager->listTableNames());
280  self::assertContains('zzz_deleted_a_test_table', $this->schemaManager->listTableNames());
281  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/newTable.sql'));
282  $updateSuggestions = $subject->getUpdateSuggestions($statements, true);
283  $subject->migrate(
284  $statements,
285  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['drop_table']
286  );
287  self::assertNotContains('a_test_table', $this->schemaManager->listTableNames());
288  self::assertNotContains('zzz_deleted_a_test_table', $this->schemaManager->listTableNames());
289  }
290 
291  #[Group('not-postgres')]
292  #[Group('not-sqlite')]
293  #[Test]
295  {
296  $subject = $this->get(SchemaMigrator::class);
297  $this->‪prepareTestTable($subject);
298  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/addCreateChange.sql'));
299  $subject->install($statements, true);
300  self::assertContains('another_test_table', $this->schemaManager->listTableNames());
301  self::assertTrue($this->‪getTableDetails()->hasColumn('title'));
302  self::assertTrue($this->‪getTableDetails()->hasIndex('title'));
303  self::assertTrue($this->‪getTableDetails()->getIndex('title')->isUnique());
304  self::assertNotInstanceOf(BigIntType::class, $this->‪getTableDetails()->getColumn('pid')->getType());
305  }
306 
307  #[Test]
309  {
310  $subject = $this->get(SchemaMigrator::class);
311  $this->‪prepareTestTable($subject);
312  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/addIndexOnChangedColumn.sql'));
313  $subject->install($statements, true);
314  self::assertNotInstanceOf(TextType::class, $this->‪getTableDetails()->getColumn('title')->getType());
315  self::assertFalse($this->‪getTableDetails()->hasIndex('title'));
316  }
317 
318  #[Test]
319  public function ‪changeExistingIndex(): void
320  {
321  $subject = $this->get(SchemaMigrator::class);
322  $this->‪prepareTestTable($subject);
323  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/changeExistingIndex.sql'));
324  $updateSuggestions = $subject->getUpdateSuggestions($statements);
325  $result = $subject->migrate(
326  $statements,
327  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
328  );
329  $indexesAfterChange = $this->schemaManager->listTableIndexes('a_test_table');
330  // indexes could be sorted differently thus we filter for index named "parent" only and
331  // use that as index to retrieve the modified columns of that index
332  $parentIndex = array_values(
333  array_filter(
334  array_keys($indexesAfterChange),
335  static function ($key) {
336  return str_contains($key, 'parent');
337  }
338  )
339  );
340  $expectedColumnsOfChangedIndex = [
341  'pid',
342  'deleted',
343  ];
344  self::assertEquals($expectedColumnsOfChangedIndex, $indexesAfterChange[$parentIndex[0]]->getColumns());
345  }
346 
347  #[Group('not-postgres')]
348  #[Group('not-sqlite')]
349  #[Test]
350  public function ‪installCanPerformChangeOperations(): void
351  {
352  $subject = $this->get(SchemaMigrator::class);
353  $this->‪prepareTestTable($subject);
354  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/addCreateChange.sql'));
355  $subject->install($statements);
356  self::assertContains('another_test_table', $this->schemaManager->listTableNames());
357  self::assertTrue($this->‪getTableDetails()->hasColumn('title'));
358  self::assertTrue($this->‪getTableDetails()->hasIndex('title'));
359  self::assertTrue($this->‪getTableDetails()->getIndex('title')->isUnique());
360  self::assertInstanceOf(BigIntType::class, $this->‪getTableDetails()->getColumn('pid')->getType());
361  }
362 
363  #[Group('not-postgres')]
364  #[Test]
365  public function ‪importStaticDataInsertsRecords(): void
366  {
367  $subject = $this->get(SchemaMigrator::class);
368  $this->‪prepareTestTable($subject);
369  $sqlCode = file_get_contents(__DIR__ . '/../Fixtures/importStaticData.sql');
370  $connection = $this->connectionPool->getConnectionForTable('a_test_table');
371  $statements = $this->sqlReader->getInsertStatementArray($sqlCode);
372  $subject->importStaticData($statements);
373  self::assertEquals(2, $connection->count('*', 'a_test_table', []));
374  }
375 
376  #[Test]
378  {
379  $subject = $this->get(SchemaMigrator::class);
380  $sqlCode = file_get_contents(__DIR__ . '/../Fixtures/importStaticData.sql');
381  $statements = $this->sqlReader->getStatementArray($sqlCode);
382  $subject->importStaticData($statements);
383  self::assertNotContains('another_test_table', $this->schemaManager->listTableNames());
384  }
385 
386  #[Group('not-postgres')]
387  #[Group('not-sqlite')]
388  #[Test]
389  public function ‪changeTableEngine(): void
390  {
391  $subject = $this->get(SchemaMigrator::class);
392  $this->‪prepareTestTable($subject);
393  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/alterTableEngine.sql'));
394  $updateSuggestions = $subject->getUpdateSuggestions($statements);
395  $index = array_keys($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change'])[0];
396  self::assertStringEndsWith(
397  'ENGINE = MyISAM',
398  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change'][$index]
399  );
400  $subject->migrate(
401  $statements,
402  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']
403  );
404  $updateSuggestions = $subject->getUpdateSuggestions($statements);
405  self::assertEmpty($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
406  self::assertEmpty($updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['change']);
407  }
408 
409  public static function ‪textFieldDefaultValueTestDataProvider(): \Generator
410  {
411  yield 'text not null default empty string value' => [
412  'fixtureFileName' => 'text-not-null-default-empty-string-value.sql',
413  'table' => 'a_textfield_test_table',
414  'fieldName' => 'testfield',
415  'assertionFileName' => 'text-not-null-default-empty-string-value.csv',
416  'expectedDefaultValue' => '',
417  'expectedNotNull' => true,
418  'expectDefaultValue' => true,
419  ];
420  yield 'text default empty string value' => [
421  'fixtureFileName' => 'text-default-empty-string-value.sql',
422  'table' => 'a_textfield_test_table',
423  'fieldName' => 'testfield',
424  'assertionFileName' => 'text-default-empty-string-value.csv',
425  'expectedDefaultValue' => '',
426  'expectedNotNull' => false,
427  'expectDefaultValue' => true,
428  ];
429  yield 'text default NULL' => [
430  'fixtureFileName' => 'text-default-null.sql',
431  'table' => 'a_textfield_test_table',
432  'fieldName' => 'testfield',
433  'assertionFileName' => 'text-default-null.csv',
434  'expectedDefaultValue' => null,
435  'expectedNotNull' => false,
436  'expectDefaultValue' => true,
437  ];
438  yield 'text not null default value string value' => [
439  'fixtureFileName' => 'text-not-null-default-value-string-value.sql',
440  'table' => 'a_textfield_test_table',
441  'fieldName' => 'testfield',
442  'assertionFileName' => 'text-not-null-default-value-string-value.csv',
443  'expectedDefaultValue' => 'database-default-value',
444  'expectedNotNull' => true,
445  'expectDefaultValue' => true,
446  ];
447  yield 'text not null default value string with single quote value' => [
448  'fixtureFileName' => 'text-not-null-default-value-string-with-single-quote-value.sql',
449  'table' => 'a_textfield_test_table',
450  'fieldName' => 'testfield',
451  'assertionFileName' => 'text-not-null-default-value-string-with-single-quote-value.csv',
452  'expectedDefaultValue' => "default-value with a single ' quote",
453  'expectedNotNull' => true,
454  'expectDefaultValue' => true,
455  ];
456  }
457 
458  #[DataProvider('textFieldDefaultValueTestDataProvider')]
459  #[Test]
461  string $fixtureFileName,
462  string $table,
463  string $fieldName,
464  string $assertionFileName,
465  string|null $expectedDefaultValue,
466  bool $expectedNotNull,
467  bool $expectDefaultValue,
468  ): void {
469  $subject = $this->get(SchemaMigrator::class);
470  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/TextFieldDefaultValue/' . $fixtureFileName));
471  $updateSuggestions = $subject->getUpdateSuggestions($statements);
472  $result = $subject->migrate(
473  $statements,
474  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
475  );
476  self::assertSame([], $result);
477 
478  $tableDefinition = $this->getConnectionPool()->getConnectionForTable($table)->createSchemaManager()->introspectTable($table);
479  self::assertTrue($tableDefinition->hasColumn($fieldName));
480  $column = $tableDefinition->getColumn($fieldName);
481  if ($expectDefaultValue) {
482  self::assertArrayHasKey('default', $column->toArray());
483  self::assertSame($expectedDefaultValue, $column->getDefault());
484  } else {
485  self::assertArrayNotHasKey('default', $column->toArray());
486  }
487  self::assertSame($expectedNotNull, $column->getNotnull());
488 
489  $this->getConnectionPool()->getConnectionForTable($table)->insert(
490  $table,
491  [
492  'pid' => 0,
493  ]
494  );
495  self::assertCSVDataSet(__DIR__ . '/../Fixtures/TextFieldDefaultValue/Assertions/' . $assertionFileName);
496  }
497 
498  public static function ‪jsonFieldDefaultValueTestDataProvider(): \Generator
499  {
500  yield 'json not null default empty object value' => [
501  'fixtureFileName' => 'json-not-null-default-empty-object-value.sql',
502  'table' => 'a_textfield_test_table',
503  'fieldName' => 'testfield',
504  'assertionFileName' => 'json-not-null-default-empty-object-value.csv',
505  'expectedDefaultValue' => '{}',
506  'expectedNotNull' => true,
507  'expectDefaultValue' => true,
508  ];
509  yield 'json default empty object value' => [
510  'fixtureFileName' => 'json-default-empty-object-value.sql',
511  'table' => 'a_textfield_test_table',
512  'fieldName' => 'testfield',
513  'assertionFileName' => 'json-default-empty-object-value.csv',
514  'expectedDefaultValue' => '{}',
515  'expectedNotNull' => false,
516  'expectDefaultValue' => true,
517  ];
518  yield 'json not null default empty array value' => [
519  'fixtureFileName' => 'json-not-null-default-empty-array-value.sql',
520  'table' => 'a_textfield_test_table',
521  'fieldName' => 'testfield',
522  'assertionFileName' => 'json-not-null-default-empty-array-value.csv',
523  'expectedDefaultValue' => '[]',
524  'expectedNotNull' => true,
525  'expectDefaultValue' => true,
526  ];
527  yield 'json default empty array value' => [
528  'fixtureFileName' => 'json-default-empty-array-value.sql',
529  'table' => 'a_textfield_test_table',
530  'fieldName' => 'testfield',
531  'assertionFileName' => 'json-default-empty-array-value.csv',
532  'expectedDefaultValue' => '[]',
533  'expectedNotNull' => false,
534  'expectDefaultValue' => true,
535  ];
536  yield 'json default NULL' => [
537  'fixtureFileName' => 'json-default-null.sql',
538  'table' => 'a_textfield_test_table',
539  'fieldName' => 'testfield',
540  'assertionFileName' => 'json-default-null.csv',
541  'expectedDefaultValue' => null,
542  'expectedNotNull' => false,
543  'expectDefaultValue' => true,
544  ];
545  yield 'json not null default data object value containing single-quote value' => [
546  'fixtureFileName' => 'json-not-null-default-data-object-value-with-single-quote-value.sql',
547  'table' => 'a_textfield_test_table',
548  'fieldName' => 'testfield',
549  'assertionFileName' => 'json-not-null-default-data-object-value-with-single-quote-value.csv',
550  'expectedDefaultValue' => '{"key1": "value1", "key2": 123, "key3": "value with a \' single quote"}',
551  'expectedNotNull' => true,
552  'expectDefaultValue' => true,
553  ];
554  yield 'json not null default data object value containing double-quote value' => [
555  'fixtureFileName' => 'json-not-null-default-data-object-value-with-double-quote-value.sql',
556  'table' => 'a_textfield_test_table',
557  'fieldName' => 'testfield',
558  'assertionFileName' => 'json-not-null-default-data-object-value-with-double-quote-value.csv',
559  'expectedDefaultValue' => '{"key1": "value1", "key2": 123, "key3": "value with a \" double quote"}',
560  'expectedNotNull' => true,
561  'expectDefaultValue' => true,
562  ];
563  }
564 
565  #[DataProvider('jsonFieldDefaultValueTestDataProvider')]
566  #[Test]
568  string $fixtureFileName,
569  string $table,
570  string $fieldName,
571  string $assertionFileName,
572  string|null $expectedDefaultValue,
573  bool $expectedNotNull,
574  bool $expectDefaultValue,
575  ): void {
576  $subject = $this->get(SchemaMigrator::class);
577  $statements = $this->sqlReader->getCreateTableStatementArray(file_get_contents(__DIR__ . '/../Fixtures/JsonFieldDefaultValue/' . $fixtureFileName));
578  $updateSuggestions = $subject->getUpdateSuggestions($statements);
579  $result = $subject->migrate(
580  $statements,
581  $updateSuggestions[‪ConnectionPool::DEFAULT_CONNECTION_NAME]['create_table']
582  );
583  self::assertSame([], $result);
584 
585  $tableDefinition = $this->getConnectionPool()->getConnectionForTable($table)->createSchemaManager()->introspectTable($table);
586  self::assertTrue($tableDefinition->hasColumn($fieldName));
587  $column = $tableDefinition->getColumn($fieldName);
588  if ($expectDefaultValue) {
589  self::assertArrayHasKey('default', $column->toArray());
590  self::assertSame($expectedDefaultValue, $column->getDefault());
591  } else {
592  self::assertArrayNotHasKey('default', $column->toArray());
593  }
594  self::assertSame($expectedNotNull, $column->getNotnull());
595 
596  $this->getConnectionPool()->getConnectionForTable($table)->insert(
597  $table,
598  [
599  'pid' => 0,
600  ]
601  );
602  self::assertCSVDataSet(__DIR__ . '/../Fixtures/JsonFieldDefaultValue/Assertions/' . $assertionFileName);
603  }
604 }
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeExistingIndex
‪changeExistingIndex()
Definition: SchemaMigratorTest.php:319
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\defaultNullWithoutNotNull
‪defaultNullWithoutNotNull()
Definition: SchemaMigratorTest.php:184
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\addNewColumns
‪addNewColumns()
Definition: SchemaMigratorTest.php:137
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\jsonFieldDefaultValueTest
‪jsonFieldDefaultValueTest(string $fixtureFileName, string $table, string $fieldName, string $assertionFileName, string|null $expectedDefaultValue, bool $expectedNotNull, bool $expectDefaultValue,)
Definition: SchemaMigratorTest.php:567
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeTableEngine
‪changeTableEngine()
Definition: SchemaMigratorTest.php:389
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\importStaticDataIgnoresTableDefinitions
‪importStaticDataIgnoresTableDefinitions()
Definition: SchemaMigratorTest.php:377
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\createNewTable
‪createNewTable()
Definition: SchemaMigratorTest.php:111
‪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\getTableDetails
‪getTableDetails()
Definition: SchemaMigratorTest.php:80
‪TYPO3\CMS\Core\Database\ConnectionPool\DEFAULT_CONNECTION_NAME
‪const DEFAULT_CONNECTION_NAME
Definition: ConnectionPool.php:50
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\jsonFieldDefaultValueTestDataProvider
‪static jsonFieldDefaultValueTestDataProvider()
Definition: SchemaMigratorTest.php:498
‪TYPO3\CMS\Core\Database\Schema\SchemaMigrator
Definition: SchemaMigrator.php:38
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\installPerformsOnlyAddAndCreateOperations
‪installPerformsOnlyAddAndCreateOperations()
Definition: SchemaMigratorTest.php:294
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\dropUnusedTable
‪dropUnusedTable()
Definition: SchemaMigratorTest.php:274
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\dropUnusedField
‪dropUnusedField()
Definition: SchemaMigratorTest.php:229
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$schemaManager
‪AbstractSchemaManager $schemaManager
Definition: SchemaMigratorTest.php:40
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\mergingTableDefinitionReturnsLatestColumnDefinition
‪mergingTableDefinitionReturnsLatestColumnDefinition()
Definition: SchemaMigratorTest.php:86
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\installCanPerformChangeOperations
‪installCanPerformChangeOperations()
Definition: SchemaMigratorTest.php:350
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\createNewTableIfNotExists
‪createNewTableIfNotExists()
Definition: SchemaMigratorTest.php:124
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\changeExistingColumn
‪changeExistingColumn()
Definition: SchemaMigratorTest.php:153
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\renameUnusedTable
‪renameUnusedTable()
Definition: SchemaMigratorTest.php:214
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\prepareTestTable
‪prepareTestTable(SchemaMigrator $schemaMigrator)
Definition: SchemaMigratorTest.php:71
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$sqlReader
‪SqlReader $sqlReader
Definition: SchemaMigratorTest.php:38
‪TYPO3\CMS\Core\Database\Schema\SchemaDiff
Definition: SchemaDiff.php:33
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\textFieldDefaultValueTest
‪textFieldDefaultValueTest(string $fixtureFileName, string $table, string $fieldName, string $assertionFileName, string|null $expectedDefaultValue, bool $expectedNotNull, bool $expectDefaultValue,)
Definition: SchemaMigratorTest.php:460
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\$connectionPool
‪ConnectionPool $connectionPool
Definition: SchemaMigratorTest.php:39
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\textFieldDefaultValueTestDataProvider
‪static textFieldDefaultValueTestDataProvider()
Definition: SchemaMigratorTest.php:409
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\tearDown
‪tearDown()
Definition: SchemaMigratorTest.php:50
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema
Definition: SchemaMigratorTest.php:18
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\renameUnusedField
‪renameUnusedField()
Definition: SchemaMigratorTest.php:199
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\setUp
‪setUp()
Definition: SchemaMigratorTest.php:42
‪TYPO3\CMS\Core\Database\Schema\TableDiff
Definition: TableDiff.php:33
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\notNullWithoutDefaultValue
‪notNullWithoutDefaultValue()
Definition: SchemaMigratorTest.php:170
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest
Definition: SchemaMigratorTest.php:37
‪TYPO3\CMS\Core\Tests\Functional\Database\Schema\SchemaMigratorTest\importStaticDataInsertsRecords
‪importStaticDataInsertsRecords()
Definition: SchemaMigratorTest.php:365