Problem/Motivation
Over a year ago in #2721313: Upgrade path between revisionable / non-revisionable entities, we added a TemporaryTableMapping class because we needed a way to generate a temporary/fake table structure in which we could save entities during the process of converting an entity type to be revisionable. After the conversion process is completed, all those temporary tables are renamed and they become the actual 'live' storage of the entity type.
Proposed resolution
This extra table mapping class was needed because we didn't have a way to say "make this entity type use this table mapping for the duration of this process".
Now that we're finally getting close to having that ability, it's time to retire the temporary table mapping class and replace it with a simple table prefix argument that can be set on the table mapping object.
Remaining tasks
Review / discuss.
User interface changes
Nope.
API changes
API addition: The table mapping class can use a prefix for all its tables.
Data model changes
Nope.
| Comment | File | Size | Author |
|---|---|---|---|
| #21 | interdiff-21.txt | 8.9 KB | amateescu |
| #21 | 2960136-21.patch | 28.48 KB | amateescu |
| #19 | interdiff-19.txt | 1.51 KB | amateescu |
| #19 | 2960136-19.patch | 22.83 KB | amateescu |
| #18 | interdiff-18.txt | 8.7 KB | amateescu |
Comments
Comment #2
amateescu commentedHere's how this would look like.
The final patch would also need to remove or deprecate the
TemporaryTableMappingclass and change all the code inSqlContentEntityStorageSchemaConverterto actually utilize this new capability, but we're blocked on quite a few issues until we're able to do that.Comment #3
amateescu commentedHere's a WIP patch which would allow us to deprecate the
TemporaryTableMappingclass and clean up the code inSqlContentEntityStorageSchemaConvertera bit.It doesn't work yet because the table names are also generated in the storage handler, regardless of the ones computed in the table mapping. Which means we need to do #2232465: Deprecate table names from entity definitions first :/
Comment #4
amateescu commentedAfter the work done in #2981220: SqlContentEntityStorage should use the table names provided by its table mapping and #2960046: Allow the table mapping to be instantiated with a custom entity type definition and a few more fixes here, this is actually working!
Comment #5
amateescu commentedThe blockers are in! The non-combined patch should apply now and we're ready for reviews :)
Comment #6
tstoecklerSince
$table_namecontains the prefix at this point, I think this change is unnecessary.I guess this is a pre-existing issue (at least for dedicated field tables), but how this actually work? Since
DefaultTableMapping::generateFieldTableName()will generate a different result thanSqlContentEntityStorageSchemaConverter::getPrefixedTableName()I don't understand how$temp_table_namecan exist at this point.Interesting that this is moved, especially with the
setTemporary()call in between. Can you explain that?This one is correct, I think, but wouldn't it be more intuitive to do
strlen($prefixed_table_name) > 48?Comment #7
amateescu commentedThanks for reviewing!
1. Ugh, that needed quite some time to figure out correctly. Updated the method to also take into account the length of the prefix.
2. Not sure how that worked for longer dedicated table names, but we actually have a simpler way to map temporary table names to the ones that will be used after the update, since we have both table mapping objects available.
3. I moved that line above because I think it makes the code easier to follow if both lines for getting the entity type and field storage definitions stay together. The
setTemporary()call does not impact the array of updated storage definitions, it's just needed before getting the table mapping :)4. Cleaned this up a bit as well.
Comment #8
tstoecklerFIrst of all, I forgot to point this out before: (Once again) Awesome work!! This is a really neat consolidation that removes a weirdness while introducing greater flexibility at the same time. What's not to love!
Awesome, you are really on a roll with removing weirdness from Drupal at the moment. Nice!
Well, if you put it that way... ...it totally makes sense! Thanks for the explanation.
Some minor points on the new patch/interdiff:
Let's move it outside of the
ifso we can use it when building$table_nameabove, as well.The logic here is correct, it's a bit hard to understand, though. Can we expand the comment just a tad, to note that 16 - 10 - (4|2) = 2|4?
Leaving at "needs review", though, as more eyes wouldn't hurt and my points aren't very substantial.
Comment #10
amateescu commented@tstoeckler, in this case I'm cleaning up my own mess, so I feel double happy about it :)
Fixed both points from #8, and also improved the test coverage to rely on the actual temporary table names returned by the table mapping instead of the bogus values from
\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchemaConverter::getPrefixedTableName.We can also go a bit further and remove most usages of
setTemporary(). There's only one left, which can only be removed in #2976035: Entity type CRUD operations must use the last installed entity type and field storage definitions .The fails from #7 are funny, because that test does not install the entity type properly so the
\Drupal\Core\Entity\EntityTypeInterface::ID_MAX_LENGTHvalue is never taken into account. Let's ensure that the entity type ID length in\Drupal\Core\Entity\Sql\DefaultTableMapping::generateFieldTableName()is correct at all times, even when the entity type object was not properly validated.Comment #11
tstoecklerWow, I almost don't want to RTBC in order not to stop you from removing more messy stuff ;-)
It really wouldn't hurt to get another pair of eyes on this, though. So leaving at "needs review" again, but it really does look very, very nice.
My try at winning the nitpick of the month award:
Super-nit: Let's use
EntityTypeInterfaceinstead.Comment #12
amateescu commentedI noticed that nitpick as well when re-reading the interdiff but I was too lazy to create it again :/
I don't think there any changes that could be made here and still keep the patch in-scope. I'll open a new one for the rest of the work :)
Comment #13
amateescu commentedThis is the followup: #2984782: Add an API for converting entity type schemas to (non-)revisionable/(non-)translatable, with or without data
Comment #15
tstoecklerAll right, I guess no one else wants to look at this. Read through the patch again and it again made me very happy ;-)
Let's get this in.
Comment #16
plachI really like where this is going but there are a few choices that I don't understand (sorry if they were discussed above):
Why do we need this? Is there any chance the entity type ID is actually greater than 32?
I'm not sure about this logic: if I'm understanding correctly, this may end up mangling the prefix, which is not what I would expect, if I explicitly bothered to provide one. I think I'd rather be fine with limiting the prefix length to 38 (48 - hash length) and have a table name that may end up being only "prefix_hash" in the worst case scenarios. This would be a generalization of what
TemporaryTableMapping::getTempTableName()is currently doing, so we'd also be BC (FWIW), I think.Why do we still need to the
temporaryproperty on the storage? If I'm readingSqlContentEntityStorageSchemaConverter's code correctly, we are already using custom table mappings for all the schema changes, we only need to inject a temp entity type definition and table mapping to store data in the temp tables, right? I'm looking at this code in particular:I might be wrong, but it seems to me that we can get rid of the temporary property, if we create a
$temp_storageclone and inject the temp table mapping and temp entity type definition. If we do that we can keep the$storageobject untouched and we no longer need to swap table mapping and definition back and forth, which is a pattern we are trying to get rid of.Why do we need a dedicated method for this? Isn't a mapping with the
old_prefix enough? Is it because theold_prefix might have been mangled by the table mapping (see bullet 1)?Don't we need test coverage for this?
Why is this change needed? Is it because previously we used to temporarily update the entity type definition and now we don't need to?
Comment #17
tstoecklerMinor nitpick since this was kicked back anyway:
Now that this has been simplified let's just use
DefaultTableMapping::create()directly instead of storing the class name separately.Comment #18
amateescu commentedThanks for reviewing, @plach!
Re #16:
1. Yes,
\Drupal\KernelTests\Core\Entity\FieldSqlStorageTest::testTableNames()tests various table names for an entity type with a long name using the table mapping class directly, without installing the entity type which would've raised an exception. Also, it was expecting the entity type ID to be cut off at 34 characters instead of 32 (EntityTypeInterface::ID_MAX_LENGTH) for historical reason I think.2. Very good point. This mangling with the prefix turned out to be problematic for #2984782: Add an API for converting entity type schemas to (non-)revisionable/(non-)translatable, with or without data so I implemented your suggestion as a final fallback table name.
3. We still need to set the
temporaryproperty on the storage because various methods fromSqlContentEntityStorageSchemalikeonEntityTypeCreate()andgetEntitySchema()are generating a custom table mapping so they would bypass the temporary table mapping object that we would set on the storage.This was actually the most frustrating thing about the patch which introduced the schema converter class: working from the outside of
SqlContentEntityStorageSchemais very very difficult, but that's something that we are now able to handle much better in #2984782: Add an API for converting entity type schemas to (non-)revisionable/(non-)translatable, with or without data.4. You're right, we no longer need that extra method.
5. Not sure what we would actually test? That class is no longer used in core but we're also not changing anything in it..
6. That's exactly the reason :) The entity type from the temporary storage does not have temporary table names in its definition anymore.
Also fixed #17, nice nitpick ;)
Comment #19
amateescu commentedFound a small problem while working on the followup, there's no need for a foreach within a foreach :)
Comment #20
plachLooks way better now, thanks!
@amateescu, #18:
I think a two lines test method in a unit test instantiating the temp table mapping and asserting the error would be enough. I’m not sure how much flexibility we have around test coverage for deprecations, but the docs don’t seem to imply it’s optional…
@amateescu, #19:
I tried hard not to suggest to store 48 in a variable, but I couldn't help myself ;)
(they are only two, I know)
Unfortunately this logic will produce the same table name for data and revision tables. We need to add also the separator to the mix, which means we have a maximum length for prefixes of 34 chars. We should probably document this somewhere. We'll also need test coverage for this.
The name for this variable is correct, however at first sight it confused me because it has "table" and "mapping" in it. I'm wondering whether the code would be more readable if we just went with something like
$backup_table_names, without stressing too much on the mapping concept. Your call, but I thought it would be good to point this out.Comment #21
amateescu commentedRe #20:
Ok, added a test for the deprecated
TemporaryTableMappingclass.1. If there were three usages, I would have done it :P
2. Very good catch, fixed and added test coverage for
getDedicatedDataTableNameandgetDedicatedRevisionTableName.3. I also thought that it might sound confusing but I didn't care too much at the time :) Changed according to your suggestion.
Comment #22
vijaycs85Looks like all review comments from #20 are addressed. Let's get it in!
Comment #23
plachSaving credits
Comment #25
plachCommitted 76fdeba and pushed to 8.7.x.
Backport to 8.6.x will need to be discussed with release managers.
Comment #26
plachDiscussed this with @catch: we prefer to address this problem space in 8.7.x.
Comment #27
jibrans/8.6.x/8.7.x
Comment #28
jibran