-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query ALTER TABLE always transmitted if collate specified on an attribute #6146
Comments
Can you reproduce the issue by only using DBAL APIs? If not, we will also need the ORM version that you are using. |
It is the same case as described here: #5338 |
Here's a failing test script using just the DBAL API calls. <?php
// Using MySQL 8.3.0 and doctrine/dbal 4.0.0
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\StringType;
require __DIR__ . '/vendor/autoload.php';
$conn = DriverManager::getConnection(params: [
'dbname' => 'test',
'user' => 'test',
'password' => 'test',
'host' => 'localhost',
'driver' => 'pdo_mysql',
]);
$conn->executeStatement(sql: 'DROP TABLE IF EXISTS foo');
echo 'STEP 1', PHP_EOL;
for ($i = 0; $i < 5; $i++) {
$column = new Column(
name: 'bar',
type: new StringType(),
options: ['length' => 32, 'platformOptions' => ['collation' => 'utf8mb4_unicode_ci']]
);
$table = new Table(name: 'foo', columns: [$column]);
$new_schema = new Schema(tables: [$table]);
$schema_manager = $conn->createSchemaManager();
$old_schema = $schema_manager->introspectSchema();
$comparator = $schema_manager->createComparator();
$schema_diff = $comparator->compareSchemas(oldSchema: $old_schema, newSchema: $new_schema);
$queries = $conn->getDatabasePlatform()->getAlterSchemaSQL(diff: $schema_diff);
foreach ($queries as $query) {
echo $query, ';', PHP_EOL;
$conn->executeStatement(sql: $query);
}
}
echo 'STEP 2', PHP_EOL;
for ($i = 0; $i < 5; $i++) {
$column = new Column(
name: 'bar',
type: new StringType(),
options: ['length' => 32, 'platformOptions' => ['collation' => 'utf8_unicode_ci']]
);
$table = new Table(name: 'foo', columns: [$column]);
$new_schema = new Schema(tables: [$table]);
$schema_manager = $conn->createSchemaManager();
$old_schema = $schema_manager->introspectSchema();
$comparator = $schema_manager->createComparator();
$schema_diff = $comparator->compareSchemas(oldSchema: $old_schema, newSchema: $new_schema);
$queries = $conn->getDatabasePlatform()->getAlterSchemaSQL(diff: $schema_diff);
foreach ($queries as $query) {
echo $query, ';', PHP_EOL;
$conn->executeStatement(sql: $query);
}
}
echo 'STEP 3', PHP_EOL;
for ($i = 0; $i < 5; $i++) {
$column = new Column(
name: 'bar',
type: new StringType(),
options: ['length' => 32, 'platformOptions' => ['collation' => 'utf8mb4_unicode_ci']]
);
$table = new Table(name: 'foo', columns: [$column]);
$new_schema = new Schema(tables: [$table]);
$schema_manager = $conn->createSchemaManager();
$old_schema = $schema_manager->introspectSchema();
$comparator = $schema_manager->createComparator();
$schema_diff = $comparator->compareSchemas(oldSchema: $old_schema, newSchema: $new_schema);
$queries = $conn->getDatabasePlatform()->getAlterSchemaSQL(diff: $schema_diff);
foreach ($queries as $query) {
echo $query, ';', PHP_EOL;
$conn->executeStatement(sql: $query);
}
} Output:
Interestingly, if step-2 uses
I note that this uses the |
I have analysed this issue, and found several parts, all related to Overview: the comparator generates two SQL snippets:
Then, if these SQL snippets differ, an Issue 1: The introspected schema may use We can fix this in Issue 2: The In versions of MySQL up to 8.0.29, this contains entries for The result is that the We can fix this by unsetting These two simple changes will fix the reported issue. Here's an updated version of private function normalizeOptions(array $options): array
{
if (isset($options['charset']) && ! isset($options['collation'])) {
$options['collation'] = $this->charsetMetadataProvider->getDefaultCharsetCollation($options['charset']);
} elseif (isset($options['collation'])) {
// If collation is specified, charset is redundant.
unset($options['charset']);
}
// utf8mb3 is a synonym for utf8. Be consistent.
if (isset($options['collation']) && str_starts_with($options['collation'], 'utf8mb3_')) {
$options['collation'] = 'utf8_' . substr($options['collation'], 8);
}
if (isset($options['charset']) && $options['charset'] === 'utf8mb3') {
$options['charset'] = 'utf8';
}
return $options;
} However, there is one more case - where the user only specifies the I'm not sure this is a common use-case. I've never seen anyone just set |
Bug Report
Summary
ALTER TABLE always sent with precision options={"collation":"XXX"} at ORM level (MySQL 5.7 / MariaDB 10.1 database)
Current behaviour
recision of options={"collation":"XXX"} of an attribute of an entity
Tested with utf8_general_ci, utf8mb4_general_ci, utf8_unicode_ci, utf8mb4_unicode_ci, latin1_swedish_ci
A query like this is returned:
ALTER TABLE table CHANGE attribute attribute VARCHAR(15) CHARACTER SET utf8 NOT NULL COLLATE
utf8_general_ci
;attribute was in this example initially in "utf8" and "utf8_general_ci". Even after executing this database query, this happens again.
Tested with:
How to reproduce
To reproduce the bug according to the example provided:
By ORM specify the attribute of an entity: options={"collation":"utf8_general_ci"} and a non-null string of length 15
Run a doctrine:schema:update --dump-sql
Expected behaviour
No ALTER TABLE should be transmitted
The text was updated successfully, but these errors were encountered: