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

Added support functional indexes for MySQL and Postgres #6414

Open
wants to merge 2 commits into
base: 4.3.x
Choose a base branch
from

Conversation

prohalexey
Copy link

Q A
Type feature

Summary

This feature allows the DBAL to create and work with functional indexes.

Documentation:
PostgreSQL (version 7 and higher) - https://www.postgresql.org/docs/7.3/indexes-functional.html
MySQL (version 8.0.13 and higher) - https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-functional-key-parts

@prohalexey prohalexey force-pushed the functionalIndexes branch 3 times, most recently from 3e07ddf to 53b632c Compare May 31, 2024 09:01
@prohalexey
Copy link
Author

@derrabus @greg0ire could you run checks?

@derrabus derrabus changed the base branch from 4.0.x to 4.1.x June 6, 2024 08:01
Copy link
Member

@derrabus derrabus left a comment

Choose a reason for hiding this comment

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

Thank you. I have to do a deeper review.

As this is not a bugfix, so please target the next feature release (4.1.x currently). I've retargeted your PR, but you'll need to rebase and resolve conflicts. The reason for that is that we've already introduced new MySQL platform classes in 4.1.x. Also, we don't want merge commits in PRs. A rebase should get rid of those.

src/Driver/AbstractMySQLDriver.php Outdated Show resolved Hide resolved
src/Driver/AbstractMySQLDriver.php Show resolved Hide resolved
src/Platforms/AbstractPlatform.php Outdated Show resolved Hide resolved
src/Schema/PostgreSQLSchemaManager.php Outdated Show resolved Hide resolved
@derrabus
Copy link
Member

derrabus commented Jun 6, 2024

The test failures appear to be related to your changes.

@prohalexey
Copy link
Author

@derrabus Please pay attention to MySQL platform inheritance. Did I do it as you said?

@prohalexey
Copy link
Author

@derrabus Do we have to restart test?

@derrabus
Copy link
Member

derrabus commented Jun 7, 2024

Don't mind the failing CodeCov upload.

@prohalexey
Copy link
Author

@derrabus Is where any news ? Could i add any additional information ?

@derrabus derrabus added this to the 4.1.0 milestone Jun 12, 2024
@derrabus
Copy link
Member

I need to find the time for a review. Soon, I hope.

Copy link
Member

@derrabus derrabus left a comment

Choose a reason for hiding this comment

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

Thank you for this promising contribution. When we add an abstraction for database feature, we should try to implement that abstraction for all platforms that support that feature. Otherwise we might have chosen the wrong abstraction without knowing it.

You've implemented the abstraction for MySQL and Postgres only, but if I'm not mistaken, all other platforms (except for MariaDB) should support functional indexes in some shape or form. Could you look into those as well?

tests/Functional/Platform/FunctionalIndexTest.php Outdated Show resolved Hide resolved
tests/Functional/Platform/FunctionalIndexTest.php Outdated Show resolved Hide resolved
tests/Functional/Platform/FunctionalIndexTest.php Outdated Show resolved Hide resolved
$table->addColumn('column2', Types::INTEGER, ['notnull' => false]);
$table->addIndex(['column1', 'column2', '(column2 IS NOT NULL)'], 'func_idx');

$this->expectException(InvalidArgumentException::class);
Copy link
Member

Choose a reason for hiding this comment

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

We should have an opinion on the exception message as well.

Copy link
Author

Choose a reason for hiding this comment

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

The text of exception is created dynamically so we should not check it? Maybe we could extend InvalidArgumentException and name it as FunctionalIndexNotSupportedException?

Copy link
Member

Choose a reason for hiding this comment

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

Especially when the message is created dynamically, we need an assertion for it. How would I make sure otherwise that the correct exception message is rendered? In this particular case, we should have all the information we need to compute the exception message we expect.

Copy link
Author

Choose a reason for hiding this comment

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

Ok. Added check

tests/Functional/Platform/FunctionalIndexTest.php Outdated Show resolved Hide resolved
src/Platforms/AbstractPlatform.php Outdated Show resolved Hide resolved
src/Platforms/MySQL8013Platform.php Outdated Show resolved Hide resolved
@@ -561,6 +562,16 @@ public function getCreateIndexSQL(Index $index, string $table): string
));
}

