-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Migrate to Index editor API #12357
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
base: 3.7.x
Are you sure you want to change the base?
Migrate to Index editor API #12357
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |||||
| use Doctrine\DBAL\Schema\ForeignKeyConstraintEditor; | ||||||
| use Doctrine\DBAL\Schema\Index; | ||||||
| use Doctrine\DBAL\Schema\Index\IndexedColumn; | ||||||
| use Doctrine\DBAL\Schema\Index\IndexType; | ||||||
| use Doctrine\DBAL\Schema\Name\Identifier; | ||||||
| use Doctrine\DBAL\Schema\Name\UnqualifiedName; | ||||||
| use Doctrine\DBAL\Schema\NamedObject; | ||||||
|
|
@@ -356,7 +357,38 @@ public function getSchemaFromMetadata(array $classes): Schema | |||||
|
|
||||||
| if (isset($class->table['uniqueConstraints'])) { | ||||||
| foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) { | ||||||
| $uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []); | ||||||
| /** @phpstan-ignore function.alreadyNarrowedType (legacy DBAL compatibility) */ | ||||||
| if (method_exists(Index::class, 'editor')) { | ||||||
| $editor = Index::editor(); | ||||||
| $editor->setUnquotedName('tmp__' . $indexName); | ||||||
| $columns = array_map( | ||||||
| static function (string $colName): IndexedColumn { | ||||||
| assert($colName !== ''); | ||||||
|
|
||||||
| return new IndexedColumn( | ||||||
| UnqualifiedName::unquoted($colName), | ||||||
| null, | ||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure whether I should specify a length here.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the ORM supports
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is supported, I have been able to specify a negative length in a test and see a deprecation. Then I wondered how this was even possible given I did not write code to parse the length. I dug deeper and found that even though we are creating an Line 368 in d3b47d2
So… parsing options is useless? 😐
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, it's worse than that. Instead of calling
Note that the index constructor is internal, and its signature will change in 5.0 (use the editor instead).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The table is created like this: Line 207 in d3b47d2
So now that I have a new instance created in 4., I need to 5. Replace the previous instance with the new one in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's one of the reasons why the old mutable API is not yet fully deprecated. Yes, we would replace the table in the schema in the same way as we would perform any modifications in the table itself, but there's no API for that. The You can edit tables but you cannot edit the schema (yet), so you at least need to pre-create all the tables and then create the schema.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. What's the plan regarding the schema editor? Is it something that you think is going to ship in DBAL 5? If yes, then maybe I shouldn't work on this until that editor exists. |
||||||
| ); | ||||||
| }, | ||||||
| $this->getIndexColumns($class, $indexData), | ||||||
| ); | ||||||
| $editor->setColumns(...$columns); | ||||||
| $editor->setType(IndexType::UNIQUE); | ||||||
| if (isset($indexData['options']['predicate'])) { | ||||||
| $editor->setPredicate($indexData['options']['predicate']); | ||||||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there other options that I should be handling here? And does
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think so. The
To take a step back, why does the ORM translate "unique constraints" to "unique indexes"? Speaking of indexes, I don't think a unique index can have a predicate (i.e. this value must be unique if it matches this and this predicate).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because it can span several columns I suppose?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A constraint can also span several columns. I think the real reason is that support for unique constraints was added later than in was supported by the ORM. Or just because they are functionally very close (the difference is primarily in semantics).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm ashamed to say I just did not know about unique constraints other than as implemented with keywords added to a single column definition. Or maybe I did but I forgot 😅
Oh? Do constraints help with performance? If not I'd rather say that would be the primary difference.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TL;DR: semantically, unique constraints are about intention (I want the values in these columns to be unique), unique indexes are about the physical implementation (build me a B-tree or hash and make sure all values are unique and use it for the queries where appropriate). For example (Postgres):
IIRC, for MySQL the two are just synonyms (you can create one and then drop the other).
Not the constraints themselves, but the underlying unique indexes, as a side effect, definitely. But if you want to optimize queries, it's better if you create an index because you know which queries you want to optimize and what type of index to create. If you only want to enforce uniqueness, you create a unique constraint. Relying on unique constraints for performance is similar to using transient dependencies in your code w/o declaring the dependency explicitly. It works, but may have its issues. |
||||||
| } | ||||||
|
|
||||||
| $uniqIndex = $editor->create(); | ||||||
| } else { | ||||||
| $uniqIndex = new Index( | ||||||
| 'tmp__' . $indexName, | ||||||
| $this->getIndexColumns($class, $indexData), | ||||||
| true, | ||||||
| false, | ||||||
| [], | ||||||
| $indexData['options'] ?? [], | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| foreach ($table->getIndexes() as $tableIndexName => $tableIndex) { | ||||||
| if ($tableIndex->isFulfilledBy($uniqIndex)) { | ||||||
|
|
||||||
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.
Unsure if I should use this or
quotedhere.Uh oh!
There was an error while loading. Please reload this page.
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.
It depends on the value of
$colName. It could befirst_name(unquoted) or"first_name"(quoted, the quotes are part of the value). If the ORM accepts names with quotes in the string form and wants to pass them to the DBAL as objects, it needs to parse them. See the implementation ofIndex#parseColumns()as a BC layer in DBAL 4.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 see that
getIndexColumnsresorts to a "quote strategy", and that the default implementation supports both ways:orm/src/Mapping/DefaultQuoteStrategy.php
Lines 29 to 31 in d3b47d2
Let me parse them then.
Uh oh!
There was an error while loading. Please reload this page.
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.
Hang on…
Parsers::getUnqualifiedNameParser()is internal. Is it OK to use it?And should we deprecate the need for parsing, that is, disallow quoting at the ORM metadata level? Since DBAL is going to quote everything, if I understood correctly.
Uh oh!
There was an error while loading. Please reload this page.
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 term "quoted" is a bit overloaded, even in the SQL standard. This article and the one it references in the beginning should answer all the questions on quoting in the DBAL.
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 guess, yes, by the ORM. The plan is to migrate all DBAL APIs to names as objects – at that point parsing will be no longer necessary, but this is a lengthy process which will likely span major releases.
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.
Well, maybe you don't need to. Let's at least try to explore this possibility.
In the
DefaultQuoteStrategy, the actual value of the column name is taken from one property and whether it needs to be quoted is taken from another. This is exactly the structure and the separation of concerns used in the new DBAL API.In the
AnsiQuoteStrategy, the "quoted" property is ignored, so the name effectively will be put into the SQL as is. Um... that's where you have to parse the name.So yeah, in the worst case scenario you need to parse. Not parsing in the default case would be solely a runtime optimization, it won't allow to save the engineering effort.