Skip to content
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

Rework handling quoted object names and reserved SQL keywords #6589

Merged
merged 4 commits into from
Nov 18, 2024

Conversation

morozov
Copy link
Member

@morozov morozov commented Nov 10, 2024

Q A
Type improvement

Technical details

With this change, DBAL will consistently quote all identifiers, regardless of whether they are keywords or not. It will also normalize the case of unquoted identifiers to match the behavior of the target database platform:

  • Oracle and IBM DB2 – upper case.
  • PostgreSQL – lower case.
  • All other platforms – original case.

To preserve the lower case of identifiers on Oracle and IBM DB2 or the upper case on PostgreSQL, the identifiers
should be quoted explicitly.

This change will address some of the issues documented in #4357.

End user impact

The impact on the users will depend on the set of target platforms they support in their applications

  • The users of MySQL, SQLite and SQL Server shouldn't experience any issues since these platforms don't change the case of the identifiers depending on their quoted.
  • The users of PostgreSQL will only experience issues if they rely on unquoted upper-case identifiers being automatically lower-cased. This sounds unlikely. Quoting those identifiers will preserve their upper case.
  • The users of Oracle and IBM DB2 will experience issues if they rely on lower-case keyword identifiers preserving their case. Quoting those identifiers will preserve their lower case.

@@ -671,8 +671,7 @@ protected function _getPortableTableColumnList(string $table, string $database,
foreach ($tableColumns as $tableColumn) {
$column = $this->_getPortableTableColumnDefinition($tableColumn);

$name = strtolower($column->getQuotedName($this->platform));
$list[$name] = $column;
$list[strtolower($column->getName())] = $column;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, it may be a breaking change but practically nobody will care.

The only reason why this might be useful is for Oracle to avoid conflicts between fully a upper-cased column name and non-fully upper-cased (e.g. ID and id). In this case, the latter will be enclosed in quotes, so even after lower-casing they will be different (id and "id") respectively.

However, this is a half-measure because:

  1. The behavior of quoting of introspected names is implemented only for Oracle but not for IBM DB2 or PostgreSQL which are also SQL-92 compliant and are prone to the same problem.
  2. Even on Oracle, it won't work for columns like Id and iD – they both will be converted to "id" and collide.

Comment on lines +190 to 191
'CREATE INDEX main."i" ON mytable ("a", "b")',
$this->platform->getCreateIndexSQL($indexDef, 'main.mytable'),
Copy link
Member Author

@morozov morozov Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The index name is quoted because it comes from Index, and we know that it's an unqualified name (i.e., the literal value). The table name comes from 'main.table', which is SQL. It's unquoted in the method argument, so it's unquoted in the resulting SQL as well. This isn't entirely correct but is out of scope.

@@ -95,31 +94,4 @@ public static function columnNameProvider(): iterable
yield ['C1', 'c1_x'];
yield ['importantColumn', 'veryImportantColumn'];
}

/** @throws Exception */
public function testRenameColumToQuoted(): void
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was introduced in #6602 and is no longer necessary. All new names are quoted by default, so this case is covered by existing tests.

}

protected function getGenerateForeignKeySql(): string
{
return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
return 'ALTER TABLE test ADD FOREIGN KEY (`fk_name_id`) REFERENCES `other_table` (`id`)';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table name here is still unquoted because AbstractPlatform::getCreateForeignKeySQL() accepts the table name as SQL, not as a literal. Consistent quoting can be achieved by introducing values objects to represent object names (#4772).

Comment on lines +397 to 398
// Foreign column with reserved keyword as name
$foreignTable->addColumn('create', Types::STRING);
Copy link
Member Author

@morozov morozov Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, this part of the test is now redundant since all identifiers are rendered in SQL consistently regardless of whether they are keywords or not. But redesigning the test to correspond to the new design is a bit too much work for now.

@morozov morozov added this to the 5.0.0 milestone Nov 17, 2024
@morozov morozov marked this pull request as ready for review November 17, 2024 01:58
@morozov morozov requested review from greg0ire and derrabus November 17, 2024 01:58
@morozov morozov merged commit 13c6154 into doctrine:5.0.x Nov 18, 2024
87 checks passed
@morozov morozov deleted the name-parser branch November 18, 2024 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants