LOCK(7)

NAME
LOCK - テーブルをロックする

SYNOPSIS

LOCK [ TABLE ] [ ONLY ] name [, ...] [ IN lockmode 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 MODE文は、ROW EXCLUSIVEを保持しているトランザクションがコミットまたはロールバックされるのを待つからです。 したがって、一度ロックを取得してしまえば、コミットされていない状態の書き込みは存在しないことになります。さらに、ロックを解除するまで他のアプリケーションは書き込みを開始することができません。

シリアライザブル隔離レベルで実行しているトランザクションで同様の効果を得るには、全てのSELECT文とデータを更新する文を実行する前にLOCK TABLE文を実行する必要があります。 シリアライザブルなトランザクション側から参照するデータの状態は、最初にSELECT文またはデータ更新用文が開始された時点で固定されます。 後からトランザクション内でLOCK TABLEを実行した場合も同時書き込みを防ぐことはできますが、トランザクションの読み込み対象データの値がコミットされた最新の値であることは保証されません。

このようなトランザクションでテーブル内データを変更する場合は、SHAREモードではなくSHARE ROW EXCLUSIVEロックモードを使用する必要があります。 これによって、この種のトランザクションが同時に複数実行されることがなくなります。 SHARE ROW EXCLUSIVEを使用しないと、デッドロックが発生する可能性があります。 2つのトランザクションの両方が、SHAREモードを取得していながら、実際の更新に必要なROW EXCLUSIVEモードを取得できない状態になる可能性があるためです （トランザクション自身が所有しているロック間は競合しないので、トランザクションはSHAREモードを保持している間もROW EXCLUSIVEを獲得することができます。 しかし、他のトランザクションがSHAREモードを保持している時にはROW EXCLUSIVEを獲得することはできません）。 デッドロックを回避するには、全てのトランザクションが、必ず同一オブジェクトに対して同一の順番でロックを取得するようにしてください。 また、1つのオブジェクトに対して複数のロックモードを呼び出す場合、トランザクションは常に最も制限の強いモードを最初に取得しなければなりません。

ロックモードとロック取得方針についてのより詳細についてはin the documentationを参照してください。

パラメータ

name
    ロックする既存のテーブルの名前です（スキーマ修飾名も可）。 ONLYが指定された場合、そのテーブルのみをロックします。 ONLYが指定されない場合、そのテーブルとすべての子テーブル（もしあれば）をロックします。 LOCK a, b;というコマンドはLOCK TABLE a; LOCK TABLE b;と同じです。 テーブルは1つひとつLOCKで指定された順番でロックされます。

lockmode
    ロックモードには、取得するロックと競合するロックを指定します。 ロックモードについては、in the documentationで説明します。 ロックモードを指定しない場合、最も制限が強いACCESS EXCLUSIVEが使用されます。

NOWAIT
    LOCK TABLEが競合するロックの解放まで待機しないことを指定します。 指定したロックがすぐに取得できない場合、トランザクションはアボートされます。

注釈

LOCK TABLE ... IN ACCESS SHARE MODEには、対象テーブルのSELECT権限が必要です。 他の形式のLOCKには、UPDATE、DELETE、TRUNCATE権限を少なくとも1つ持たなければなりません。

LOCK TABLEはトランザクションブロックの外側では意味がありません。 文が完了するまでしかロックは保持されません。 したがってPostgreSQLはLOCKがトランザクションブロックの外側で使用された場合にエラーを報告します。 トランザクションブロックを定義するためにはBEGIN [begin(7)]およびCOMMIT [commit(7)]（またはROLLBACK [rollback(7)]）を使用してください。

LOCKが扱うのはテーブルレベルのロックのみです。 そのため、モード名にROWが含まれるのは適切ではありません。 これらのモード名によって、普通は、ロックされたテーブル内で行レベルのロックを取得できると思ってしまうでしょう。 また、ROW EXCLUSIVEモードは共有可能なロックです。 LOCK TABLEに関しては、全てのロックモードが同じ意味を持っていることに注意してください。 違うのは、どのモードがどのモードと競合するかという規則だけです。 行レベルでのロックを獲得する方法についてはSELECTマニュアルページのin the documentationとFOR UPDATE/FOR SHARE句 [select(7)]を参照してください。

例

外部キーテーブルへの挿入を行う際に、プライマリキーテーブルへの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 [set_transaction(7)]を参照してください。

ACCESS SHARE、ACCESS EXCLUSIVE、SHARE UPDATE EXCLUSIVEロックモードを除き、PostgreSQLのロックモードとLOCK TABLE構文はOracleのものと互換性があります。