-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[WIP] Connection: Upsert #2939
[WIP] Connection: Upsert #2939
Conversation
It's not really an |
@morozov Is it really that easy? I was under the impression there were some insurmountable issues with that, given that it's been high on the wishlist for years now |
@jnvsor you can try and see if it is. There indeed are some serious issues. You can try approaching it and seeing where you end up. The last time I tried it, I ended up with |
public function upsert($tableExpression, array $data, array $identifier, array $types = array()) | ||
{ | ||
$qb = $this->createQueryBuilder() | ||
->select($this->getDatabasePlatform()->getCountExpression('*')) |
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.
Getting COUNT(*)
with arbitrary WHERE
may be expensive depending on the table size and filtering conditions. We don't need the count, we only need if there's a single row matching the criteria. Maybe, WHERE EXISTS (SELECT *)
would be better than SELECT COUNT(*)
.
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.
You would need the count if you wanted to prevent upsert
accidentally updating multiple rows here
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's a slippery slope. If the UPSERT
query is not specific enough so that it can match more than one record, then it's invalid for this use case, no matter if zero, one or more matching records exist.
Could you give an example of a table and/or query where INSERT
would succeed but UPDATE
would affect multiple rows?
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.
That's also a good point
@morozov Is there a code-style rule that says AbstractDriver can't return multiple values? If it could return both the SQL and the values to bind to it that would solve the problem, but it would be a first so I'm guessing that's not gonna fly. |
Isn't it what |
That... Is actually a really good point. So we'd pass a querybuilder and an array of fields into the driver and the driver would return a new querybuilder with the query completed? (Or should we just alter the qb passed in in the first place?) |
@jnvsor it doesn't matter that much at this point. You can make whatever properly working and tested PoC first and then we can polish the details. |
@morozov Actually querybuilder won't work well either. The reason the drivers all return strings is because they tend to be non-standard. We'd have to shove the entire |
Would require a more flexible querybuilder first. I've added an issue for that: #2972 |
Since @Ocramius has said we can't make the querybuilder more flexible, I don't see any way upsert can be implemented in DBAL. Closing. |
@jnvsor a |
@Ocramius Is there any way to get the fields from the constraint in question from a |
If we work on upsert, then it should most likely not be implemented by
trial and fail. Each platform has a different syntax for it, so that should
really be the last resort, with a fetch+update/insert rather than exception
catching for systems that are incapable of dealing with the operation
…On 9 Jan 2018 19:00, "Jonathan Vollebregt" ***@***.***> wrote:
@Ocramius <https://github.com/ocramius> Is there any way to get the
fields from the constraint in question from a
UniqueConstraintViolationException? What if the upsert is violating
multiple unique constraints?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2939 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAJakBkECg48rUVxYTN4OukAw-krAPDBks5tI6lYgaJpZM4RDx3P>
.
|
@Ocramius We'd need to fetch all of the unique constraints one by one before insert/updating. Worst case scenario we'd have multiple structure lookups followed by multiple fetches before even deciding whether to insert or update |
Yes, that's the downside of emulation 😜
…On 9 Jan 2018 19:07, "Jonathan Vollebregt" ***@***.***> wrote:
@Ocramius <https://github.com/ocramius> We'd need to fetch all of the
unique constraints one by one before insert/updating. Worst case scenario
we'd have multiple structure lookups followed by multiple fetches before
even deciding whether to insert or update
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2939 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAJakPveggrthcn4CgEyYEaopwaeCSXCks5tI6rYgaJpZM4RDx3P>
.
|
Given that that could be better performed when structure information is cached, perhaps we should bounce this back to orm and leave it out of DBAL? |
I don't follow: what is cached?
…On 9 Jan 2018 19:11, "Jonathan Vollebregt" ***@***.***> wrote:
Given that that could be better performed when structure information is
cached, perhaps we should bounce this back to orm and leave it out of DBAL?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2939 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAJakNOzMxlHDdobHS9EpxNdlhNosdRDks5tI6vUgaJpZM4RDx3P>
.
|
Sorry, I presumed the orm layer cached structure information and probably unique constraints too |
The ORM does that, but DBAL should still provide an API for performing the operation. The problem is that it cannot be done via query builder, but only as atomic transactional operation to deal with emulation. |
@morozov I don't feel the kind of complex emulation discussed above is worth implementing, since it's very complex and very slow. My original idea was simpler, but rejected since it's not a true upsert. I don't intend to work on this, and I'm fine with leaving this as a wontfix personally. Perhaps someone will redesign the querybuilder/drivers to allow upserts some day. Maybe this should be moved to a more general 'Upsert' issue? |
I don't believe it's going to happen any time soon because of the complexity of the issue and absence of real demand. In my experience, upserts are mostly used when a DB table is used as a key-value storage which is usually way slower than other alternatives. |
@morozov this issue is linked to a similar issue about supporting custom entity persisters. If we can't fix this issue, can we at least look at making the entity persistence strategy configurable? It seems like we should be able to inject this somewhere in the The specific use case I'm asking about is for MS SQL Server where one of my tables has a trigger that inserts into another another table. For this entity I want to use |
This would be a question to ask in doctrine/orm. |
This proposes a method that would allow consistent cross-platform upserts, with the downside that you have to explicitly provide the fields to check for existence of the row (It doesn't automatically handle unique constraint violations)