0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Windows版PostgreSQLの拡張機能 pg_hint_plan をビルドする

Last updated at Posted at 2025-06-10

はじめに

PostgreSQLの拡張機能pg_hint_planは、実行計画を誘導するための特殊なコメントです。
詳細:pg_hint_planを用いて実行計画を制御する
ところがLinux版と違い、Windows版は公式にビルドしたものをリリースしていません。

別件のトラブルでpg_statsinfoについて調べていると、Windows版のpg_repackバイナリ配布しているという記事を見つけました。

SRA OSSとしてPostgreSQL拡張のWindowsバイナリ提供を開始しているが、今のところはサポート加入者のみで、一般配布は計画中とのこと。

SRA OSSで継続バイナリリリース

  • pg_hint_plan
  • pgaudit
  • pg_repack
  • pg_dbms_stats

※2025年06月時点ではまだ一般配布は行われていません。

じゃー自力でなんとかしてみようと考えたところです。

環境

  • Windos 11 Home 64bit
  • Visual Studio 2022(C++ によるデスクトップ開発) インストール済み
  • PostgreSQL 14.7

仕事の関係上、現時点では少し古めの PostgreSQL のバージョンを使用します。
PostgreSQL 14は、2021/09/30リリース

開発準備

pg_hint_planをビルドする上で今回 Visual Studio を使用します。

プロジェクトファイルを Github に置きました。
ただし、pg_hint_planのソースコードやpg_hint_plan.dllは含まれていませんがqueryjumble.objのみ含めました。

pg_hint_plan を取得

下記サイトから Source code(zip) を取得します。

筆者は、C:¥WorkSpace に展開しました。

queryjumble.objの取得

pg_hint_planをビルドには、「queryjumble.obj」ファイルが必要となります。
PostgreSQLをソースコードからビルドして取得します。

PostgreSQLインストール配下にobjフォルダを作成し、「queryjumble.obj」ファイルをコピーします。
今回の筆者の場合、PostgreSQLインストールフォルダは「C:¥Program Files¥PostgreSQL¥14」になります。

プロジェクト作成

C++でダイナミックリンクライブラリ(DLL)を選択します。
image.png
pg_hint_plan でプロジェクトを作成します。

image.png

プロジェクトを一旦閉じます。
同じ拡張機能のpg_repackに倣って、pg_hint_planフォルダからmsvcに変更したらプロジェクトを開き直します。

image.png

ファイルの追加の既存ファイル削除

上位フォルダから「pg_hint_plan.c」ファイルを追加します。
あと、既存ファイルの下記ファイルは不要なので削除します。

  • dllmain.cpp
  • pch.cpp
  • framework.h
  • pch.h

定義ファイルの作成

モジュール定義ファイル(.def)を作成して、msvcフォルダに配置します。

pg_hint_plan.def
LIBRARY pg_hint_plan
EXPORTS
    _PG_init
    _PG_fini

最初は、元ソースコードにPGDLLEXPORTを追記する方法でビルドしていたのですが、モジュール定義を使用してエクスポート指定すると、元ソースコードの変更せずビルド出来ることが分かりました。

/* Module callbacks */
PGDLLEXPORT void _PG_init(void);
PGDLLEXPORT void _PG_fini(void);

プロパティ設定

pg_hint_planでプロジェクトのプロパティにて、構成を「すべての構成」にします。

プリプロセッサ定義

C/C++の「プリプロセッサ」を選択、「プリプロセッサの定義」に「WIN32」を追加します。
PostgreSQLは「WINDLL」を同等のものとして認識しないため、Win32プラットフォームの場合は明示的に指定する必要があります。
※2014年の記事を参考にしているため、もう改善されている可能性があります。
image.png

C/C++の例外を無効化

C/C++の「コード生成」を選択、「C/C++の例外を有効にする」で「いいえ」にします。
image.png

コンパイルをC言語へ

C/C++の「詳細設定」を選択、「コンパイル言語の選択」で「C コードとしてコンパイル(/TC)」にします。
次に「指定の警告を無視する」を「4703;4996」をセットします。
image.png

  • 4703…未使用要素
  • 4996…非推奨関数

