Problem/Motivation
As discovered in #2542748-15: Automatic entity updates can fail when there is existing content, leaving the site's schema in an unpredictable state, SqlContentEntityStorageSchema is not properly detecting if stored column definitions have changed since any field storage using FieldStorageConfig does not have its schema property stored:
public function __sleep() {
// Only serialize necessary properties, excluding those that can be
// recalculated.
$properties = get_object_vars($this);
unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
return array_keys($properties);
This causes the schema property to be recalculated, and thus equal in this check:
if ($storage_definition->getColumns() != $original->getColumns()) {
throw new FieldStorageDefinitionUpdateForbiddenException("The SQL storage cannot change the schema for an existing field with data.");
}
This causes automatic entity schema updates to silently fail, leaving the actual schema out of sync from the codebase.
Proposed resolution
Fallback to check the stored schema.
Remaining tasks
User interface changes
API changes
Data model changes
Comments
Comment #1
jhedstromThis is the fix first posted in #2542748-29: Automatic entity updates can fail when there is existing content, leaving the site's schema in an unpredictable state.
Comment #2
jhedstromComment #3
jhedstromThis is a start at a test. I quickly realized we'll need a new beta12 base-dump, since once there is content in node, the automatic entity updates won't run to the point where we need them to *fail* to prove this bug/fix. There were several schema changes *after* the db dump was added to core, but *before* beta12 was cut.
I used the patch from #2544972: Add options to limit tables, exclude data, or only export data to DbDumpCommand to generate the limited node table dump.
Comment #4
jhedstromActually, this test need not even rely on the update db being loaded. The steps from #2542748: Automatic entity updates can fail when there is existing content, leaving the site's schema in an unpredictable state can be easily produced in a test setup, then the stored schema can be updated manually in the key_value table to be say, an int instead of a float, and then
applyUpdatescalled, and it should throw the exception.Comment #6
plachThanks for opening this, +1 on what I read so far (both code and comments :)
Comment #7
jhedstromGoing to work on that test.
Comment #8
jhedstromI've moved the test to the field module, since this issue is mainly with field storage that is using
FieldStorageConfig. The interdiff is against #1.Comment #10
stefan.r commentedWe have almost this exact same logic in
SqlContentEntityStorageSchema::requiresFieldStorageSchemaChanges(), would it make sense to make the retrieving of the schema into its own method?And wonder if we need to add an
elsecase (for custom storage?) that returns FALSE directly, as in such case$schema[$table]['fields']won't be set and the schema comparison further down will give a notice.When we say "column schema-changes" (here and elsewhere in the patch) it may be a bit unclear what we are talking about, do we have such a thing as a "column schema"? Maybe rather something like "Compares schemas to check for changes in the column definitions"?
Would prepending "Tests that" still make this fit within the 80 cols? :)
2 empty lines at the end of
setUp()Comment #11
alexpottDiscussed with @catch, @xjm, @effulgentsia and @webchick, we all agreed that this is a critical issue has a silent fail and updating of the stored schema to something different than the actual schema could cause serious bugs even if this in not called from uppdate.php.
Comment #12
stefan.r commentedLet me see about adding a patch that addresses comments in #10
Comment #13
stefan.r commentedComment #14
stefan.r commentedactually that isset() shouldn't be needed now that we check for custom storage
Comment #15
webchickTagging.
Comment #16
dawehnerThe patch looks really great for me.
!
Here is a question, while schema is bound to SQL, custom storages might have a similar concept, so I'm wondering whether we need a new issue about allowing custom storage to opt into that somehow.
Nitpick: missing empty line
Comment #17
plachLooks great, thanks!
RTBC +1
nice :)
@dawehner:
As I mentioned during the previous critical meeting call, there is a flaw in the API I was not able to address: schema is not necessarily a SQL-specifc concept, but field types return a Schema API definition in their
::schema()method. Ideally we should be able to derive also that schema from field property definitions. The issue you are bringing up is related and a dedicated issue to discuss that makes totally sense to me.Comment #18
stefan.r commentedAdding that missing empty line.
Comment #19
alexpottCommitted 6bf0202 and pushed to 8.0.x. Thanks!
Comment #21
stefan.r commentedThis may have introduced a PHP7 regression, triggering testbot to confirm.
In another issue we got "Fatal error: Uncaught Error: Cannot access protected property Drupal\user\UserStorageSchema::$storage in /var/www/html/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php:212" on Drupal\ban\Tests\IpAddressBlockingTest
@neclimdul is looking at this
Comment #22
heddnTried to run a site install using a build of PHP7 from today. I can confirm this is an error.
Comment #23
stefan.r commentedneclimdul> i tried mocking a test case and it was fine so... this might be some sort of obtuse interaction of references or something...This looks to be a PHP7 bug, but maybe we can do a workaround ourselves...
Comment #24
heddnOpened #2547715: Cannot access protected property $this->storage on Install inside SqlContentEntityStorageSchema:: for the bug found in #22.