- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- PostgreSQL/解析/拡張機能の構築基盤 へ行く。
- 1 (2017-06-24 (土) 00:33:32)
- 2 (2017-06-25 (日) 23:07:52)
- 3 (2017-06-26 (月) 23:08:39)
- 4 (2017-06-28 (水) 23:25:26)
- 5 (2017-07-01 (土) 01:18:40)
- 6 (2017-07-02 (日) 20:09:39)
- 7 (2017-07-11 (火) 20:01:26)
- 8 (2017-07-11 (火) 22:58:56)
- 9 (2017-07-11 (火) 23:57:22)
- 10 (2017-07-12 (水) 23:31:04)
- 11 (2017-07-18 (火) 21:51:44)
- 12 (2018-02-04 (日) 22:32:14)
概要 †
ここでは,拡張機能の開発で必要となる知識について整理する。
具体的には,拡張機能のディレクトリ構造,ビルドスクリプト,制御ファイルについて記述する。
拡張機能のディレクトリ構造 †
CREATE EXTENSIONとしてインストールされる拡張機能のディレクトリ構造は,以下のようになっている(ここでは,拡張機能を「myext」として説明を進める)。
myext |- myext.control |- myext--xx.sql |- Makefile |- myext.c
- myext.control
- CREATE EXTENSIONやALTER EXTENSIONが実行されたときに読み込まれる。
- myext--xx.sql
- CREATE EXTENSIONやALTER EXTENSIONが実行されたときに実行されるターゲットバージョンのスクリプト。xxからyyへのアップデートに段階的な更新が必要な場合は,myext--xx--yy.sqlのようにする。
- インストールするオブジェクトがない場合,空ファイルを作成しておけばCREATE EXTENSIONコマンドは成功する。
- Makefile
- ビルド定義
- myex.c
- ソースコード。もちろん複数のソースコードのコンパイル&リンクも問題ない。
サンプル1 trueを返すだけのmyext関数の作成
各ファイルの内容はざっと最小限で書いて以下のような感じになる。各ファイルの定義内容については後述する。
Makefile
MODULES = myext EXTENSION = myext DATA = myext--1.0.sql PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)
myext--1.0.sql
\echo USE "CREATE EXTENSION myext" TO LOAD this file. \quit CREATE OR REPLACE FUNCTION myext() RETURNS BOOL AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
myext.c
#include "postgres.h" #include "fmgr.h" #include "funcapi.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(myext); Datum myext(PG_FUNCTION_ARGS) { PG_RETURN_BOOL(true); // ただtrueを返すだけの関数 }
この内容で作成される拡張関数だが,以下のような結果を返すものである。
postgres=# select myext(); myext ------- t (1 row)
Makefile †
- MODULES
- 同じ系のソースから生成される共有ライブラリのリスト。
- myext.cとmyext2.cがあれば,myext.soとmyext2.soが作成される。
# myext.soとmyext2.soがターゲットリストに加わる MODULES = myext myext2
- MODULE_big
- 作成する共有ライブラリ。
- OBJSにオブジェクトファイルを列挙する。
# myext.soターゲットが作成される # イメージ) # myext.so: $(OBJS) MODULE_big = myext OBJS = myext.o hoge.o
- PROGRAM
- $(pg_config --bindir)にプログラムがインストールされる実行ファイル。
- OBJSにオブジェクトファイルを列挙する。
PROGRAM = myext OBJS = myext.o hoge.o
- EXTENSION
- 拡張の名前を指定する。$(EXTENSION).controlファイルもパッケージに必要。
MODULES = myext # 拡張の名前 # $(pg_config --sharedir)/extension/にmyext.controlがインストールされる EXTENSION = myext
- 拡張の名前を指定する。$(EXTENSION).controlファイルもパッケージに必要。
- MODULEDIR
- DATAやDOCSのインストール先ディレクトリ。
- $(pg_config --sharedir)/$(MODULEDIR)となる。
指定がない場合は,EXTENSIONが使われる。EXTENSIONがなければcontribとなる。MODULES = myext EXTENSION = myext MODULEDIR = myext DATA = myext--1.0.sql
- DATA
- $(pg_config --sharedir)にインストールされるデータ。
MODULES = myext EXTENSION = myext DATA = myext--1.0.sql
- $(pg_config --sharedir)にインストールされるデータ。
- DATA_built
- $(pg_config --sharedir)にインストールされるデータ。
- メモ:DATAとの違いがよく分からない,DATAとの違いは$(srcdir)のプリフィックスはつかないこと?
MODULES = myext EXTENSION = myext DATA_built = myext--1.0.sql
- DOCS
- $(pg_config --docdir)にインストールされるドキュメント。
MODULES = myext DOCS = $(addsuffix .example, $(MODULES))
- $(pg_config --docdir)にインストールされるドキュメント。
- SCRIPTS
- $(pg_config --bindir)にプログラムがインストールされるスクリプト。
SCRIPTS = myext.sh
- $(pg_config --bindir)にプログラムがインストールされるスクリプト。
- SCRIPTS_built
- $(pg_config --bindir)にプログラムがインストールされるスクリプト。
- メモ:SCRIPTSとの違いがよく分からない,SCRIPTSとの違いは$(srcdir)のプリフィックスはつかないこと?
SCRIPTS_built = myext.sh
- REGRESS
- リグレッションテストのリスト(.sqlは不要)。
# sql/myext.sqlが実行される REGRESS = myext
- リグレッションテストのリスト(.sqlは不要)。
- REGRESS_OPTS
- リグレッションテストの実行引数。pg_regressに渡すオプション。
# portオプションを渡す。 # (デフォルトと異なるportで動作している場合) REGRESS_OPTS += --port $(PG_PORT)
- リグレッションテストの実行引数。pg_regressに渡すオプション。
- EXTRA_CLEAN
- make cleanで削除されるデータ
# rm -rf $(EXTRA_CLEAN) EXTRA_CLEAN = my.log
- make cleanで削除されるデータ
- PG_CPPFLAGS
- CPPFLAGSに追加されるビルドオプション
CPPFLAGS = -std=c++11
- CPPFLAGSに追加されるビルドオプション
- PG_LIBS
- PROGRAMのリンクに追加されるライブラリ
PG_LIBS = /usr/local/postgres/libs/hello.so
- PROGRAMのリンクに追加されるライブラリ
- SHLIB_LINK
- MODULE_bigリンクに追加されるライブラリ
MODULE_big = /usr/local/postgres/libs/hello.so
- MODULE_bigリンクに追加されるライブラリ
- PG_CONFIG
- pg_configコマンド(pg_configは,bindirやsharedirなどの情報を出力する)
PG_CONFIG = pg_config
- pg_configコマンド(pg_configは,bindirやsharedirなどの情報を出力する)
- PGFILEDESC
- ライブラリの概要
# cleanターゲットで以下のように展開される。PGFILEDESC指定があれば,cleanでwin32ver.oが削除される。 # # clean: # ifdef MODULES # rm -f $... $(if $(PGFILEDESC),$(WIN32RES)) # endif PGFILEDESC = "myext - always returns true"
- ライブラリの概要
制御ファイル(.control) †
CreateExtensionやAlterExtensionコマンド実行時に参照される制御設定用ファイルである。
- directory (string)
- 拡張のスクリプトファイルがあるディレクトリ。
- 指定がなければ,"`pg_config --sharedir`/extension"となる。絶対パス指定の場合は指定された値が,相対パスの場合はSHAREDIRからの総体パスとなる。
# デフォルト #directory = 'extension' directory = 'myext'
参考 get_extension_script_directory() - on doxygen.postgresql.org
- default_version (string)
- 拡張のデフォルトのバージョンを示す。CREATE EXTENSIONやALTER EXTENSIONでバージョン指定がない場合にこの値が使われる。
default_version = '1.0'
参考 ExecAlterExtensionStmt() - on doxygen.postgresql.org
参考 CreateExtension() - on doxygen.postgresql.org
- 拡張のデフォルトのバージョンを示す。CREATE EXTENSIONやALTER EXTENSIONでバージョン指定がない場合にこの値が使われる。
- comment (string)
- 拡張機能に関するコメント
comment = 'myext is a only function that always returns true.'
参考 CreateComments() - on doxygen.postgresql.org
- 拡張機能に関するコメント
- encoding (string)
- スクリプトファイルで使用される文字コード。デフォルトでは,データベースのエンコードが使われる。
encoding = 'EUC-JP'
参考 read_extension_script_file() - on doxygen.postgresql.org
- スクリプトファイルで使用される文字コード。デフォルトでは,データベースのエンコードが使われる。
- module_pathname (string)
- スクリプト内のMODULE_PATHNAMEをこの文字列に置換する。
module_pathname = '$libdir/myext'
参考 execute_extension_script() - on doxygen.postgresql.org
- スクリプト内のMODULE_PATHNAMEをこの文字列に置換する。
- requires (string)
- 拡張が依存する拡張機能のリストで,カンマ区切りで指定する。
requires = 'myext1, myext2'
- 拡張が依存する拡張機能のリストで,カンマ区切りで指定する。
- superuser (boolean)
- trueの場合superuserのみが拡張機能を作成または更新できる。
superuser = true
参考 execute_extension_script() - on doxygen.postgresql.org
- trueの場合superuserのみが拡張機能を作成または更新できる。
- relocatable (boolean)
- 拡張機能をインストールしたスキーマから移動可能か否か。trueの場合は移動可能。
- relocatable = falseの場合は,schema変数を指定してスクリプト内の@extschema@置換変数を使ってスキーマ指定することができる。
relocatable = true
- schema (string)
- 再配置可能ではない(relocatable = false)の拡張に対して指定する,オブジェクトをインストールするスキーマ。relocatable = trueでこの値を指定するとエラーになる。
- 拡張機能が指定のスキーマにインストールされるよう強制する。通常指定がない場合は,publicとなるだろう。
schema = 'sales'
CREATE EXTENSION †
CreateExtension実行の流れ †
CREATE EXTENSION呼び出しの流れをデバッガのスタックトレースを実行し俯瞰する。
まずは,以下のコマンドを実行してみる。myextは,ページ内の サンプル1 を参照。
CREATE EXTENSION myext;
最終的にはpg_dlsymが呼ばれるだろうと想定しブレークポイントを貼っておく。以下,コマンドを実行しブレークポイントで中断させた時のスタックトレースである。
(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 24.1 * frame #0: 0x000000010edba000 postgres`pg_dlsym(...) at dynloader.c:38 <-- 最終的にシステムコールで共有オブジェクトの読み込み frame #1: 0x000000010f0083bd postgres`internal_load_library(...) at dfmgr.c:240 <-- ライブラリのロード frame #2: 0x000000010f007d7f postgres`load_external_function(...) at dfmgr.c:105 frame #3: 0x000000010eb8f532 postgres`fmgr_c_validator(...) at pg_proc.c:823 frame #4: 0x000000010f00c143 postgres`OidFunctionCall1Coll(...) at fmgr.c:1596 frame #5: 0x000000010eb8f0bb postgres`ProcedureCreate(...) at pg_proc.c:726 frame #6: 0x000000010ec45204 postgres`CreateFunction(...) at functioncmds.c:1048 frame #7: 0x000000010ee7520e postgres`ProcessUtilitySlow(...) at utility.c:1361 frame #8: 0x000000010ee73878 postgres`standard_ProcessUtility(...) at utility.c:892 frame #9: 0x000000010ee7299d postgres`ProcessUtility(...) at utility.c:334 frame #10: 0x000000010ec3f486 postgres`execute_sql_string(...) at extension.c:744 <-- インストールスクリプト実行 frame #11: 0x000000010ec39764 postgres`execute_extension_script(...) at extension.c:904 frame #12: 0x000000010ec386af postgres`CreateExtension(...) at extension.c:1461 <-- ここでCREATE EXTENSIONコマンド実行 frame #13: 0x000000010ee74cb9 postgres`ProcessUtilitySlow(...) at utility.c:1281 frame #14: 0x000000010ee73878 postgres`standard_ProcessUtility(...) at utility.c:892 frame #15: 0x000000010ee7299d postgres`ProcessUtility(...) at utility.c:334 frame #16: 0x000000010ee72318 postgres`PortalRunUtility(...) at pquery.c:1183 frame #17: 0x000000010ee7155c postgres`PortalRunMulti(...) at pquery.c:1314 frame #18: 0x000000010ee70b6d postgres`PortalRun(...) at pquery.c:812 frame #19: 0x000000010ee6c527 postgres`exec_simple_query(...) at postgres.c:1104 frame #20: 0x000000010ee6b730 postgres`PostgresMain(...) at postgres.c:4045 frame #21: 0x000000010edd45e2 postgres`BackendRun(...) at postmaster.c:4253 frame #22: 0x000000010edd389d postgres`BackendStartup(...) at postmaster.c:3927 frame #23: 0x000000010edd2965 postgres`ServerLoop at postmaster.c:1698 frame #24: 0x000000010edd02d6 postgres`PostmasterMain(...) at postmaster.c:1306 frame #25: 0x000000010ed1097f postgres`main(...) at main.c:228 frame #26: 0x00007fffdb4e0235 libdyld.dylib`start + 1 frame #27: 0x00007fffdb4e0235 libdyld.dylib`start + 1
以下プロセスの概要を記述する。
- 制御ファイル(share/extension/myext.control)を読み込みパースする。
- バージョン指定があるか確認する。
- CREATE EXTENSIONコマンドで指定されている又は.controlファイルのdefault_version指定が必要。
- 更新パスを計算する。
- pg_extensionテーブルに拡張機能の情報を登録及び依存エントリーを更新。
- .controlファイルに定義commentがあれば更新。
- インストールスクリプトを実行。
CreateExtensionの実行 †
CreateExtensionの実行は初回だけでよい。CREATE EXTENSIONコマンドでデータベースに登録されたデータベースオブジェクトはカタログテーブルから確認することができる。
postgres=# SELECT pe.*, PC.probin FROM pg_extension pe INNER JOIN pg_proc pc ON (pe.extnamespace = pc.pronamespace) WHERE extname = 'myext'; -[ RECORD 1 ]--+-------------- extname | myext extowner | 10 extnamespace | 2200 extrelocatable | t extversion | 1.0 extconfig | extcondition | probin | $libdir/myext
CREATE EXTENSIONコマンドは,バックエンドプロセス終了後,再度新規にプロセスを開始しても再実行する必要はない。関連する拡張機能の情報はデータベースに登録されており,カタログから参照できるからだ。psqlクライアントで接続後にCREATE EXTENSIONで登録した関数を含むクエリを実行してみると,FuncExprノードの評価時に関数オブジェクトに関連する共有オブジェクトがロードされていることが確認できる。関数オブジェクト(共有オブジェクトも)は初回ロード時にバックエンドプロセスのキャッシュテーブルに登録され,次回以降はキャッシュテーブルの情報が参照されるようになっている。
(...) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 22.1 * frame #0: 0x0000000106ee3d69 postgres`load_external_function(...) at dfmgr.c:102 <-- 共有オブジェクトをロードし_PG_initを呼ぶ。 frame #1: 0x0000000106eeab29 postgres`fmgr_info_C_lang(...) at fmgr.c:351 frame #2: 0x0000000106ee5647 postgres`fmgr_info_cxt_security(...) at fmgr.c:282 frame #3: 0x0000000106ee56c5 postgres`fmgr_info_cxt(...) at fmgr.c:172 frame #4: 0x0000000106b94991 postgres`init_fcache(...) at execQual.c:1328 frame #5: 0x0000000106b993d9 postgres`ExecEvalFunc(...) at execQual.c:2393 frame #6: 0x0000000106b9e603 postgres`ExecTargetList(...) at execQual.c:5401 frame #7: 0x0000000106b9e4fe postgres`ExecProject(...) at execQual.c:5625 frame #8: 0x0000000106bbf57c postgres`ExecResult(...) at nodeResult.c:155 frame #9: 0x0000000106b92aaa postgres`ExecProcNode(...) at execProcnode.c:385 frame #10: 0x0000000106b8e1e3 postgres`ExecutePlan(...) at execMain.c:1549 frame #11: 0x0000000106b8e111 postgres`standard_ExecutorRun(...) at execMain.c:337 frame #12: 0x0000000106b8df5a postgres`ExecutorRun(...) at execMain.c:285 frame #13: 0x0000000106d4d081 postgres`PortalRunSelect(...) at pquery.c:942 frame #14: 0x0000000106d4ca74 postgres`PortalRun(...) at pquery.c:786 frame #15: 0x0000000106d48527 postgres`exec_simple_query(...) at postgres.c:1104 frame #16: 0x0000000106d47730 postgres`PostgresMain(...) at postgres.c:4045 frame #17: 0x0000000106cb05e2 postgres`BackendRun(...) at postmaster.c:4253 frame #18: 0x0000000106caf89d postgres`BackendStartup(...) at postmaster.c:3927 frame #19: 0x0000000106cae965 postgres`ServerLoop at postmaster.c:1698 frame #20: 0x0000000106cac2d6 postgres`PostmasterMain(...) at postmaster.c:1306 frame #21: 0x0000000106bec97f postgres`main(...) at main.c:228 frame #22: 0x00007fffa402e235 libdyld.dylib`start + 1 frame #23: 0x00007fffa402e235 libdyld.dylib`start + 1
参考 load_external_function() - on doxygen.postgresql.org
ちなみに関数に関する情報は,スタックトレースの上流でカタログテーブルから読み込みされている。具体的には,parser/parse_func.cのParseFuncOrColumn関数である。以下関連するリンクを掲載する。
参考
- ParseFuncOrColumn() - on doxygen.postgresql.org
- func_get_detail() - on doxygen.postgresql.org
shared_preload_librariesとの違い †
shared_preload_librariesという設定があるが,これはpostgres起動時にライブラリをプレロードするための設定である。こちらは,EXTENSIONとは異なり制御ファイルやインストールスクリプトは必要ない。サーバー起動時,postmasterプロセスがServerLoopに到達するより前の時点で共有オブジェクトがロードされる。サーバー起動時にのみ操作可能な処理を行なうような拡張機能はこの設定を利用する。
参考 PostmasterMain() - on doxygen.postgresql.org
また,類似の設定があるので合わせてメモしておく。
設定 | 説明 | 設定場所例 |
---|---|---|
shared_preload_libraries | サーバー起動時にのみ有効な設定。ライブラリをプレロードする。 | postgresql.conf,pg_ctlオプション,PGOPTIONS |
session_preload_libraries | セッション起動時に有効な設定。ライブラリをプレロードする。スーパーユーザのみが指定可能。 | postgresql.conf,pg_ctlオプション,PGOPTIONS,ALTER ROLE |
local_preload_libraries | セッション起動時に有効な設定。ライブラリをプレロードする。任意のユーザーが指定可能。 ただし,ライブラリのパスは,$libidr/pluginsに限定されている。 データベース管理者がこのディレクトリに安全と判断されたライブラリを設置し,ユーザーが任意にロードできるような仕組みらしい。 | postgresql.conf,pg_ctlオプション,PGOPTIONS,ALTER ROLE |
サンプル
# pg_ctlの例 pg_ctl start -w -o "-c session_preload_libraries=myext" # PGOPTIONSの例 export PGOPTIONS="-c local_preload_libraries=myext" psql -U guest postgres # ALTER ROLEの例 ALTER ROLE guest SET session_preload_libraries = myext;
shared_preload_libraries指定有無による共有オブジェクトロードの確認 †
myext共有オブジェクトがロードされているかデバッガーで確認してみる。
postgresql.confに拡張機能を指定し,psqlで接続後にバックグラウンドプロセスにアタッチする。
# postgresql.conf shared_preload_libraries = 'myext'
shared_preload_librariesの指定がない場合
myextシンボルは確認できない。
(lldb) image lookup -r -n myext
shared_preload_librariesの指定をした場合
myextシンボルが確認できる。
(lldb) image lookup -r -n myext 2 matches found in /Users/guest/workspace/PostgresAnalysis/pg/lib/postgresql/myext.so: Address: myext.so[0x0000000000000f10] (myext.so.__TEXT.__text + 144) Summary: myext.so`myext at myext.c:28 Address: myext.so[0x0000000000000f00] (myext.so.__TEXT.__text + 128) Summary: myext.so`pg_finfo_myext at myext.c:24 (lldb)
サンプル shared.tar.gz
CREATE EXTENSIONを使わずにオブジェクトを登録する †
インストールスクリプトに記述されている内容をそのまま実行すれば類似のことが可能ではあるが,段階的な更新スクリプトの実行やEXTENSIONに関連するオブジェクト一式の紐付けが行われない。
例: 個別にインストールスクリプトの内容を実行した場合
CREATE OR REPLACE FUNCTION myext() RETURNS BOOL AS '$libdir/myext.so' LANGUAGE C STRICT;
pg_extensionに情報は登録されないため,DROP EXTENSION myext;コマンドで関連オブジェクト一式を削除するといった操作ができない。
参考 RemoveObjects() - on doxygen.postgresql.org
拡張の更新 †
拡張の再配置 †
参考リンク †
- extension.c - on doxygen.postgresql.org
- CreateExtension() - on doxygen.postgresql.org
- AlterExtension() - on doxygen.postgresql.org
- parse_extension_control_file() - on doxygen.postgresql.org
- 35.15. 関連するオブジェクトを拡張としてパッケージ化 - PostgreSQL 9.5.4文書
- 35.16. 拡張構築基盤 - PostgreSQL 9.5.4文書
- 18.11. クライアント接続デフォルト - PostgreSQL 9.5.4文書
- PosteSQL を拡張せよ! - https://www.sraoss.co.jp/event_seminar/2014/extend_postgres_20140618.pdf