-
-
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
Added support functional indexes for MySQL and Postgres #6414
base: 4.3.x
Are you sure you want to change the base?
Conversation
3e07ddf
to
53b632c
Compare
53b632c
to
fb58217
Compare
There was a problem hiding this 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.
586dfcb
to
dfef783
Compare
The test failures appear to be related to your changes. |
dfef783
to
6484bf9
Compare
@derrabus Please pay attention to MySQL platform inheritance. Did I do it as you said? |
@derrabus Do we have to restart test? |
Don't mind the failing CodeCov upload. |
@derrabus Is where any news ? Could i add any additional information ? |
I need to find the time for a review. Soon, I hope. |
There was a problem hiding this 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?
$table->addColumn('column2', Types::INTEGER, ['notnull' => false]); | ||
$table->addIndex(['column1', 'column2', '(column2 IS NOT NULL)'], 'func_idx'); | ||
|
||
$this->expectException(InvalidArgumentException::class); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Added check
@@ -561,6 +562,16 @@ public function getCreateIndexSQL(Index $index, string $table): string | |||
)); | |||
} | |||
|
|||
if ($index->isFunctional() && ! $this->supportsFunctionalIndex()) { |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
src/Platforms/MySQL8013Platform.php
Outdated
{ | ||
public function getColumnNameForIndexFetch(): string | ||
{ | ||
return "COALESCE(COLUMN_NAME, CONCAT('(', REPLACE(EXPRESSION, '\\\''', ''''), ')'))"; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
src/Driver/AbstractMySQLDriver.php
Outdated
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', | ||
); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
Thanks for your feedback, I'll take care of your comments. Not sure about another databases, but I'll look into it. |
if (! $platform->supportsFunctionalIndex()) { | ||
self::markTestSkipped('Platform does not support functional indexes.'); | ||
} |
There was a problem hiding this comment.
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.
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()); | ||
} |
There was a problem hiding this comment.
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
?
foreach ($table->getIndexes() as $index) { | ||
$this->expectException(Exception::class); | ||
|
||
$platform->getCreateIndexSQL( | ||
$index, | ||
$table->getQuotedName($platform), | ||
); | ||
} |
There was a problem hiding this comment.
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.
/** | ||
* A flag that indicates whether the platform supports functional indexes. | ||
*/ | ||
public function supportsFunctionalIndex(): bool | ||
{ | ||
return true; | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Functional tests that are missing: Schema comparison and migration. |
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