PostgreSQL のオブジェクト識別子データ型(OID 別名型)と使い方
目的
PostgreSQL には「オブジェクト識別子 (OID)」の別名型として「オブジェクト識別子データ型(OID 別名型)」があります。
8.19. オブジェクト識別子データ型
本記事ではオブジェクト識別子データ型と使い方を中心に解説します。最後にその実装にも踏み込んで解説をします。
前提
OID については下記の解説記事を参照ください。
オブジェクト識別子を活用する | Let's POSTGRES
PostgreSQL 13 で調査を行いました。参照する PostgreSQL 日本語ドキュメントのバージョンは現時点で最新の 12 としています。
オブジェクト識別子データ型の一覧
オブジェクト識別子データ型の一覧はマニュアルに記載されています。PostgreSQL 12 のマニュアルから引用します。
8.19. オブジェクト識別子データ型
型名 参照 説明 値の例 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
オブジェクト識別子データ型へのキャスト
オブジェクト識別子データ型へは型キャストの構文でキャストできます。型キャストの構文を以下に示します。
PostgreSQL は型キャストに2つの等価な構文を受け付けます。
CAST ( expression AS type ) expression::typeCAST 構文は 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 の別名型なので、当然といえるかもしれません。