Skip to content

Add {:unsafe_fragment, ...} support to RETURNING clause#722

Open
henryzhan013 wants to merge 1 commit intoelixir-ecto:masterfrom
henryzhan013:master
Open

Add {:unsafe_fragment, ...} support to RETURNING clause#722
henryzhan013 wants to merge 1 commit intoelixir-ecto:masterfrom
henryzhan013:master

Conversation

@henryzhan013
Copy link

Summary

This lets you pass raw SQL fragments to the RETURNING clause, just like you already can with conflict_target.

Why

PostgreSQL supports expressions in RETURNING — things like:

RETURNING id, (price != EXCLUDED.price) AS was_updated

But Ecto only accepts column names for :returning, which all get quoted as identifiers. If you try passing a string with an expression, it becomes "(price != EXCLUDED.price) AS was_updated" which is invalid.

I ran into this working on ash_postgres where we need to detect whether an upsert actually updated a row or was skipped due to conflict conditions. Right now that requires an extra query. With this change, we can do it in one shot by returning a computed expression.

Changes

One new function clause in returning/1:

defp returning({:unsafe_fragment, fragment}),
  do: [" RETURNING ", fragment]

This is the same pattern conflict_target/1 already uses (line 284), so there's precedent for it. Existing code using lists of column names works exactly as before.

This adds support for raw SQL fragments in the RETURNING clause,
matching the existing pattern used by conflict_target.

This enables use cases like returning computed expressions:
  RETURNING id, (price = EXCLUDED.price) AS was_skipped
@josevalim
Copy link
Member

Can you please add this to tds as well? It has a similar defp returning clause.

Also please send a PR to elixir-ecto/ecto itself that updates the docs!

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.

2 participants