4996の非推奨関数の警告を無視したくない場合、#define _CRT_SECURE_NO_WARNINGSの指定やstrcpyの代わりにstrcpy_sへ書き換える方法もあります。

プリコンパイル済みヘッダーを使用しない

C/C++の「プリコンパイル済みヘッダー」を選択、「プリコンパイル済みヘッダー」で「プリコンパイル済みヘッダーを使用しない」にします。
image.png

マニフェストを生成しない

リンカーの「マニフェスト ファイル」を選択、「マニフェストの生成」で「いいえ (/MANIFEST:NO)」にします。
image.png

追加の依存関係

リンカーの「入力」を選択、「追加の依存ファイル」に「postgres.lib」と「queryjumble.obj」を追加します。
image.png
「親またはプロジェクトの既定値から継承」のチェックは外さないでください。
単純な拡張機能では必要ありませんが、サーバー関数にリンクする場合は必要になります。
image.png

モジュール定義関係

モジュール定義ファイル「pg_hint_plan.def」をセットします。
image.png

インクルードディレクトリ

C/C++の「全般」を選択、「追加のインクルードディレクトリ」で、PostgreSQLインストール配下の4つのフォルダを追加します。

  • include\server\port\win32_msvc
  • include\server\port\win32
  • include\server
  • include

image.png

今回の筆者の場合、PostgreSQLインストールフォルダは「C:\Program Files\PostgreSQL\14」になります。
image.png

ライブラリディレクトリ

リンカーの「全般」を選択、「追加のライブラリ ディレクトリ」で、PostgreSQLインストール配下のlibフォルダとobjフォルダを追加します。
次に「ライブラリ依存関係のリンク」を「いいえ」にします。
image.png

objフォルダは前述した「queryjumble.obj」用です。

image.png

プロジェクトの構築

プルダウンメニューから「Relese」を選択します。
image.png

プログラム

構成

image.png

msvcフォルダの中身、x64¥Releseフォルダに「pg_hint_plan.dll」ファイルが生成される。
image.png

修正内容

関数_PG_initと _PG_fini の宣言と定義の両方にPGDLLEXPORTマクロを追加するだけで済みました。
ただし、関数 _PG_init はpostgresが提供するインクルードファイル server\plpgsql.h で宣言されているため、コンパイラエラーpg_hint_plan.c(667,19): error C2375: '_PG_init': redefinition; different linkageを回避するために、関数の宣言にも同様の修正が必要です
https://github.com/ossc-db/pg_hint_plan/issues/201

最初はissues 201に従って元ソースコードを修正する方法だったため、修正方法を記載していたのですがモジュール定義ファイルの方法にしたため、元ソースコードを修正する必要がなくなりました。

ビルド時の不明点

警告として下記メッセージが出たのですが、Grep検索しても関数が見つからないです。
もし何か知っている方がいたら、コメントをお願いします。

'pg_hint_plan_standard_join_search' の関数定義が見つかりません。
'pg_hint_plan_make_join_rel' の関数定義が見つかりません。
'pg_hint_plan_join_search_one_level' の関数定義が見つかりません。

導入

配置

今回の筆者の場合、PostgreSQLインストールフォルダは「C:¥Program Files¥PostgreSQL¥14」になりますので、下記に配置します。

ファイル 配置先
pg_hint_plan.dll lib
pg_hint_plan.control share\extension
pg_hint_plan--1.3.0.sql 同上
pg_hint_plan--1.3.0--1.3.1.sql 同上
pg_hint_plan--1.3.1--1.3.2.sql 同上
pg_hint_plan--1.3.2--1.3.3.sql 同上
pg_hint_plan--1.3.3--1.3.4.sql 同上
pg_hint_plan--1.3.4--1.3.5.sql 同上
pg_hint_plan--1.3.5--1.3.6.sql 同上
pg_hint_plan--1.3.6--1.3.7.sql 同上
pg_hint_plan--1.3.7--1.3.8.sql 同上
pg_hint_plan--1.3.8--1.3.9.sql 同上
pg_hint_plan--1.3.9--1.3.10.sql 同上
pg_hint_plan--1.3.10--1.4.sql 同上
pg_hint_plan--1.4.1--1.4.2.sql 同上
pg_hint_plan--1.4--1.4.1.sql 同上
pg_hint_plan--1.4.2--1.4.3.sql 同上

