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

# はじめに
PostgreSQLの拡張機能`pgAudit`は、監査ログ(audit logging)を追加するため機能です。
別件のトラブルで`pg_statsinfo`について調べていると、Windows版の`pg_repack`バイナリ配布しているという記事を見つけました。

* [PostgreSQL拡張のWindowsバイナリ配布はじめました(半年前から)](https://www.docswell.com/s/harukat/Z4QDPL-2024-07-04-171804)
* [第47回 PostgreSQLアンカンファレンス@オンライン](https://posfie.com/@nuko_yokohama/p/zH6v1xr?page=3)

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

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

* [pg_hint_plan](https://qiita.com/drafts/333423bd631430f5f905) 対応済
* 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リリース

# 開発準備
`pgaudit`をビルドする上で今回 Visual Studio を使用します。

https://github.com/yaju/pgaudit-1.6.3-msvc

最初に`pgAudit`でプロジェクトを作成しましたが、他のdllと合わせ`pgaudit`と英小文字にしました。一部のキャプチャには`pgAudit`が残っていますがご了承ください。

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

https://github.com/pgaudit/pgaudit/releases/tag/1.6.3

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

## プロジェクト作成
C++でダイナミックリンクライブラリ(DLL)を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/a4b574e3-1cf7-4cf9-bd1a-6c6aea2153dc.png)
`pgaudit` でプロジェクトを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/572de115-f6b6-4ea7-96fd-5a18fd2f6ab4.png)

プロジェクトを一旦閉じます。
同じ拡張機能の`pg_repack`に倣って、`pgaudit`フォルダから`msvc`に変更したらプロジェクトを開き直します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/4b603e7a-9aa9-42b3-af27-98e27891c664.png)

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

* dllmain.cpp
* pch.cpp
* framework.h
* pch.h

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

```text:pgaudit.def
LIBRARY pgaudit
EXPORTS
  _PG_init
  pgaudit_ddl_command_end
  pgaudit_sql_drop
```

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/7a691b8e-1dc6-4ac7-9ff3-824464045714.png)

### C/C++の例外を無効化
C/C++の「コード生成」を選択、「C/C++の例外を有効にする」で「いいえ」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/871c7642-ff6f-4314-ab76-f994689b00ed.png)

### コンパイルをC言語へ
C/C++の「詳細設定」を選択、「コンパイル言語の選択」で「C コードとしてコンパイル(/TC)」にします。
次に「指定の警告を無視する」を「4703;4996」をセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/eeafa298-46a5-46ab-99fa-21cd10a29e12.png)

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

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

https://theolizer.com/visual_cpp/visual-c%E3%81%AEc4996%E8%AD%A6%E5%91%8A%E3%81%B8%E3%81%AE%E5%AF%BE%E5%87%A6/

### プリコンパイル済みヘッダーを使用しない
C/C++の「プリコンパイル済みヘッダー」を選択、「プリコンパイル済みヘッダー」で「プリコンパイル済みヘッダーを使用しない」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/9bb440ee-51d4-444a-a3f0-0201a7a96930.png)

### マニフェストを生成しない
リンカーの「マニフェスト ファイル」を選択、「マニフェストの生成」で「いいえ (/MANIFEST:NO)」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/e6b49561-d4f1-4bdf-9985-998e25b8d826.png)

### 追加の依存関係
リンカーの「入力」を選択、「追加の依存ファイル」に「postgres.lib」を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/a027565a-c2b9-42bb-b9db-8b8286d50fc3.png)
「親またはプロジェクトの既定値から継承」のチェックは外さないでください。
単純な拡張機能では必要ありませんが、サーバー関数にリンクする場合は必要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/27ac4e6c-76ac-4caf-8833-c1081ee84719.png)

### モジュール定義関係
モジュール定義ファイル「pgaudit.def」をセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/59466eb0-5ddc-49c5-88c4-96ef684393fb.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/37c99295-2b2a-447c-b01b-a5ba35f2791d.png)

今回の筆者の場合、PostgreSQLインストールフォルダは「C:\Program Files\PostgreSQL\14」になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/8fd8a6a5-2c5d-483f-9ff1-5a5f80f2cb60.png)

### ライブラリディレクトリ
リンカーの「全般」を選択、「追加のライブラリ ディレクトリ」で、PostgreSQLインストール配下の`lib`フォルダを追加します。
次に「ライブラリ依存関係のリンク」を「いいえ」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/82fa04b8-1cdb-49f5-907d-3a8c4b92b5b2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/30ab599d-89ef-4bbb-a4b6-62132f79908f.png)

