Skip to content
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

BUG Fix writeBaseRecord with unique indexes #8831

Merged

Conversation

tractorcow
Copy link
Contributor

Fixes #6819

I've had this bug in mind for years. This will fix the below bug once and for all:

Couldn't run query:

INSERT INTO "App_MemberRole"
 (\"Created\")
 VALUES
 (?)

Duplicate entry '0-0' for key 'OneMemberPerProperty'

This often came up when writing ChangesetItem due to the primary key being a composite on a couple of columns.

@tractorcow
Copy link
Contributor Author

Interestingly this bug has been around as long as SilverStripe has.

https://github.com/silverstripe/silverstripe-framework/blob/2.1/core/model/DataObject.php#L432-L436

@tractorcow tractorcow force-pushed the pulls/4.2/fix-write-base-record branch from be7b229 to d1396b7 Compare February 27, 2019 03:40
@tractorcow
Copy link
Contributor Author

tractorcow commented Feb 27, 2019

Oops, prepareManipulationTable assumed ID was set. A small tweak fixed that. :)

Copy link
Member

@kinglozzer kinglozzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 👍

@dhensby dhensby merged commit 3629875 into silverstripe:4.2 Feb 28, 2019
@dhensby
Copy link
Contributor

dhensby commented Feb 28, 2019

Nice one @tractorcow! 🎉

@tractorcow tractorcow deleted the pulls/4.2/fix-write-base-record branch March 1, 2019 01:54
@patricknelson
Copy link
Contributor

I'm guessing in the example above App_MemberRole is actually a descendant of another DataObject correct? If so, then the reason why this fix works (i.e. checking $this->isInDB() === true first) is because you're actually writing a new record in a child object's table, so the child object (who's ID must be synchronized with the parent object's table) doesn't actually exist in the DB, which is why you're assigning the ID. As the code is written, it looks backwards, but it works because the ->ID is indeed assigned because the base record has in fact already been written and (naturally) an artifact of that is the the ID will not be zero and thus no duplicate errors.

It's more of a point on succinct semantics and reducing tech debt/legacy. Maybe a comment can be introduced here, like:

        // Ensure we have an ID first and then synchronize ID's across child object records.
        if ($this->isInDB()) {
            $manipulation[$table]['id'] = $this->record['ID'];
        }

Or similar. I've proposed an explanation of an API level disambiguation on that nuance in #8411 (comment)

@tractorcow
Copy link
Contributor Author

I would write something like // Provide ID if generated for this record, otherwise leave blank to trigger a new auto-increment value.

@robbieaverill
Copy link
Contributor

@tractorcow the kitchen sink found some bugs that this introduced, I've made a tiny patch at #8839 to fix that

@robbieaverill
Copy link
Contributor

@tractorcow ignore me, actually some dodgy logic in the auditor module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants