ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

PostgreSQL のオブジェクト識別子データ型(OID 別名型)と使い方

目的

PostgreSQL には「オブジェクト識別子 (OID)」の別名型として「オブジェクト識別子データ型(OID 別名型)」があります。
8.19. オブジェクト識別子データ型

本記事ではオブジェクト識別子データ型と使い方を中心に解説します。最後にその実装にも踏み込んで解説をします。

前提

OID については下記の解説記事を参照ください。
オブジェクト識別子を活用する | Let's POSTGRES

PostgreSQL 13 で調査を行いました。参照する PostgreSQL 日本語ドキュメントのバージョンは現時点で最新の 12 としています。

オブジェクト識別子データ型の一覧

オブジェクト識別子データ型の一覧はマニュアルに記載されています。PostgreSQL 12 のマニュアルから引用します。

型名 参照 説明 値の例
oid すべて 数値オブジェクト識別子 564182
regproc pg_proc 関数名 sum
regprocedure pg_proc 引数の型を持つ関数 sum(int4)
regoper pg_operator 演算子 +
regoperator pg_operator 引数の型を持つ演算子 *(integer,integer) or -(NONE,integer)
regclass pg_class リレーション名 pg_type
regtype pg_type データ型名 integer
regrole pg_authid ロール名 smithee
regnamespace pg_namespace 名前空間 pg_catalog
regconfig pg_ts_config テキスト検索設定 english
regdictionary pg_ts_dict テキスト検索辞書 simple
8.19. オブジェクト識別子データ型

オブジェクト識別子データ型へのキャスト

オブジェクト識別子データ型へは型キャストの構文でキャストできます。型キャストの構文を以下に示します。

PostgreSQL は型キャストに2つの等価な構文を受け付けます。

CAST ( expression AS type )
expression::type

CAST 構文は SQL に準拠したものです。:: を使用する構文は、PostgreSQL で伝統的に使用されている方法です。

4.2. 評価式

オブジェクトの名称から OID を得る

別名データ型により、オブジェクトの OID 値の検索が簡単になります。例えば、mytable テーブルに関連した pg_attribute 行を確認するには、以下のように記述することができます。

SELECT * FROM pg_attribute WHERE attrelid = 'mytable'::regclass;
8.19. オブジェクト識別子データ型

この例では 'mytable' を regclass にキャストしています。regclass は pg_class を参照しており、リレーション(テーブル、インデックス、ビューなど)の OID に変換できます。

SELECT 句で OID を取得したいときは OID にキャストします。

-- OID にキャストしないと名称を取得する
SELECT 'pg_class'::regclass;

 regclass
----------
 pg_class
(1 row)

-- OID にキャストすれば OID を取得できる
SELECT 'pg_class'::regclass::oid;

 oid
------
 1259
(1 row)

OID からオブジェクトの名称を得る

SELECT oid FROM pg_class WHERE relname = 'pg_class';

 oid
------
 1259
(1 row)

SELECT 1259::regclass;

 regclass
----------
 pg_class
(1 row)

'pg_class' の OID が 1259 であることを確認しました。その 1259 を regclass にキャストすることで、該当するリレーション名の 'pg_class' を取得できました。

システムカタログでのオブジェクト識別子データ型の適用

オブジェクト識別子データ型(8.19. オブジェクト識別子データ型)の「参照」とシステムカタログの列の説明にある「参照先」を比較することで、適用できるケースを判別できます。

一例として pg_cast (51.10. pg_cast) の列をマニュアルで確認します。

システムカタログでのオブジェクト識別子データ型の適用

castsource, casttarget の参照先は pg_type.oid となっています。オブジェクト識別子データ型で regtype の参照は pg_type となっています。このことから、regtype にキャストすることで「データ型名」に変換できるとわかります。

castfunc の参照先は pg_proc.oid となっています。オブジェクト識別子データ型で regprocedure の参照は pg_proc となっています。このことから、regprocedure にキャストすることで「引数の型を持つ関数」に変換できるとわかります。

'text' からのキャストを調べる例で使用方法を示します。

SELECT
  castsource::regtype,
  casttarget::regtype,
  castfunc::regprocedure
FROM
  pg_cast
WHERE
  castsource = 'text'::regtype
ORDER BY
  castsource,
  casttarget;

 castsource |    casttarget     |    castfunc
------------+-------------------+----------------
 text       | "char"            | "char"(text)
 text       | name              | name(text)
 text       | xml               | xml(text)
 text       | character         | -
 text       | character varying | -
 text       | regclass          | regclass(text)
(6 rows)

regproc と regoper 別名型の制限

マニュアルに以下の記述があります。

regproc と regoper 別名型は、一意な(オーバーロードしていない)名前のみを入力として受け入れるため、これらの使用には限度があります。ほとんどの場合、regprocedure または regoperator を使用するのが適切です。

8.19. オブジェクト識別子データ型

この記述を実際の挙動から確認します。

-- pg_proc カタログで pi と sum の定義数を確認する
SELECT
  proname,
  count(*)
FROM
  pg_proc
WHERE
  proname IN ('pi', 'sum')
GROUP BY
  proname;

 proname | count
---------+-------
 pi      |     1
 sum     |     8
(2 rows)

-- pg_proc カタログ内で関数名が一意な場合
SELECT 'pi'::regproc;

 regproc
---------
 pi
(1 row)

-- pg_proc カタログ内で関数名が一意でない場合
SELECT 'sum'::regproc;

ERROR:  more than one function named "sum"
LINE 1: SELECT 'sum'::regproc;

-- 引数の型を指定して regprocedure でキャストする
SELECT 'sum(integer)'::regprocedure;

 regprocedure
--------------
 sum(integer)
(1 row)

regproc では一意な名前を指定しないとエラーになりました。このような場合、マニュアルに記載の通り、引数の型を指定して regprocedure でキャストする必要があります。

キャストの実装と挙動

まず regclass の「入出力変換関数(テキスト形式)」と「データ型変換パス」を実装している関数名について調べます。

「入出力変換関数(テキスト形式)」の説明をマニュアルから引用します。

ユーザ定義データ型では必ず入力関数と出力関数が必要です。 これらの関数は、その型が(ユーザによる入力とユーザへの出力のための)文字列としてどのように表現されるかと、その型がメモリ中でどう構成されるかを決定します。

37.13. ユーザ定義の型

「データ型変換パス」はキャスト可能な型やその変換方法を定義したものです。

-- regclass の入出力変換関数(テキスト形式)を調べる
SELECT
  typinput::oid,
  typinput::regprocedure,
  typoutput::oid,
  typoutput::regprocedure
FROM
  pg_type
WHERE
  typname = 'regclass';

 typinput |      typinput       | typoutput |       typoutput
----------+---------------------+-----------+-----------------------
     2218 | regclassin(cstring) |      2219 | regclassout(regclass)
(1 row)

-- regclass の入出力変換関数(テキスト形式)の関数名を調べる
SELECT
  oid,
  proname,
  prosrc
FROM
  pg_proc
WHERE
  oid IN (2218, 2219);

 oid  |   proname   |   prosrc
------+-------------+-------------
 2218 | regclassin  | regclassin
 2219 | regclassout | regclassout
(2 rows)

-- regclass のデータ型変換パスを調べる
SELECT
  castsource::regtype,
  casttarget::regtype,
  castfunc,
  castfunc::regprocedure,
  castcontext,
  castmethod
FROM
  pg_cast
WHERE
  castsource = 'regclass'::regtype
  OR casttarget = 'regclass'::regtype
ORDER BY
  castsource,
  casttarget;

    castsource     | casttarget | castfunc |    castfunc    | castcontext | castmethod
-------------------+------------+----------+----------------+-------------+------------
 bigint            | regclass   |     1287 | oid(bigint)    | i           | f
 smallint          | regclass   |      313 | int4(smallint) | i           | f
 integer           | regclass   |        0 | -              | i           | b
 text              | regclass   |     1079 | regclass(text) | i           | f
 oid               | regclass   |        0 | -              | i           | b
 character varying | regclass   |     1079 | regclass(text) | i           | f
 regclass          | bigint     |     1288 | int8(oid)      | a           | f
 regclass          | integer    |        0 | -              | a           | b
 regclass          | oid        |        0 | -              | i           | b
(9 rows)

-- text -> regclass のキャストを実行する関数名を調べる
SELECT
  oid,
  proname,
  prosrc
FROM
  pg_proc
WHERE
  oid = 1079;

 oid  | proname  |    prosrc
------+----------+---------------
 1079 | regclass | text_regclass
(1 row)

テキスト入出力のキャスト

regclass のテキストを入出力とするキャストの実装関数を表にまとめます。

目的 関数
入力変換関数(テキスト形式) regclassin
出力変換関数(テキスト形式) regclassout
text ⇒ regclass text_regclass

これらの関数はいずれも下記のソースコード内に存在します。
src/backend/utils/adt/regproc.c

regclassin, text_regclass 共に入力がテキストという点では同じです。そこで、プリントデバッグの手法でどのようなケースにおいて、どちらがコールされるかを調査しました。

結果は以下の通りとなりました。

クエリ 関数
SELECT 'pg_class'::regclass; regclassin
SELECT 'pg_class'::text::regclass; text_regclass
SELECT nextval('foo'); regclassin
SELECT nextval('foo'::text); text_regclass

text_regclass のコメント及びマニュアルの記載より、text_regclass は PostgreSQL 8.1 より前のシーケンス関数の挙動である「動的束縛」を実現するために用意されているようです。詳細はマニュアルの「シーケンス操作関数(9.16. シーケンス操作関数)」に記載されている注記を参照ください。

regclassout のコメントには以下の記載があります。

converts class OID to "class_name"
If OID doesn't match any pg_class entry, return it numerically

これを実際の挙動から確認します。

-- pg_class に実在する OID なら名称に変換される
SELECT 1259::regclass;

 regclass
----------
 pg_class
(1 row)

-- pg_class に実在しない OID なら値のままとなる
SELECT 9999::regclass;

 regclass
----------
 9999
(1 row)

数値でのキャスト

regclass のデータ型変換パスで以下の変換は castmethod = b であり、『型がバイナリを強制しているため、変換が必要ない(引用元:51.10. pg_cast)』となっています。

  • integer ⇒ regclass
  • oid ⇒ regclass
  • regclass ⇒ integer
  • regclass ⇒ oid

『型がバイナリを強制している』が何を指しているかは CREATE CAST の説明を参照ください。

2つのデータ型をバイナリ強制互換とすることができます。これは、関数をまったく呼び出さなくても、「自由に」変換を行うことができることを意味します。これには、対応する値は、同じ内部表現を使用している必要があります。例えば、データ型 text と varchar には、両方向でバイナリ互換性があります。バイナリ強制互換性は必ずしも対称関係ではありません。例えば、現在の実装では xml から text へのキャストは自由に行うことができますが、逆方向では少なくとも構文検査を行う関数が必要です。(2つの型が両方向でバイナリ強制互換であることは、バイナリ互換性と呼ばれます。)

CREATE CAST

oid データ型の実装に関して以下の記述があります。

oid データ型は現在、符号なし4バイト整数として実装されています。

8.19. オブジェクト識別子データ型

これらのことから、regclass も oid と同じ内部表現を持っている、つまり符号なし4バイト整数として実装されていると推測できます。regclass は oid の別名型なので、当然といえるかもしれません。