-
Notifications
You must be signed in to change notification settings - Fork 348
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] Upserts #595
[WIP] Upserts #595
Conversation
"upsert" in { | ||
val e = TestEntity("", 1, 1L, Some(1)) | ||
val q = quote { | ||
query[TestEntity].upsert(lift(e)).conflict(_.i).conflictUpdate(_.i -> lift(2), _.l -> lift(1L), _.s -> lift("Test String")) |
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 doesn't seem that conflict
can be used without conflictUpdate
, I think we could simplify things and avoid misuse:
query[TestEntity].insert(lift(e)).onConflict(_.i)(_.i -> lift(2), _.l -> lift(1L), _.s -> lift("Test String"))
The AST could also be simplified to a single new type. Wdyt?
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.
Sure that's a great idea, I'd must rather have one type, than the 2 I had, but my familiarity with the library led me to do what I knew how to do at the time. I can definitely trim that down back to the single type. Lemme try that
I updated some of this with new commits, // Update a conflict
val q = quote {
query[TestEntity].insert(lift(e)).onConflict(_.i)(_.i -> lift(1), _.l -> lift(2L))
}
// Do nothing...
val q2 = quote {
query[TestEntity].insert(lift(e)).onConflict(_.i)().returning(_.i)
} INSERT INTO TestEntity (s,l,o) VALUES (?, ?, ?) ON CONFLICT(x1.i) UPDATE SET x2.i = ?, x3.l = ?
INSERT INTO TestEntity (s,l,o) VALUES (?, ?, ?) ON CONFLICT(x4.i) DO NOTHING is how the queries look. When the cases are implemented in the top level SQL idiom they don't have the x1, x2, x3 so on things added to them, but when they are implemented in their proper dialect trait, in this case PostgreSQL, they add them. @fwbrasil can you explain why this is and how I can stop it? |
|
@schmitch I don't think it really matters, you can do either way of writing a full update and binding to it or you can write it with what you had laid out, they'll do mostly the same thing I believe |
@fwbrasil poke poke, could you help me with some of the issues I described above? |
Actually they are quite different when it comes to resource usage. a prepared statement is not free every variable costs space in big queries that actually can matter. |
Well my point was you are reusing the same variables from before, which wouldn't add any cost, right? Or maybe I'm not understanding it correctly. |
unfortunatly you don't. if you have two
of course postgresql see's that as two variables the same happens in |
@hntd187 do you have plans to work on this feature? It'd be great if we could support upserts! |
@fwbrasil hey! I'm sorry, I've just been swamped and haven't gotten a chance to tie this up, but I promise I'll finish it. I just need to rebase and tie up a few things, but this is basically at the finish line I just gotta push it across, sorry for the delay. |
@hntd187 no worries at all :) let us know if there's something we can help with! |
Hey. Could you please give me a brief info into what's missing? I'd love to help out. I find this feature super useful. |
Okay, I rebased master back onto my branch, but clearly it's been awhile, I still have some work to do here, but this clearly is all kinds of screwed up now. Leave it or open a new pull @fwbrasil ? |
@hntd187 You did not rebase, but merged master into your branch. You should reset your branch to latest commit you made with |
@mxl not the same as |
@hntd187 No, you do |
Well TIL thanks, I'll fix this up in a bit. |
… on update. There is some compatibility issues with PSQL vs MySQL, which makes some of the code look a bit off. I'd like to fix this, but it'll require probably some more DSL additions.
@mxl well would ya look at that, cool beans. This still needs some work, but I've sorted out at least that part of it so far. The MySQL and Postgres dialects are different enough that the syntax I have for them seems good for PSql, but horrible for MySQL, I'm not sure if I should refactor it again to be somehow agnostic to them both or if there is a way to implement a dialect specific version of each? |
@hntd187 I mean what about both approaches? Both dialects could have a And I am also for having a some implicit/explicit update all functionality which would work something like ExamplesPgSQL query[TestEntity].insert(lift(e)).onConflict(_.a).updateAll()
query[TestEntity].insert(lift(e)).onConflict(_.a).updateSet(_.a -> lift(e.a), _.b -> lift(e.b)) INSERT INTO TestEntity (a, b) VALUES (?, ?) ON CONFLICT(a) DO UPDATE SET a = EXCLUDED.b, b = EXCLUDED.b
INSERT INTO TestEntity (a, b) VALUES (?, ?) ON CONFLICT(a) DO UPDATE SET a = ?, b = ?
-- or the actual values, I am not sure how does quill handle the right-hand side params now MySQL query[TestEntity].insert(lift(e)).onConflict().updateAll() INSERT INTO TestEntity (a, b) VALUES (?, ?) ON DUPLICATE KEY UPDATE a = VALUES(a), b = VALUES(b); In a case of Postgres it can be simplified by removing all What do you think? I find it intuitive. |
Well I had a free afternoon so I checked that out and it seems to me that this would require some changes.
I kind of fail to change the signature of case class Conflict(query: Ast, property: List[Ast]) extends Action
// or
case class Conflict(query: Ast, property: List[Property]) extends Action because in If you agree with what I described above could you please changing the def conflict[R](f: E => R): ActionConflict[E, R] = NonQuotedException() to def conflict[R](f: (E => R)*): ActionConflict[E, R] = NonQuotedException() and dealing with the problem I described above? Also an idea. We can
|
@letalvoj Thanks for looking into it. I think it'd be nicer to have a single |
Again just an idea but the problem which you've described has a solution. What if there were those two traits
This would allow to have a builder-like structure for some more complicated and yet it would not fail during runtime. One problem I can think of is that the error would be something like Anyway I can not proceed until someone implements the vararg parser for |
any progress ? |
/* | ||
INSERT INTO TestEntity (s, l, o) VALUES (s, l, o) ON CONFLICT(i) DO UPDATE SET i = ?, l = ?, s = ? | ||
INSERT INTO TestEntity (s, l, o) VALUES (?, ?, ?) ON CONFLICT(i) DO NOTHING | ||
INSERT INTO TestEntity (s, l) VALUES ('Hi', 10) ON CONFLICT(i) DO UPDATE SET s = 'Yoyo' |
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.
maybe Hihi
instead of Yoyo
?
@hntd187 Hello, how things are going with that? Do you mind finishing that or you need some help? :) |
This is in reference to #16
Problem
This ideally should bring atomic upsert support to PostgreSQL 9.5+ and MySQL via the following queries
PgSQL
MySQL
Solution
I have added to the DSL an interface that I think encompasses most of the upsert syntax here. You can see in the example queries there are 3 important parts, the insert, the conflict key and then what to do about it.
Thus I have modeled the DSL to look like this
There are some problems with it right now as I've written it, but I think this is mostly consistent with how it should be, but you guys are in charge so don't let me tell you how to model your DSL :-)
The checklist in #16 was rather out of date with the current library it seemed, so I had to improvise a bit in some places. I'm not sure if the normalization is necessary at all to be honest. There is also some other bugs I've run across I'd like some insight on how to sort out.
Notes
Additional notes.
Checklist
INSERT INTO TestEntity (s,l,o) VALUES (?, ?, ?) ON CONFLICT(i) DO NOTHING
@getquill/maintainers