I’m using Ecto + Postgres, and I’d like to be able to lock only a specific row from a table, when I have a join clause. In Postgres you accomplish this by adding an OF <tablename> to the lock expression. For example:
In Postgres SQL:
# lock `users` but not `roles`
SELECT * FROM users INNER JOIN roles ON users.role_id = roles.id FOR UPDATE OF users;
Using Ecto, it seems like the only way to accomplish this is to hardcode the generated table alias (u0 here), which is pretty brittle:
User
|> join(:inner, [u] _ in assoc(u, :role))
|> lock("FOR UPDATE OF u0")
Is there any way to do it with Ecto in a non-brittle way?
Seems like fragments won’t work either.
I found this StackOverflow question as well. The conclusion was there’s no clean way to do it with Ecto, but double checking with the Forum.
Also, I’ll create a feature request ticket in github depending what I learn here.
Also in case anyone is curious or needs more info, here’s the relevant text in the Postgres docs which explains how the locking clause works with a list of tables:
If specific tables are named in a locking clause, then only rows coming from those tables are locked; any other tables used in the SELECT are simply read as usual. A locking clause without a table list affects all tables used in the statement. If a locking clause is applied to a view or sub-query, it affects all tables used in the view or sub-query. However, these clauses do not apply to WITH queries referenced by the primary query. If you want row locking to occur within a WITH query, specify a locking clause within the WITH query.