LOCK — テーブルをロックする
LOCK [ TABLE ] [ ONLY ]name
[ * ] [, ...] [ INlockmode
MODE ] [ NOWAIT ] ここでlockmode
には以下のいずれかが入ります。 ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE
LOCK TABLE
はテーブルレベルのロックを取得します。必要であれば競合するロックが解除されるまで待機します。
NOWAIT
が指定された場合は、対象のロックを取得できるまで待機せず、すぐにロックが取得できなければ、このコマンドを中止し、エラーを出力します。
ロックは、一度取得されると現行のトランザクションが完了するまで保持されます
(UNLOCK TABLE
といったコマンドはありません。
ロックが解除されるのは常にトランザクションの終了時です)。
テーブルを参照するコマンドのために自動的にロックを取得する場合、PostgreSQLは使用可能な一番弱いロックモードを常に使用します。
LOCK TABLE
はより制限の強いロックが必要な場合のために用意されています。
例えば、隔離レベルREAD COMMITTED
でトランザクションを実行するアプリケーションで、トランザクションの間中、テーブルのデータを確実に安定させる必要がある場合を考えます。
この場合、問い合わせ実行前にテーブル全体にSHARE
ロックモードを使用します。
これにより、データが同時に変更されるのを防ぎ、それ以降のテーブルの読み取りは、コミット済みの安定したデータが見えるようになります。
なぜならSHARE
ロックモードは書き込み側が取得するROW EXCLUSIVE
ロックと競合するので、LOCK TABLE
文は、name
IN SHARE MODEROW EXCLUSIVE
を保持しているトランザクションがコミットまたはロールバックされるのを待つからです。
したがって、一度ロックを取得してしまえば、コミットされていない状態の書き込みは存在しないことになります。さらに、ロックを解除するまで他のアプリケーションは書き込みを開始することができません。
REPEATABLE READ
またはSERIALIZABLE
隔離レベルで実行しているトランザクションで同様の効果を得るには、全てのSELECT
文とデータを更新する文を実行する前にLOCK TABLE
文を実行する必要があります。
REPEATABLE READ
またはSERIALIZABLE
トランザクション側から参照するデータの状態は、最初にSELECT
文またはデータ更新用文が開始された時点で固定されます。
後からトランザクション内でLOCK TABLE
を実行した場合も同時書き込みを防ぐことはできますが、トランザクションの読み込み対象データの値がコミットされた最新の値であることは保証されません。
このようなトランザクションでテーブルのデータを変更する場合は、SHARE
モードではなくSHARE ROW EXCLUSIVE
ロックモードを使用する必要があります。
これによって、この種のトランザクションが同時に複数実行されることがなくなります。
SHARE ROW EXCLUSIVE
を使用しないと、デッドロックが発生する可能性があります。
2つのトランザクションの両方が、SHARE
モードを取得していながら、実際の更新に必要なROW EXCLUSIVE
モードを取得できない状態になる可能性があるためです
(トランザクション自身が所有しているロック間は競合しないので、トランザクションはSHARE
モードを保持している間もROW EXCLUSIVE
を獲得することができます。
しかし、他のトランザクションがSHARE
モードを保持している時にはROW EXCLUSIVE
を獲得することはできません)。
デッドロックを回避するには、全てのトランザクションが、必ず同一オブジェクトに対して同一の順番でロックを取得するようにしてください。
また、1つのオブジェクトに対して複数のロックモードを呼び出す場合、トランザクションは常に最も制限の強いモードを最初に取得するべきです。
ロックモードとロック取得方針についてのより詳細については13.3. 明示的ロックを参照してください。
name
ロックする既存のテーブルの名前です(スキーマ修飾名も可)。
テーブル名の前にONLY
が指定された場合、そのテーブルのみをロックします。
ONLY
が指定されない場合、そのテーブルとすべての子テーブル(もしあれば)をロックします。
オプションで、テーブル名の後に*
を指定することで、明示的に継承するテーブルも含まれることを示すことができます。
LOCK a, b;
というコマンドはLOCK TABLE a; LOCK TABLE b;
と同じです。
テーブルは1つひとつLOCK
で指定された順番でロックされます。
lockmode
ロックモードには、取得するロックと競合するロックを指定します。 ロックモードについては、13.3. 明示的ロックで説明します。
ロックモードを指定しない場合、最も制限が強いACCESS EXCLUSIVE
が使用されます。
NOWAIT
LOCK TABLE
が競合するロックの解放まで待機しないことを指定します。
指定したロックがすぐに取得できない場合、トランザクションはアボートされます。
LOCK TABLE ... IN ACCESS SHARE MODE
には、対象テーブルのSELECT
権限が必要です。
LOCK TABLE ... IN ROW EXCLUSIVE MODE
には、対象テーブルのINSERT
、UPDATE
、DELETE
またはTRUNCATE
権限が必要です。
他の形式のLOCK
には、テーブルレベルのUPDATE
、DELETE
あるいはTRUNCATE
権限を持たなければなりません。
LOCK TABLE
はトランザクションブロックの外側では意味がありません。
文が完了するまでしかロックは保持されません。
したがってPostgreSQLはLOCK
がトランザクションブロックの外側で使用された場合にエラーを報告します。
トランザクションブロックを定義するためにはBEGINおよびCOMMIT(またはROLLBACK)を使用してください。
LOCK
が扱うのはテーブルレベルのロックのみです。
そのため、モード名にROW
が含まれるのは適切ではありません。
これらのモード名は、普通は、ロックされたテーブル内で行レベルのロックを取得する意図と解釈されるでしょう。
また、ROW EXCLUSIVE
モードは共有可能なテーブルロックです。
LOCK TABLE
に関しては、全てのロックモードが同じ意味を持っており、違うのは、どのモードがどのモードと競合するかという規則だけであることに注意して下さい。
行レベルでのロックを獲得する方法についてはSELECT
参照マニュアルの13.3.2. 行レベルロックとロック処理句を参照してください。
外部キーテーブルへの挿入を行う際に、主キーテーブルへのSHARE
ロックを獲得します。
BEGIN WORK; LOCK TABLE films IN SHARE MODE; SELECT id FROM films WHERE name = 'Star Wars: Episode I - The Phantom Menace'; -- レコードがなければROLLBACKしてください。 INSERT INTO films_user_comments VALUES (_id_, 'GREAT! I was waiting for it for so long!'); COMMIT WORK;
削除操作を行う際に主キーテーブルのSHARE ROW EXCLUSIVE
ロックを取得します。
BEGIN WORK; LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE; DELETE FROM films_user_comments WHERE id IN (SELECT id FROM films WHERE rating < 5); DELETE FROM films WHERE rating < 5; COMMIT WORK;
標準SQLにはLOCK TABLE
はありません。
その代わりにトランザクションの同時性レベルを指定するSET TRANSACTION
が使用されます。
PostgreSQLはこのコマンドもサポートしています。
詳細はSET TRANSACTIONを参照してください。
ACCESS SHARE
、ACCESS EXCLUSIVE
、SHARE UPDATE EXCLUSIVE
ロックモードを除き、PostgreSQLのロックモードとLOCK TABLE
構文はOracleのものと互換性があります。