Skip to content

Commit ebb87c5

Browse files
committed
Fixing columns order in Oracle (and many other small issues)
1 parent 0620eab commit ebb87c5

6 files changed

+94
-6
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"doctrine/annotations": "^1.10",
3636
"zendframework/zend-code": "^3.4",
3737
"psr/container": "^1",
38-
"brain-diminished/schema-version-control": "^1.0.2",
38+
"brain-diminished/schema-version-control": "dev-master",
3939
"ext-PDO": "*",
4040
"ext-json": "*",
4141
"ext-hash": "*",

phpunit.oracle.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
convertNoticesToExceptions="true"
88
convertWarningsToExceptions="true"
99
processIsolation="false"
10-
stopOnFailure="true"
10+
stopOnFailure="false"
1111
bootstrap="vendor/autoload.php"
1212
>
1313
<testsuites>

src/SchemaLockFileDumper.php

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\DBAL\Schema\Schema;
1111
use Doctrine\DBAL\Schema\Table;
1212
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
13+
use TheCodingMachine\TDBM\Utils\ColumnsReorderer;
1314
use TheCodingMachine\TDBM\Utils\ImmutableCaster;
1415
use function array_map;
1516
use function array_values;
@@ -92,6 +93,7 @@ public function getSchema(bool $ignoreCache = false): Schema
9293
} else {
9394
$this->schema = $this->schemaVersionControlService->loadSchemaFile();
9495
ImmutableCaster::castSchemaToImmutable($this->schema);
96+
ColumnsReorderer::reorderTableColumns($this->schema);
9597
$this->cache->save($cacheKey, $this->schema);
9698
}
9799
}

src/Utils/ColumnsReorderer.php

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\TDBM\Utils;
5+
6+
use Doctrine\DBAL\Schema\Column;
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\DBAL\Schema\Table;
9+
use ReflectionClass;
10+
use function array_merge;
11+
use function in_array;
12+
13+
/**
14+
* The sole purpose of this class is to work around a DBAL bug related to Oracle that
15+
* does not return the columns in the same order as the other databases.
16+
* In order to have consistent results (and consistent tests), we need those columns in
17+
* the same order in all databases.
18+
*/
19+
class ColumnsReorderer
20+
{
21+
public static function reorderTableColumns(Schema $schema): void
22+
{
23+
foreach ($schema->getTables() as $table) {
24+
self::reorderColumnsForTable($table);
25+
}
26+
}
27+
28+
private static function reorderColumnsForTable(Table $table)
29+
{
30+
$columns = self::getColumnsInExpectedOrder($table);
31+
32+
// Note: the only way to rewrite columns order is to tap into a PROTECTED method.
33+
// This is bad BUT!
34+
// - we only do this for Oracle databases
35+
// - we know Doctrine consider protected methods as part of the API for BC changes so risk is limited.
36+
$refClass = new ReflectionClass(Table::class);
37+
$addColumnMethod = $refClass->getMethod('_addColumn');
38+
$addColumnMethod->setAccessible(true);
39+
40+
foreach ($columns as $column) {
41+
$table->dropColumn($column->getName());
42+
$addColumnMethod->invoke($table, $column);
43+
//$table->_addColumn($column);
44+
}
45+
}
46+
47+
/**
48+
* @param Table $table
49+
* @return Column[]
50+
*/
51+
private static function getColumnsInExpectedOrder(Table $table): array {
52+
if ($table->hasPrimaryKey()) {
53+
$pkColumns = $table->getPrimaryKey()->getUnquotedColumns();
54+
} else {
55+
$pkColumns = [];
56+
}
57+
$fks = $table->getForeignKeys();
58+
59+
$fkColumns = [];
60+
61+
foreach ($fks as $fk) {
62+
$fkColumns = array_merge($fkColumns, $fk->getUnquotedLocalColumns());
63+
}
64+
65+
$first = [];
66+
$second = [];
67+
$last = [];
68+
69+
foreach ($table->getColumns() as $column) {
70+
if (in_array($column->getName(), $pkColumns, true)) {
71+
$first[] = $column;
72+
} elseif (in_array($column->getName(), $fkColumns, true)) {
73+
$second[] = $column;
74+
} else {
75+
$last[] = $column;
76+
}
77+
}
78+
return array_merge($first, $second, $last);
79+
}
80+
}

tests/Dao/TestCountryDao.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function getCountriesByUserCount()
2121
SELECT country.*
2222
FROM country
2323
LEFT JOIN users ON users.country_id = country.id
24-
GROUP BY country.id
24+
GROUP BY country.id, country.label
2525
ORDER BY COUNT(users.id) DESC
2626
SQL;
2727

tests/SchemaLockFileDumperTest.php

+9-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Doctrine\Common\Cache\ArrayCache;
2525
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
2626
use TheCodingMachine\TDBM\Utils\ImmutableCaster;
27+
use function sort;
2728

2829
class SchemaLockFileDumperTest extends TDBMAbstractServiceTest
2930
{
@@ -45,16 +46,21 @@ public function testSchemaLock(): void
4546
//lock file doesn't save the database name so we have to replace it manually.
4647
ImmutableCaster::castSchemaToImmutable($schemaFromConnec);
4748
foreach ($schemaFromConnec->getTableNames() as $tableName) {
48-
$tableNames[] = str_replace(['tdbm_testcase', 'postgres'], 'public', $tableName);
49+
$tableNames[] = str_replace(['tdbm_testcase', 'postgres', 'tdbm'], 'public', $tableName);
4950
}
5051

5152
$cache = new ArrayCache();
5253
$schemaLockFileDumper = new SchemaLockFileDumper(self::getConnection(), $cache, Configuration::getDefaultLockFilePath());
5354

5455
$schemaFromAnalyser = $schemaLockFileDumper->getSchema(true);
5556
$schemaFromAnalyserCached = $schemaLockFileDumper->getSchema();
56-
$this->assertEquals($tableNames, $schemaFromAnalyser->getTableNames());
57-
$this->assertEquals($schemaFromAnalyser->getTableNames(), $schemaFromAnalyserCached->getTableNames());
57+
sort($tableNames);
58+
$tablesFromAnalyser = $schemaFromAnalyser->getTableNames();
59+
sort($tablesFromAnalyser);
60+
$tablesFromAnalyserCached = $schemaFromAnalyserCached->getTableNames();
61+
sort($tablesFromAnalyserCached);
62+
$this->assertEquals($tableNames, $tablesFromAnalyser);
63+
$this->assertEquals($tablesFromAnalyser, $tablesFromAnalyserCached);
5864
}
5965

6066
public function testGetSchema(): void

0 commit comments

Comments
 (0)