こんなにsqlファイルが必要なのか、1つでいいのではないかと思ったのですが、pg_hint_plan.control に書かれたdefault_version(例: 1.4.3)のように、バージョン3桁のみのファイル(例 1.4.3.sql)が無い場合、バージョン3桁のみファイルから指定バージョンまでアップデートしていく仕組みになっているようです。

postgres.confの修正

設定ファイルにpg_hint_planの事前ロード用の設定を追加します。

postgres.conf
shared_preload_libraries = 'pg_hint_plan'

ファイル編集後、PostgreSQLサーバを起動、または再起動してください。
※shared_preload_librariesには、カンマ区切りで複数のモジュールが登録できます。

PostgreSQLに拡張機能の登録

CREATE EXTENSION pg_hint_plan;

登録の確認

SELECT extname, extversion
FROM pg_extension
WHERE extname = 'pg_hint_plan';
extname extversion
pg_hint_plan 1.4.3

検証

検証データ作成

ChatGPTで検証するSQLを作成してもらいました。

基本
CREATE TABLE test_table (
    id serial PRIMARY KEY,
    name text,
    created_at timestamp
);

INSERT INTO test_table (name, created_at)
SELECT
    'name_' || s,
    now() - (s || ' days')::interval
FROM generate_series(1, 10000) AS s;
応用
CREATE TABLE t1 (id int, val text);
CREATE TABLE t2 (id int, val text);

INSERT INTO t1 SELECT i, 'A' FROM generate_series(1, 1000) i;
INSERT INTO t2 SELECT i, 'B' FROM generate_series(1, 100000) i;

実行プラン基本の確認

ヒント句なしの通常プランの場合、インデックスが効いています。

EXPLAIN SELECT * FROM test_table WHERE id = 1234;

-- 結果
QUERY PLAN
Index Scan using test_table_pkey on test_table  (cost=0.29..8.30 rows=1 width=21)
  Index Cond: (id = 1234)
EXPLAIN SELECT * FROM test_table WHERE id = 1234;

-- 結果
QUERY PLAN
Index Scan using test_table_pkey on test_table  (cost=0.29..8.30 rows=1 width=21)
  Index Cond: (id = 1234)

ヒント句でSeqScanを指定した場合、インデックスが効かなくなっています。

EXPLAIN SELECT /*+ SeqScan(test_table) */ * FROM test_table WHERE id = 1234;

-- 結果
QUERY PLAN
Seq Scan on test_table  (cost=0.00..189.00 rows=1 width=21)
  Filter: (id = 1234)

実行プラン応用の確認

ヒント句なしの通常プランの場合、Hash JoinMerge Joinになることが多いです。

EXPLAIN SELECT * FROM t1 JOIN t2 ON t1.id = t2.id;

-- 結果
QUERY PLAN
Hash Join  (cost=27.50..1855.50 rows=1000 width=12)
  Hash Cond: (t2.id = t1.id)
  ->  Seq Scan on t2  (cost=0.00..1443.00 rows=100000 width=6)
  ->  Hash  (cost=15.00..15.00 rows=1000 width=6)
        ->  Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=6)

ヒント句でNestLoop(t1 t2)を強制した場合

EXPLAIN SELECT /*+ NestLoop(t1 t2) Leading(t1 t2) */ * 
FROM t1 JOIN t2 ON t1.id = t2.id;

-- 結果
QUERY PLAN
Nested Loop  (cost=0.00..1501460.50 rows=1000 width=12)
  Join Filter: (t1.id = t2.id)
  ->  Seq Scan on t2  (cost=0.00..1443.00 rows=100000 width=6)
  ->  Materialize  (cost=0.00..20.00 rows=1000 width=6)
        ->  Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=6)

最後に

なんとか、pg_hint_planをビルドすることが出来ました。このissue 201がなかったら諦めてたと思います。

今回はPostgreSQL 14でしたが、バージョンごとに導入物を用意する必要があるようです。
SRA OSSで一般配布をして頂けるのがいいですけどね。

Windows版PostgreSQLの拡張機能もサポートがもっと充実するようになればいいのにな。

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?