### プロジェクトの構築
プルダウンメニューから「Relese」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/4e18549e-f48e-4ca1-b070-9e71797903f8.png)

# プログラム
## 構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/43946763-329a-4d89-ab22-e3c71a46cffa.png)

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

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

|ファイル|配置先|
|:--|:--|
|pgaudit.dll|lib|
|pgaudit.control|share\extension|
|pgaudit--1.6.3.sql|同上|

`pgaudit.control` に書かれた`default_version`(例: 1.6.3）のように、バージョン3桁のみのファイル(例 1.6.3.sql)があったため、他のsqlは含めませんでした。

:::note info
他バージョンでバージョン3桁のsqlファイルがない場合、アップグレードさせるために他のsqlを含めてください。
:::

## postgres.confの修正
設定ファイルに`pgaudit`の事前ロード用の設定を追加します。

```ini:postgres.conf
shared_preload_libraries = 'pgaudit'
```

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

```ini:postgres.conf
shared_preload_libraries = 'pg_hint_plan, pgaudit'
```

## PostgreSQLに拡張機能の登録
```sql
CREATE EXTENSION pgaudit;
```

登録の確認

```sql
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'pgaudit';
```

|extname|extversion|
|:--|:--|
|pgaudit|1.6.3|

## 検証
### postgres.conf追加設定
```int:postgres.conf
#------------------------------------------------------------------------------
# pgaudit
#------------------------------------------------------------------------------
pgaudit.log = 'ALL'
pgaudit.log_catalog = off
pgaudit.log_level = log
pgaudit.log_parameter = on
pgaudit.log_relation = on
pgaudit.log_statement_once = off
pgaudit.role = 'auditmrole'
```
### 検証データ作成
下記サイトを参考にしました。

https://www.sraoss.co.jp/tech-blog/pgsql/pgaudit/

#### auditmroleマスターロールの作成
オブジェクト監査ロギングの対象ロールとして auditmrole マスターロールを作成します。

```sql
CREATE ROLE auditmrole;
```

#### マスターロールへの権限の付与
auditmrole マスターロールに table1 テーブルの t1 カラムへの SELECT の権限を与えます。

```sql
CREATE TABLE table1 (id int, t1 text);

INSERT INTO table1 VALUES (1, 'aaa'),(2, 'bbb');

SELECT * FROM table1;

GRANT SELECT (t1) on table1 to auditmrole;
```

#### マスターロールでの種々の操作と監査ログ
t1 カラムを含む SELECT と t1 カラムを含まない SELECT を実施します。

```sql:検証
SELECT * FROM table1;

SELECT id FROM table1;

SELECT id,t1 FROM table1;
```

auditmrole ロールに与えられた権限の通り、t1 カラムを含む SELECT は記録(`AUDIT_TYPE`が`OBJECT`)されましたが、t1 カラムを含まないidカラムのみの SELECT 文は記録されませんでした。このようにオブジェクト監査ロギングでは auditmrole ロールに与えられた権限のみが PostgreSQL ログに記録されますので詳細なレベルで取得する情報を設定できます。

```text:ログ
2025-06-22 23:20:29.253 JST [16948] LOG:  AUDIT: OBJECT,17,1,READ,SELECT,TABLE,test.table1,SELECT * FROM table1,<none>
2025-06-22 23:20:29.253 JST [16948] LOG:  AUDIT: SESSION,17,1,READ,SELECT,TABLE,test.table1,SELECT * FROM table1,<none>
2025-06-22 23:20:34.568 JST [16948] LOG:  AUDIT: SESSION,18,1,READ,SELECT,TABLE,test.table1,SELECT id FROM table1,<none>
2025-06-22 23:20:38.236 JST [16948] LOG:  AUDIT: OBJECT,19,1,READ,SELECT,TABLE,test.table1,"SELECT id,t1 FROM table1",<none>
2025-06-22 23:20:38.236 JST [16948] LOG:  AUDIT: SESSION,19,1,READ,SELECT,TABLE,test.table1,"SELECT id,t1 FROM table1",<none>
```

# 最後に
`pg_hint_plan`の経験があったので、比較的スムーズにビルドすることが出来ました。
簡易的なテストしかしていませんが、問題ないかと思います。