LoginSignup
7
0

More than 5 years have passed since last update.

トランザクションIDに関する関数の追加

Last updated at Posted at 2017-12-17

はじめに

にゃーん
この記事は、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)
7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0