-
Notifications
You must be signed in to change notification settings - Fork 312
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
Allow data-modifying CTEs in Ecto #532
Conversation
This looks good to me. We need to write tests in test/ecto/adapters and also implement the same for other adapters. |
Thank you @josevalim for your prompt interest in this! ❤️ This is as far as I got with my attempt at writing a test for the above:
And I get this error
|
It looks like the cte query didn't go through the planner so the types are not being resolved. Similar to what you do with the main query you could pipe the cte query into |
Actually sorry I don't think ^ makes sense the planner on the main query should be enough. Maybe there is an issue with the planner not handling |
Yeah this appears to be it. When the cte is planned it only looks at the https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/query/planner.ex#L924 {planned_query, _params, _key} = cte_query |> attach_prefix(query) |> plan(:all, adapter) https://github.com/elixir-ecto/ecto/blob/master/lib/ecto/query/planner.ex#L1784 @all_exprs [with_cte: :with_ctes, distinct: :distinct, select: :select, from: :from, join: :joins,
where: :wheres, group_by: :group_bys, having: :havings, windows: :windows,
combination: :combinations, order_by: :order_bys, limit: :limit, offset: :offset] Maybe we need a special CTE operation that is basically ^ plus |
I've just added a test for the feature. So far all the documentation I found for MySQL and MSSQL doesn't show any possibility of data-modifying CTEs; most likely it's supported by PostgreSQL only. @greg-rychlewski you are right. Adding LE: this is my commit for the above in ecto: mpotra/ecto@2c144d1 |
Please send a PR for Ecto too! For MySQL/TDS, you can raise if the cte has updates :) |
|
This LGTM! Here is what we need to do:
|
I was thinking about how to generalize this to inserts and deletes. I'm not sure about inserts yet but I think we could pretty easily handle deletes and updates together if we add a new I'm not sure there is a good way to handle insert because it doesn't go through the planner. But maybe it's at least worth handling delete? Deletes are a pretty good use case for this functionality. Delete from the CTE then take the values from |
I forgot we allow edit: But there are more complications with conflict/conflict target/returning so maybe this is better evaluated in a separate issue/pr. |
Based on @greg-rychlewski's feedback I've made some changes to elixir-ecto/ecto#4220 (introduce I haven't done |
operation = case opts[:operation] do | ||
:update_all -> :update_all | ||
:delete_all -> :delete_all | ||
:insert_all -> :insert_all | ||
_ -> :all | ||
end |
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.
As per my comment on Ecto, we should guarantee the .operation
field always exist and it is never nil. Once we tidy up the Ecto side, we can revisit this one. :)
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.
I've just updated the code to ensure .operation
and handle nil
, now that Ecto PR has been merged
@mpotra excellent. Can you please update mix.exs to point to Ecto's |
💚 💙 💜 💛 ❤️ |
Apparently Ecto does not interpret
UPDATE
statements inside CTEs, because it's calling onall/2
for each CTE query.Sadly this turns all data-modifying CTEs into read only (
SELECT
) CTEs.For example, given the following:
would be turned into this undesirable SQL
Apparently it was somewhat easy to enable support for data-modifying CTEs (
UPDATE
only) inside Postgres Adapter, by matchingcte_query/3
on%Ecto.Query{updates: [_ | _]}
and replacingall/2
withupdate_all/2
, which this PR does.I got stuck at doing this for inserts and deletes, and for writing a quick tests for it (apparently
plan/1
in tests complains about references)Hopefully this provides a good starting point for allowing data-modifying CTEs in Ecto.