はじめに
にゃーん
この記事は、PostgreSQL 10全部ぬこ Advent Calendar 2017 の18日目のエントリです。
うー、あと一週間。なんとか完走できるといいなー。
今日はPostgreSQL 10で追加されたトランザクションIDに関するSQL関数について軽く。
PostgresQL 10では以下の2関数が追加された。
関数名 | 引数 | 返却型 | 概要 |
---|---|---|---|
txid_current_if_assigned() | なし | bigint | トランザクションIDが既に割り当てられている場合は、txid_current()と同様にトランザクションIDを返却する。トランザクションIDが割り当てられていない状態だと、トランザクションIDをアサインする代わりにnullを返却する |
txid_status() | xid bigint | txid_status | xidで与えたトランザクションIDの状態(commited, abort, in progress, null)を返却する。 |
PostgreSQLでは慣習的にトランザクションのことを"tx"や"x"と表記する文化らしく、このためトランザクションIDも"xid"と表記することが多い。また、それがSQL関数の名前にも使われている。
#txid_current_if_assigned()
psqlで検証する場合、以下のような準備をしておくと見やすい。
xid=# \pset null (null)
Null display is "(null)".
xid=#
こうしておくと、nullが返却されたときに(null)
と表示されるので見やすくなる。
まず、以前から存在していたtxid_current()
を実行する。
この関数は新たにトランザクションIDを割り当て、そのトランザクションIDを返却する。
xid=# SELECT txid_current();
txid_current
--------------
1287942
(1 row)
同じようにtxid_current_if_assigned()
を実行した場合、発行時点ではトランザクションIDは割り当てられていないので、nullが返却される。
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(null)
(1 row)
ここまではpsqlのAUTOCOMMITモード上での動作となる。
今度は、明示的にトランザクションを開始し、そのトランザクション内で、txid_current()
と、txid_current_if_assigned()
を実行してみる。
xid=# BEGIN;
BEGIN
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(1 row)
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(1 row)
xid=# SELECT txid_current();
txid_current
--------------
1287950
(1 row)
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
1287950
(1 row)
xid=# ROLLBACK;
ROLLBACK
xid=#
トランザクション開始直後にtxid_current_if_assigned()
を実行してもnullである。
明示的にtxid_current()
を実行した後だと、txid_current_if_assigned()
も同じトランザクションIDを返却する。
トランザクションIDは、BEGINでトランザクションを開始した後には払い出されない。INSERT/UPDATE/DELETE等の更新文が発行される契機でトランザクションIDが発行される。
xid=# BEGIN;
BEGIN
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(null)
(1 row)
xid=# INSERT INTO test VALUES (1, 'aaa');
INSERT 0 1
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
1287952
(1 row)
で、この関数、どういうときに使うのか。実はあんまり自分でもよく分かっていない。
でも、psql上で使うなら、 \if, \then, \els \endif などと組合せて、トランザクションIDの有無で、後続のSQL発行を分岐させたりするのに役に立つのかも。
txid_status()
実行例
トランザクションを開始し、その直後にtxid_current_if_assigned
を実行してもnullが返却されるのは、上で説明したとおり。
では、このnullのトランザクションIDをtxid_status()
に与えると・・・nullが返却される。ふむ。
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
(null)
(1 row)
xid=# SELECT txid_status(null);
txid_status
-------------
(null)
(1 row)
同じトランザクション内で、INSERT文を実行する。これでトランザクションIDは払い出されるはず。
xid=# INSERT INTO test VALUES (1, 'aaa');
INSERT 0 1
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
1287956
(1 row)
xid=# SELECT txid_status(1287956);
txid_status
-------------
in progress
(1 row)
払い出されたトランザクションID(1287956)をtxid_status()
に与えると、in progress
と表示される。
さらにこの状態でCOMMITして、その後に同じトランザクションIDの状態を見ると
xid=# COMMIT;
COMMIT
xid=# SELECT txid_status(1287956);
txid_status
-------------
committed
(1 row)
このようにcommitted
と表示される。
トランザクションがロールバックした場合には、
xid=# BEGIN;
BEGIN
xid=# DELETE FROM test;
DELETE 1
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
1287957
(1 row)
xid=# SELECT txid_status(1287957);
txid_status
-------------
in progress
(1 row)
xid=# ROLLBACK;
ROLLBACK
xid=# SELECT txid_status(1287957);
txid_status
-------------
aborted
(1 row)
このようにaborted
と表示される。
この関数の用途は?
マニュアルをちら見した感じだと、二層コミットの状態確認なんかに使えるようにも思える。
良くわからない挙動・・・
で、これを動かしていて、「ん?」という動作があった。
xid=# BEGIN;
BEGIN
xid=# DELETE FROM test;
DELETE 1
xid=# SELECT txid_current_if_assigned();
txid_current_if_assigned
--------------------------
1287960
(1 row)
で、今のトランザクションIDをtxid_status()
に与えてみる。
xid=# SELECT txid_status(1287960);
txid_status
-------------
in progress
(1 row)
うん。ここまではわかる。で、今のトランザクションIDから1つ進めた数をtxid_status()
に与えてみる。
xid=# SELECT txid_status(1287961);
txid_status
-------------
in progress
(1 row)
えええ、これもin progressになるのか。
さらに1つ先の数を指定すると、
xid=# SELECT txid_status(1287962);
ERROR: transaction ID 1287962 is in the future
今度はエラーになってしまった。
エラーになったので仕方がない。ROLLBACKしよう。
xid=# ROLLBACK;
ROLLBACK
で、ROLLBACKしたあと、1287960, 1287961のトランザクションIDの状態を見ると、
xid=# SELECT txid_status(1287960);
txid_status
-------------
aborted
(1 row)
xid=# SELECT txid_status(1287961);
txid_status
-------------
in progress
(1 row)
xid=#
1287960はaborted
に、1287961はin progress
になる。これが正しい挙動なのかどうか良くわからない・・・。
おまけ
関数xid_status()
は、backend/utils/adt/txid.c
の792行目(PostgreSQL 10.1の場合)あたりに実装コードがある。
で、ERROR: transaction ID 1287962 is in the future
のエラーメッセージは、その関数内で最初のほうに実行しているTransactionIdInRecentPast()
の中で出力されているっぽい。
現状の作りはそうなんだー、なんだけどせめてPostgreSQL文書のxid_status()
関数の説明のところには、ERRORになるケースを書いてもいいんじゃないかなー。
とふと思った。
参考:該当するリリースノート
本エントリに関連するPostgreSQL 10リリースノートの記載です。
E.2.3.1.5. General Performance
- Add function txid_current_if_assigned() to return the current transaction ID or NULL if no transaction ID has been assigned (Craig Ringer)
- Add function txid_status() to check if a transaction was committed (Craig Ringer)