Closed (fixed)
Project:
Drupal core
Version:
8.6.x-dev
Component:
entity system
Priority:
Major
Category:
Bug report
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
22 Sep 2014 at 14:00 UTC
Updated:
21 Jan 2019 at 19:56 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
david_garcia commentedI think this belongs to core:
Is it correct to allow UPDATE statements that change (even if new value is same as previous) PRIMARY KEY values?
Does it make sense?
I guess the database driver could be modified to remove primary key fields from updates, but does not look like a good solution to me.
Actually it is not primary keys that are not updatable in SQL Server, but IDENTITY fields. IDENTITY columns are created for SERIAL fields in Drupal.
IDENTITY column values are immutable.
Comment #2
mradcliffeCool. Added issue summary template and tried to clarify the issue based on your recent comment. Thanks.
Comment #3
plachMoving to the proper queue.
Comment #4
david_garcia commentedI've boosted this to Major. Without this issue solved we will never see D8 running on SQL Server.
I know it's just 10,000 sites (0.1%), but the issue itself makes sense.
Comment #5
david_garcia commentedComment #6
mradcliffeI'm pretty sure there is a test that specifically tests updating a serial field so this is going to fail.
This only fixes it for entities.
Comment #8
david_garcia commentedLooks good and fixes original issue.
Comment #9
mradcliffeI guess there was not a test that updates an entity's identity/serial column. I think this may need a test in system module as well.
Comment #10
berdirI'm not sure if we can write a useful test for this but we can try.
The change is wrong however, there is a method that should be used for this in 8.x: isNew()
Comment #11
david_garcia commentedTested, it's working OK now, D8 starts to be usable on SQL Server with this!
Fixed the ->isNew() issue.
Comment #12
david_garcia commented1) Improved original implementation code readability.
2) With the change, revision management was all broken. The disturbing thing about that is that Drupal Tests were not able to detect a completely broken entity revision management sql storage.
3) Fixed revision management in new patch.
Remaining tasks: investigate why tests were not deteting the issue, and propose tests if needed.
Comment #14
david_garcia commentedwhat a mess...
Comment #15
david_garcia commentedLet's see how this new one behaves with tests.
Comment #17
berdirIf you switch to $entity, then use the id() method instead of accessing the field directly.
This seems a lot more complicated than it has to be? both checks include serial, so something like this would IMHO be much more readable:
Same here, use getRevisionId(). Note that this won't work with isset(), so you will need to do a !== NULL or so instead.
Comment #20
david_garcia commented@bedir
1. Done, makes sense.
2. I also don't like it like that, but I wanted to keep as much comments as possible and looked into Drupal coding standards where they recommend doing that rather than breaking the condition into several lines to keep the comments. Anyways, I am trying something new here in the last patch.
3. Done, makes sense.
My local tests are broken, so I will need to mess up a little with the .org tests.
Comment #21
david_garcia commentedBTW: Is this supposed to be the way to check if a column is serial? isColumnSerial is just checking if this is ID or Revision Id, but what about other serial fields. Shouldn't this check depend on the schema type?
Comment #23
mradcliffeYeah, that's not a really good method name if all it does is that.
Comment #24
david_garcia commentedSo, last patch has many failures, but looking into it that is (at least some of the failurse) because isColumnSerial is failing to properly tell if a column is serial or not. Making the asumption that if a field is the ID or the REVISION ID of the entity it must be serial is not reliable.
I need some help here, I'm totally lost with the new way of defining database Schema's in D8.
I am trying to retrieve storage schema specifications by means of:
in the scope of a SqlContentEntity class. But there is no hint of "serial" field information inside the provided schema.
NID field appears be "int" everywhere but according to Schema API it is "serial". But the field in database is created as "serial" (int with autoincremental) so I guess at some point Drupal is able to tell that the NID field from the NODE entity is serial, I just don't seem to be able to retrieve that information.
This 5 minute patch is transforming into a nightmare, looks like a chain of some broken stuff that was holding together thanks to the loose behaviour of mySQL.
Comment #25
mradcliffeI ran into similar confusion when I investigated mapToStorageRecord previously...
Back in Drupal 7, we had the concept of a field which had its "schema" defined in hook_field_storage or whatever where a field api field could define multiple columns, etc... for its field tables. In Drupal 8, every property on an entity is considered a "field" for consistency. Many fields are 1:1 (nid, vid, uid, etc...) where as complex field types and field api fields have that field/column storage definition.
So in mapToStorageRecord() the code first goes through an entity's fields, and for each field, it goes through its column storage definition. In that first loop, it calls getFieldStorageDefinitions(). I think this has the Schema API type of "serial" defined IIRC whereas when it loops through column storage it has the standard SQL types.
I hope that helps.
Comment #26
david_garcia commentedI deeply inspected getFieldStorageDefinitions(), getTableMapping() and getStorageSchema() none of them is able to tell the NID field of the NODE table is "serial", it is shown everywhere to be "integer".
I am even unable to tell what drupal data type is the NID column in NODE by using drupal_get_complete_schema(), this is just returning a handfull of tables, but not the NODE table schema.
What is the way in D8 to tell the storage data type of an entity's property?
I am starting to think that whoever wrote the isColumnSerial the first time was unable to acquire that information so decided to implement as is now.
Comment #27
david_garcia commentedBad patch, but let's see how it does against tests.
Comment #29
plachThis is not an option, sorry. We tried hard to remove the need to use schema at runtime, we should not add it back here. The basic idea behind the entity storage is that we know how the schema is shaped because it's derived from entity and field definitions. We don't need to go asking the storage schema handler whether a field is serial, we know which ones are. What's wrong with the current code?
Comment #30
plachThis comment is not wrapping at column 80. Also should be "immutable" :)
We usually link only follow-ups in code, not sure why we have another reference a few lines above but no need to add one. Git blame can be used to find the issue if needed.
The
::getEntitySchema()method is protected for a good reason, which is we don't need to expose the schema array. This should be removed altogether.Comment #31
david_garcia commented@plach: Glad to see someone with knowledge of how EntityStorage works jumping in, I am very lost here. Sorry for the coding standards, I was just trying this to overally pass tests as a starting point.
What concerns me now most about this issue is how SqlContentEntityStorage::isColumnSerial works.
Well, this is the ContentEntityStorage (in charge of dumping the entity to database) trying to figure out what the type of a field is in the database.
That means that, as per isColumnSerial current implementation, any Entity's PrimaryKey or EntityRevision Revision Key is assumed to be SERIAL (and cannot be differently) and all other columns ARE NOT SERIAL (and this also cannot be differently).
And if your entity does not work like this, then the ContentEntityStorage that is in charge of dumping the Entity into the database has now way of telling what database type a column is. So basically, all Entities in Drupal should work like that.
Investigating this issue I saw that in D8 schema is inferred from Entity's field definition, in Entity/Node.php:
Then StorageSchemaHandler is forcing to be serial any key that is of type integer when constructing the Schema:
Then why not use this insted (can't even wonder if it works):
I can workaround this issue in the SQL Server database driver (and I guess it will need to be like that because looks like D6 migration is asuming you can UPDATE and/or INSERT values into a serial column, wich you can actualy do in SQL Server but with special techniques), but I don't think it is formally correct to have this sort of statement being issued against the database:
UPDATE xxx SET nid = 1, property0 = :value0 WHERE nid = 1Futhermore, why does SqlContentEntityStorage have a getStorageSchema() and getFieldStorageDefinitions() method that is saying that storage type for the NID is 'int' instead of 'serial'?
Comment #32
mradcliffe@plach wrote:
drupal_write_record() was verbose and a monster, but at least it took care not to make assumptions about writing to the database. The implementation of mapToStorageRecord is very naive and makes assumptions that cannot be made for writing to a SQL database. It is absolutely necessary that code that sets up writes to a database knows how to write to a database i.e. knows what it should or should not do. The class name is SqlContentEntityStorage and thus it should behave how SQL expects it to.
@david_garcia wrote:
I agree with @david_garcia that it should be possible to add other sequence fields, either base field definitions or complex field definitions, for an arbitrary content entity.
@david_garcia wrote:
However, I think that content entities need to be consistent. It is safe to assume that a content entity's primary key and revision key should behave consistently with all other content entity implementations and be identity columns. The current patch needs work - whatever mapToStorageRecord calls needs to distinguish which serial field is an identity type for a particular table. On the revision table, it's the revision field, on the entity table it's the id field.
Perhaps isColumnSerial() is important, but isFieldSerial() is also important for the logic of whether to treat Id or Revision fields as serial for whichever operation.
@david_garcia wrote:
Migrate worries me too for pgsql driver.
Comment #33
david_garcia commentedMakes sense to have a consistent entity design all accross drupal regarding keys and serial columns, documentation should be extremely clear about this:
- Serial fields do not exist, you can only have a serial column per entity and that is it's key and it must always be set to 'integer'.
- If a key in an entity is an 'integer', then it is automatically assumed to be serial. Other types are allowed though... That means that you cannot have an entity key to be integer and not auto incremental, but you can have an entity key being a string?
I am still not fond of isColumnSerial implementation, if these asumptions are all made inside StorageSchemaHandler then why not ask that class what has it decided to do with keys and serials. Complete agree with this:
@mradcliffe wrote:
Comment #35
david_garcia commentedFound the annoying bug in the patch, let's retest.
Comment #37
david_garcia commentedComment #39
david_garcia commentedFailing tests are amazingly passing in my local environment with latest patch (SQL Server though...), try to retest.
Comment #42
plach@david_garcia:
Yep, this is how the default SQL entity storage is supposed to work. If you have an entity type that needs to behave differently you just need to provide an alternative storage class.
Because
'serial'is a Schema API type, while field definitions are using Type Data API types, for which serial is meaningless: it just cares about the native type.I'm not saying it's correct, I totally agree with the goal of avoiding this kind of update statements. I'm just saying we can rely on a known schema to deal with them.
The storage schema needs to be instantiated only in rare conditions, that is when dealing with schema updates, that are forwarded from the storage class to the storage schema class because the latter is not part of the "public API", in fact not every entity storage would require schema handling (the Mongo Storage will not, probably).
@mradcliffe:
I can only repeat that we are making no assumptions, we know how the schema is shaped. If the current implementation of
mapToStorageRecordis incorrect or does not take into account all possible SQL flavors I'm all for fixing it, I'm just saying we don't need to retrieve the schema, because it's generated, hence it's well known.AFAIK only one serial field is allowed for each table on most RDBMS, anyway, as I said above, if you need a different behavior you can just code your own classes. The current approach has worked well for all core entity types and lots of contrib ones so far.
@david_garcia:
Nope :) That just means that most entity types behave that way. For instance, the User storage class overrides
isColumnSerial()to use a non-serial integer id column.From an architectural POV you are totally right: it would be better if this kind of logic were encapsulated in the storage schema class. Here we decided to go this way to avoid the need of instantiating the storage schema class on every request. This is a bit like cheating but given how much coupled the storage and storage schema classes are, it should not be a problem in practice. This is not set in stone however, we are still evaluating these implementation details: a possible alternative could be moving this knowledge into the table mapping class and make the storage and storage schema rely on it. However this is a different issue :)
Comment #43
david_garcia commentedOk... this issue was great learning on to how EntityStorage works in D8 :)
A briefing of things to do:
- First we need to fix issuing update statements that have identity updates, while preserving al other behaviour. That's what my last patch tries to achieve, but fails some tests. I cannot replicate locally, they are all passing. I even moved from SQL Server to MySQL to run the tests, and still passing locally.
- Figure out why although patch in #6 passed tests, entity updates were left completely broken. Will probably require some new tests to be writen.
- Open a new Issue to move forward on this discussion:
Comment #44
plachComment #45
jhedstromPatch is no longer applying. This is tagged as needing tests, but it seems like a difficult thing to test for.
Comment #46
deepakaryan1988Re-rolled!
Comment #48
nitesh sethia commentedComment #49
nitesh sethia commentedRerolled the patch.
Comment #50
nitesh sethia commentedChanging the status to In review.
Comment #52
david_garcia commentedIt would probably be a good idea to find out where are the 6 test failures comming from in #37 before introducing the extra code in #50.
Comment #53
madhavvyas commentedPatch re-rolled for #39
Comment #54
madhavvyas commentedComment #57
sharique commentedWhat this $record is? It is giving syntax error.
Comment #58
madhavvyas commentedRemoved $record variable as suggested. It was not used in query.
Comment #59
madhavvyas commentedComment #62
audriusb commentedit is still a problem in v8.1.2
Comment #64
ruloweb commentedFix a few brackets in patch #53 (which is based on #37). Added also feedback in #57.
This currently works for Drupal 8.2.3.
I haven't addressed the failing tests.
Comment #65
ruloweb commentedLet's test it, my local MSSQL is quiet slow.
Comment #69
beakerboyIs there a stutus update on this issue? Anything I can do to test it?
Comment #70
beakerboyIs there a simple change I can make to allow users to update values of custom entities? I could either modify core to prevent " SET id={id}" from appearing in entity update SQL, or add something to my custom module to rewrite the SQL statement before it executes. I attempted to use some of the simpler patches on my Drupal 8.4 site, but it caused my site to stop responding.
Comment #71
beakerboyAs an update. I fixed this with the following small change to SqlContentEntityStorage.php
Comment #72
beakerboyPatch File for my change.
Comment #73
beakerboySecond Try. Removing DOS line endings.
Comment #74
beakerboyAdding Missing Newline
Comment #75
beakerboyComment #76
beakerboyComment #77
beakerboyLast try before I give up.
Comment #78
beakerboyComment #79
beakerboyComment #80
beakerboyPlease review and let me know if there is anything I need to do to get this into core.
Comment #81
david_garcia commented@Beakerboy Sorry but the last patch is a dirty hack that does not fit the architecture. Please see the work in all the previous posts. At some point this got close to being solved, but needed test coverage. Thanks!
Comment #82
beakerboy@david_garcia I would say it’s a very clean hack...three lines and breaks absolutely nothing. Everything else has been in the works for years, and continues to fail unit tests. Let me know where you feel the changes should be made and I’ll look into it. I like simple solutions over over-engineered projects. This code efficiently removes the problem, exclusively where it is an issue.
I’ve never used SimpleTest to do unit tests. How would you propose testing this? The way I would approach it would be to find where an entity update is tested, and examine the $query object to ensure that “id” is not in the field array, or to pass $query->__toString() through a regular expression to ensure it does not match “SET.*id=.*WHERE”.
Comment #83
beakerboyHere is a version that checks the id status of each field, and only removes it if it is the base table's idKey
Comment #84
beakerboyMissing Line break
Comment #85
beakerboyThis version handles the revision table as well.
Comment #86
beakerboyThe previous version was using an incorrect $record_array. If this works, then the alias storage will need to be updated.
Comment #87
beakerboyThis fixes the issue with the AliasStorage object
Comment #88
beakerboyThe pid element in the $fields array is used elsewhere. Putting it back in, but after the update query is constructed.
Comment #89
beakerboyHere is an 8.5 patch
Comment #90
beakerboyTypo in the previous patch. Please offer suggestion on the best way to test this. I'm more than happy to write a test or two. If you feel there is a better way to accomplish this that does not require refactoring several objects, please offer constructive advice. I would love it if someone with a sql server test install could test this patch. The only thing I think I might want to change is to rename $record_array to $fields, for consistency with other calls to the update method.
Comment #91
beakerboyComment #92
beakerboyIs there anyone who could test this patch on a sql server installation? Can anyone recommend a way to unit test this? I have asked on both StackExchange and the drupal slack channel with no responses.
I found a unit test in /core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php which specifically tests that primary keys CAN be updated. This test will need to be removed before SQL Server will pass testing.
Comment #93
beakerboyNobody on the Slack channel, stackExchange, the IRC channel, or the support forums here have been able to offer any advice on how to test this fix.
Comment #94
rakshith.thotada commentedHello @beakerboy Thanks for the patch.
We have our production running on Windows server and when we used SqlSrv module - we faced this issue.
I will mark this as RTBC. Thanks for this Contribution.
Comment #95
rakshith.thotada commentedComment #96
berdirI think that all existing tests continue to work is enough test coverage, agreed that this would be very hard to test without very low-level unit test that would test the actual arguments to the query builder methods.
I also added test runs for PostgreSQL/SQlite.
Comment #97
catchThis goes over 80 characters. We should also explicitly mention that updating the serial fails on SQL server here I think.
Comment #98
beakerboyThe comment has been changed.
Comment #99
mradcliffeI made an interdiff of patch 89 and 90 manually via
interdiff not-update-entity-serial-columns-2342699-90.patch not-update-entity-serial-columns-2342699-98.patch, and attached it.I didn't have time to review the additional changes in #90.
Comment #100
beakerboyComment #101
beakerboyComment #103
beakerboyThe only change since #90 is in comments. #90 passed tests, but the test server is out of hard drive space, which is preventing #98 from running.
Comment #105
beakerboyTest server is back online. PHP 5.6 & MySQL 5.5 passed, others are queued.
Comment #106
rakshith.thotada commentedHello @Beakerboy,
I used your Patch - https://www.drupal.org/files/issues/not-update-entity-serial-columns-234....
Everything Worked fine. But when we use Paragraphs, We faced an issue with creation/updation of node.
The error was - Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Cannot update identity column 'revision_id'.: UPDATE paragraphs_item_revision.
So I had to create the new patch and it will fix the issue here. Please review once.
https://www.drupal.org/files/issues/drupal-paragraphs-revision-2342699-1...
Comment #107
plachI'll have a look ASAP.
Comment #108
plachThe general direction of this patch is ok IMO, but I think this could be fixed in a single place by adjusting the logic in
::mapToStorageRecord(). We are already dealing with SQL server towards the bottom of the loop, so that seems to be the right place to address this issue. We'd just need to avoid setting the value in$recordin the first place, regardless of$valuebeing set, when we are going to perform an update query and we have a serial (identity) column. We can easily check this via::isNew()or::isNewRevision()depending on the table being the base table or the revision table.Comment #109
rakshith.thotada commented@plach
Thanks for reviewing it. We would still need those keys in $record array, as we use those Identity column/Primary key as condition in Update queries.
Let me know your thoughts on this.
Comment #110
plachWe have
$entity: we can populate conditions via$entity->id()and$entity->getRevisionId().Comment #111
beakerboyI've changed the logic in mapToStorageRecord as suggested. Let me know if this looks better. It seems to work on my local install.
Comment #112
plachComment #113
beakerboylast patch was missing the changes outside mapToStoarageRecord()
Comment #114
beakerboywrong drupal repo on previous. This one is correct.
Comment #115
beakerboyLast patch was missing calls to getValue(). This patch seems to fix updates on custom entities, but node updates fail.
This patch probably needs an edit for readability:
/r/->getValue()[0]['values']/->value/gComment #117
plachDidn't look at the patch, but
$entity->get($name)->valueshould be enough to retrieve the field value.Comment #118
beakerboyWhich is preferable,
$entity->{$this->idKey}->valueor$entity->get($this->idKey)->value?Comment #119
plachThey are the same, I personally prefer to use
::get()when the field name is stored in a variable, but that's just me :)Comment #120
berdir> They are the same, I personally prefer to use ::get() when the field name is stored in a variable, but that's just me :)
That's me too, +1.
Comment #121
MixologicCan somebody help me figure out why all the patches in #111,#113-#115 are generating so much output as to completely fill the testbot disks? We didn't used to have this problem, and this is the third time this month that some bad patches create *mountains* of output, I suspect its not just an accident here, but somehow we've ratcheted up some logging or error handling that is compounding the issue.
Its causing the disks to fill up to the point that the testbot dispatcher cannot tell if the disks are full, and pull the testbot out of rotation. Which causes it to keep running whatever is in the queue, thus, killing testing for everything thats waiting for a test.
Please be careful when resubmitting any of the patches in #111-115 - Please run it locally first, at least to prove that the error common in all of those result sets is not going to replicate twenty thousand times.
And if anybody has any ideas about what may have recently changed in core to create all this extra output, I would certainly appreciate a pointer.
Thanks.
Comment #122
beakerboyHere is a 8.4 patch that works on my system for custom entities, nodes, and node revisions. However, it includes a slight API change. The format is now starting to look like what was being done at around patch #12...which is where nobody was ever able to resolve all the testing failures, and prompting me to take a different approach. Maybe it has to do with
entity->get($this->idKey)->valuenot matching up with$record->{$this->idkey}, such that replacing the later with the former causes failures?Comment #123
beakerboyI moved the call to fetch the $record inside each of the "update" and "insert" blocks since the necessary parameters will be different.
Comment #124
beakerboyPlease review. I can simplify this patch by using the existing
$updatevariable withindoSaveFieldItems()and callingmapToStorageRecord()once like it was originally. Please review and I'll roll the simplification out tomorrow. I don't see a way to accomplish this without changing the API like I did.mapToStorageRecord()would have to redo all the tests that the calling functions do to determine if the $query will be an update or insert.Comment #125
tacituseu commented@Mixologic: posted a reply in old testing issue so as not to clutter this one.
Comment #126
plachLooking good, thanks!
I still have to review the code more in depth, but this looks promising. A simplification would be welcome of course :)
Trailing whitespace. Also, let's use
TRUEplease.Comment #127
beakerboyHere is a slightly simplified version. The API change is still in it, but instead of hard-coding a few
TRUEs in the function call, I set and passed in an$updatevariable.Comment #128
berdirusually update refers to updating an existing entity. But for the revision case, saving as a new revision is also an update in how we use it in general in the entity system.
If we need this argument then I would recommend a more explicit $include_serial_fields (or something like that) argument.
The alternative would be a new wrapper method like mapToStorageRecordWithoutSerialFields() but adding a new optional argument to a protected method should be fine IMHO.
if you're working with the entity then you can just use $entity->getRevisionId() ?
Comment #129
beakerboy@Berdir
1. The
$updateparameter was already defined in this class as whether or not the SQL statement would be an update statement or not. I just maintained this. I would rather change the name than make a wrapper for simplicity.2. I can change that, no problem.
Comment #130
berdir1. Yes, it existed in one of two methods :) But that's kind of my point, in the doSaveFieldItems(), $update is correct, it's either a new new entity or not. But in the revision context, the meaning of $update isn't clear, so lets use a more specific variable name for the helper function that explicit about what it does.
Comment #131
beakerboyIncluding suggestions from @Berdir
Comment #133
beakerboyWith the end of Drupal 8.4 development...please test this 8.5 patch so we can keep this moving. Thanks!
Comment #134
rbrenton commentedThis is still broken in the main branches. The patch #133 referenced works for us on SQL Server 2016.
Comment #135
rbrenton commentedThis needs merged into at least 8.4.x-dev, 8.5.x-dev and 8.6.x-dev. I haven't checked earlier than 8.4.
Comment #136
alexpottI'm not sure we're fixing this at the right level.
I'm wondering why
Was not attempted. The thing is contrib (or custom) will do things like what you see in AliasStorage. For me that makes this a db driver problem and not something that should be fixed in the entity storage level.
Comment #137
alexpottIf we do decide to go for #133 then I think we should add a test for the new logic in
mapToStorageRecord()so can be sure not to break this in the future.Comment #138
rbrenton commentedResponding to #136.
IMHO, #133 fixes it in the most correct place. If fixed at the driver level and later there is a need to update a primary key, the driver would be incorrect and we would have to return to #133 and patch it this way anyway. I think it would be ideal if the core did not send unnecessary fields to update to the driver. Also, there's still the fix in the patch for what looks like a line ordering error of $fields['pid'] = $pid in core/lib/Drupal/Core/Path/AliasStorage.php?
Comment #139
alexpott@rbrenton the problem is wider than the entity system though. It's anything that updates a record in the database. Most contrib and custom modules are used with MySQL / Postgres and DrupalCI also offers SQLite coverage but we have no automated testing for a DB that behaves this way so if this isn't fixed in the DB layer there's a good chance something will be broken somewhere.
What I'm trying to say is that the restriction on not updating an identity column comes solely from mssql and the entity sql storage implementation will not be the only place this causes problems.
Comment #140
alexpottI've discussed this a bit with @amateescu in slack. He's pointed out that fixing this in the db driver level is going to be very expensive - mssql driver will have to get the schema for every table during an update and work stuff out.
I have two concerns:
mapToStorageRecord()is mutable depending on whether you are inserting or updating - that just feels wrong to me.So here's an even less testable version of #133 but the difference is that it doesn't change
mapToStorageRecord(). The changes in that code concern me because it make the result mutable depending on whether you are updating or inserting whereas I think conceptually an entity with identical values should map to the same storage record regardless of whether you are inserting or updating. Therefore I think we should fix it closer to the queries by doing something like the patch attached.Still no sure how to test this. Posting to see what people think and maybe someone can test on mssql.
Comment #141
alexpottSo @amateescu pointed out the that @plach asked for the changes to mapToStorageRecord in #108. As stated in #140 I'm not sure this is the right way to go because I don't think it makes sense to have a mutable storage record for different operations. It feels like the wrong axis to change that on.
Comment #142
beakerboy@alexpott...any updates in your thoughts on this patch, or this issue in general?
Comment #144
plachI discussed #141 with @alexpott: I'm not completely sold on that argument, but I'm fine with going that way. I'd rather see us making progress on this issue, given that the differences between the two approaches aren't likely to be that relevant in practice.
Regarding test coverage, we both agreed that the proper way to test these changes, would be to run our test suite on SQL server, so no need to add explicit coverage for that. OTOH while discussing possible alternatives to #140, I realized that we are missing an explicit test to cover entity ID assignment on creation, although we likely have implicit coverage for that via migration tests. I'm offering the attached patch as a complement to #140. Feel free to merge them, otherwise I'll open a separate issue for this.
If we don't end up merging the two patches, I'm +1 on RTBC-ing/committing #140.
Comment #145
beakerboy@platch
Thanks for the update. I will test #140 on mssql shortly. Thinking ahead, do we need to be on the lookout for cases where a user creates an entity with additional IDENTITY fields beyond just the id column? Can a user create and entity with multiple IDENTITY fields? #140 appears to just remove "id", while #133 checks each field with isColumnSerial() and removes it if it is.
Comment #146
plachI don't think entities were ever intended to have more than one ID field. I cannot completely rule out the possibility that a heavily customized entity type could try to implement two ID fields, but I'm very skeptical it would be possible to do that.
Comment #147
mradcliffeDBTNG does allow to set
serialtype to multiple columns, but I don't think it's used in core.I think it's theoretically possible for an entity to define multiple base field definitions that may have the serial type, but I don't think there's a test for it.
Comment #148
alexpott@plach I think it is great to add an explicit test here. And whilst it does not 100% confirm the fix it does prove no regressions so adding it here makes sense. Wrt to multiple serial columns - does anyone have a use-case? We could open a follow-up to deprecate supporting that because it feels that doing multiple serial columns does not make a great deal of sense.
Patch attached merges #140 and #144.
Comment #149
plachI don't think we need to worry about advanced use cases implying multiple serial columns: this is not how core entities work. An highly specialized entity type requiring multiple serial columns could extend the storage to deal with its use case.
I'm in favor of RTBC-ing #148 as soon as @Beakerboy (or someone else) confirms the patch is good for MSSQL.
Comment #150
mradcliffeI agree with that. The patch makes sense and is documented.
Comment #151
beakerboyThe latest patch resolves the issue on my Drupal site. Thanks everyone for all the thought and work over the past 4 years to get this changed!
Comment #152
beakerboyComment #153
catchCommitted/pushed to 8.7.x and cherry-picked to 8.6.x, thanks!
Comment #157
jibranFYI https://gist.github.com/chx/eb71941faae245bbf7d0f20747551c21
Comment #158
tuutti commentedSome of our custom entities broke on core update (8.6.2 -> 8.6.7) and I tracked the issue to here:
This seems to assume that ID is stored in a property called
valueand breaks the update statements when using a field where that is not the case.Comment #159
beakerboyI’d suggest opening a new bug report. This one is already closed and the patch has been rolled out, so I don’t know if it would be re-opened. If you open a new report you can reference this issue, submit this patch, and run core tests against it. That would probably get more core developers attention since it’s a new report versus a comment on a closed one. Add as much detail as you can to your report, like how your custom entity is designed. It’s possible that there is some requirement that all entities have a ‘value’ property.
It took a year of prodding to get this issue fixed, so it’s possible yours may take some time as well.