Skip to content

Permit outer joins when using update_all with PostreSQL #457

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

Conversation

frerich
Copy link
Contributor

@frerich frerich commented Nov 11, 2022

The PostgreSQL UPDATE command permits specifying multiple 'from_item' expressions to allow columns from other tables to appear in the update expression. In particular, the documentation explains (cf. https://www.postgresql.org/docs/current/sql-update.html):

This uses the same syntax as the FROM clause of a SELECT statement

Thus, a statement such as

UPDATE students student
SET
  name = person.name,
  tutor_name = tutor.name
FROM persons person
LEFT JOIN tutors tutor ON tutor.id = person.tutor_id 
WHERE student.person_id = person.id

is legal.

However, executing query like this this via Ecto would fail, raising an error:

  ** (Ecto.QueryError) PostgreSQL supports only inner joins on update_all, got: `left` in query:

  from x0 in "x",
    join: y1 in "y",
    on: true,
    left_join: z2 in "z",
    on: true,
    update: [set: [a: 1]]

This PR fixes the issue by adding a Postgres.Connection.using_join/4 clause for the :update_all case: in this scenario, at least one inner join has to be specified, but additional other joins are permissible.

The PostgreSQL UPDATE command permits specifying multiple 'from_item'
expressions to allow columns from other tables to appear in the update
expression. In particular, the documentation explains (cf.
https://www.postgresql.org/docs/current/sql-update.html):

> This uses the same syntax as the FROM clause of a SELECT statement

Thus, a statement such as

  UPDATE x
  SET a = z.a
  FROM y
  LEFT_JOIN z ON z.i = y.i

is legal.

However, executing query like this this via Ecto would fail, raising an
error:

  ** (Ecto.QueryError) PostgreSQL supports only inner joins on update_all, got: `left` in query:

  from x0 in "x",
    join: y1 in "y",
    on: true,
    left_join: z2 in "z",
    on: true,
    update: [set: [a: 1]]

This PR fixes the issue by adding a `Postgres.Connection.using_join/4`
clause for the :update_all case: in this scenario, at least one inner
join has to be specified, but additional other joins are permissible.
@frerich frerich marked this pull request as ready for review November 11, 2022 09:36
@josevalim
Copy link
Member

@frerich perhaps, instead of doing the validation ourselves, what if we simply allow the user to specify the joins and we emit the query? Will Postgres show a proper error message telling them they need at least one inner join or the error message is not good?

@frerich
Copy link
Contributor Author

frerich commented Nov 11, 2022

what if we simply allow the user to specify the joins and we emit the query? Will Postgres show a proper error message telling them they need at least one inner join or the error message is not good?

@josevalim PostgreSQL merely reports a syntax error:

ERROR:  syntax error at or near "LEFT"
LINE 5: LEFT JOIN tutors tutor ON tutor.id = person.tutor_id

That didn't seem terribly friendly to me. 😔

The PostgreSQL adapter already seems to make some effort to come up with helpful hints as to why something goes wrong (e.g. "the :conflict_target option is required on upserts by PostgreSQL") and since this check seemed easy enough, I felt it would be plausible to keep it.

The inner joins need to come before other joins.

Co-authored-by: José Valim <jose.valim@gmail.com>
The error raised when doing update_all with PostgreSQL changed slightly.
@josevalim josevalim merged commit 5ab355a into elixir-ecto:master Nov 11, 2022
@josevalim
Copy link
Member

💚 💙 💜 💛 ❤️

@frerich frerich deleted the permit_outer_joins_with_postgres_update_all branch May 13, 2023 20:40
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