if ($index->isFunctional() && ! $this->supportsFunctionalIndex()) {
Copy link
Member

Choose a reason for hiding this comment

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

We're inside a platform class for SQLite. We should know whether SQLite supports functional indexes.

@@ -101,10 +109,14 @@ public function getQuotedColumns(AbstractPlatform $platform): array
foreach ($this->_columns as $column) {
$length = array_shift($subParts);

$quotedColumn = $column->getQuotedName($platform);
if ($this->isFunctional()) {
$quotedColumn = $column->getName();
Copy link
Member

Choose a reason for hiding this comment

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

A functional column does not have a name. We should find a better representation for such columns.

Copy link
Author

Choose a reason for hiding this comment

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

And which representation is better :) Even in databases, functional indexes are stored in a hackish way.

{
public function getColumnNameForIndexFetch(): string
{
return "COALESCE(COLUMN_NAME, CONCAT('(', REPLACE(EXPRESSION, '\\\''', ''''), ')'))";
Copy link
Member

Choose a reason for hiding this comment

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

This looks unnecessarily complicated. Can't we just select the COLUMN_NAME and EXPRESSION columns as separate columns?

Copy link
Author

Choose a reason for hiding this comment

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

I doesn't make sense, because expression could be a part of composite index for example: (column, column, expression, column). So in term of code it is a column

Copy link
Member

Choose a reason for hiding this comment

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

I understand that, but that does not really answer my question.

Comment on lines 67 to 71
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/6343',
'Support for MySQL <= 8.0.13 is deprecated and will be removed in DBAL 5',
);
Copy link
Member

Choose a reason for hiding this comment

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

If you move the deprection trigger above the if statement, you can remove the other deprecation further below. Please also add a note to our upgrade guide about the new deprecation.

Copy link
Author

Choose a reason for hiding this comment

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

Please look it closer, I think I'm made it right. There is a mariadb branch, mysql 8.0 and higher and default branch for lower(5.7 is still supported, despite of deprecation).

Copy link
Member

Choose a reason for hiding this comment

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

After your change, we have a deprecation message for MySQL < 8.0.13 and another one for MySQL < 8.0. The second one is redundant.

@prohalexey
Copy link
Author

Thanks for your feedback, I'll take care of your comments. Not sure about another databases, but I'll look into it.

@derrabus derrabus removed this from the 4.1.0 milestone Jul 8, 2024
Comment on lines +19 to +21
if (! $platform->supportsFunctionalIndex()) {
self::markTestSkipped('Platform does not support functional indexes.');
}
Copy link
Member

@derrabus derrabus Jul 8, 2024

Choose a reason for hiding this comment

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

Again, in our tests we should know which platform supports functional indexes. We should not ask the platform.

Comment on lines +39 to +43
if ($platform instanceof PostgreSQLPlatform) {
self::assertEquals(['column1', 'column2', '(column2 IS NOT NULL)'], $index->getColumns());
} elseif ($platform instanceof MySQLPlatform) {
self::assertEquals(['column1', 'column2', '(`column2` is not null)'], $index->getColumns());
}
Copy link
Member

Choose a reason for hiding this comment

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

And what if the platform is neither PostgreSQLPlatform nor MySQLPlatform?

Comment on lines +45 to +52
foreach ($table->getIndexes() as $index) {
$this->expectException(Exception::class);

$platform->getCreateIndexSQL(
$index,
$table->getQuotedName($platform),
);
}
Copy link
Member

Choose a reason for hiding this comment

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

This loop does not make sense. The test will exit at the first exception. At this point, we should know…

  • … how many indexes the table has.
  • … which of the raises the exception.

Comment on lines +2006 to +2012
/**
* A flag that indicates whether the platform supports functional indexes.
*/
public function supportsFunctionalIndex(): bool
{
return true;
}
Copy link
Member

Choose a reason for hiding this comment

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

Repeating myself: I don't think we need this supports() function. We've removed a lot of those and I'm not eager to add any new ones.

Copy link
Author

Choose a reason for hiding this comment

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

Why we have supportsPartialIndexes but can't have supportsFunctionalIndex ?
What's the difference ?

Copy link
Member

Choose a reason for hiding this comment

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

The difference is that the method in question was introduced in 2014 and we have 2024 now. Quoting myself:

We've removed a lot of those and I'm not eager to add any new ones.

Copy link
Author

Choose a reason for hiding this comment

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

I can't figure out what the alternative is? If you told me this approach is wrong, what is the right approach?
There are databases that do not supports functional indexes, so we must have a way to check and know that.

Copy link
Author

Choose a reason for hiding this comment

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

I can change the code, but I can't read your thoughts on it.

Copy link
Member

Choose a reason for hiding this comment

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

I can't figure out what the alternative is?

Create a new method in the platform classes that renders DDL for a functional index. Let it throw by default. Override that method inside the platform classes that support functional indexes.

There are databases that do not supports functional indexes, so we must have a way to check and know that.

No, we don't.

@derrabus
Copy link
Member

derrabus commented Jul 8, 2024

Functional tests that are missing: Schema comparison and migration.

@derrabus derrabus changed the base branch from 4.1.x to 4.2.x August 15, 2024 09:17
@derrabus derrabus changed the base branch from 4.2.x to 4.3.x October 10, 2024 13:08
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