Better support of locking clauses (FOR UPDATE/SHARE) when supported

オープン
#12,101 コメント 4 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
35/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
postgresql, python, sql
領域
backend, databases

調査の方向性

ORM の select/query エントリポイントと with_for_update() から始め、単一の ForUpdateArg がどのように保存され、コンパイルされるかを追跡します。PostgreSQL に対して joinedload() を使用した例を再現し、生成されたロック句を期待される SQL と比較します。複数のロック句を、サポート対象のバックエンドやドキュメント化された警告を壊すことなく表現できれば完了です。

索引モデルが issue の本文から書いたものです。

説明

oracle postgresql use case
Describe the use case

The Postgres docs specifically say that you should be able to have both a FOR UPDATE and a FOR SHARE in the same query:

Multiple locking clauses can be written if it is necessary to specify different locking behavior for different tables

which is often useful with join queries. In a common scenario, I'd want to take an exclusive lock on the primary row(s) being queried, and a SHARE lock on any rows that are brought in via relationships, since they're going to be read-only for the current transaction.

As far as I can tell, though, it's not actually possible to construct that in the SqlAlchemy ORM. That's because Query only supports a single ForUpdateArg, which is overwritten by with_for_update() as opposed to appending to a list. On supported platforms - or maybe attempt on all platforms and leave a user-beware dragon - we should be able to have multiple for-update clauses (see example).

I do recognize that the SqlAlchemy docs have a dragon warning for this whole area:

Using with_for_update in the context of eager loading relationships is not officially supported or recommended by SQLAlchemy and may not work with certain queries on various database backends. When with_for_update is successfully used with a query that involves joinedload(), SQLAlchemy will attempt to emit SQL that locks all involved tables.

That seems overly pessimistic, though, at least when using joinedload() (it's clearly more problematic with selectinload(), where we'd need to set the right lock clauses on the right individual selects). This feels along the lines of https://github.com/sqlalchemy/sqlalchemy/issues/5583 - is it possible to have more tailored warnings (both in code and in docs) when there are scenarios that are likely to work?

Databases / Backends / Drivers targeted

Postgres (documented valid in all versions since 9.3)

Example Use

Python:

q = (
  select(A)
  .filter_by(id='xyz')
  .options(
     joinedload(A.related_b),
     joinedload(A.related_c),
  )
  .with_for_update(of=A)
  .with_for_update(of=[B, C], read=True)
)
result = session.execute(q)

# sample continuation - reading from `related_b`, writing to the primary object:
#   my_item = result.scalars().first()
#   my_item.col1 = my_item.related_b.other_value + 2
#   session.commit()

Expected SQL:

  SELECT a.id, a.col1, a.col2, ..., b.id, b.colb1, ..., c.id, c.colc1, ...
  FROM a
    LEFT JOIN b ON (b.id=a.fk_to_b)
    LEFT JOIN c ON (c.id=a.fk_to_c)
  WHERE a.id='xyz'
  FOR UPDATE OF a
  FOR SHARE OF b, c;
Additional context

No response

主要言語
Python
スター
12.3k
フォーク
1.8k
平均マージ
2日 10時間
マージ済み PR(30日)
9

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

sqlalchemy/sqlalchemy のほかの issue

sqlalchemy/sqlalchemy の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。