Windows版PostgreSQLの拡張機能を作ってみる

# はじめに
2018年に下記の記事を書きました。しかし、残念ながら Windows版の`pg_repack`を正しく構築することが出来ませんでした。

https://qiita.com/yaju/items/94b7b290eb1dbbc5d70f

別件のトラブルで`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バイナリ提供を開始しているが、今のところはサポート加入者のみで、一般配布は計画中とのこと。
※2025年05月時点ではまだ一般配布は行われていません。

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

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

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

# 開発準備
PostgreSQL拡張をC言語を使用して開発するのですが、Visual Studio で下準備が必要となります。

単純な`Hello World`の文字列を返すだけではつまらないので、フィボナッチ数を返す拡張機能を作成することにします。

Visual Studio用の拡張機能のビルド方法は[PostgreSQLのWiki](https://wiki.postgresql.org/wiki/Building_and_Installing_PostgreSQL_Extension_Modules)にまとまっていますが、英語の上に古いのは否めません。
今回、分かりやすく書いてみました。

参照記事

https://www.enterprisedb.com/blog/compiling-postgresql-extensions-visual-studio-windows

https://zenn.dev/forcia_tech/articles/202310_nagao_cfunction



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

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

### プリプロセッサ定義
C/C++の「プリプロセッサ」を選択、「プリプロセッサの定義」に「WIN32」を追加します。
PostgreSQLは「WINDLL」を同等のものとして認識しないため、Win32プラットフォームの場合は明示的に指定する必要があります。
※2014年の記事を参考にしているため、もう改善されている可能性があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/e949f98f-0674-43d5-9f7f-7c8cfc3fe1a1.png)

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

### コンパイルをC言語へ
C/C++の「詳細設定」を選択、「コンパイル言語の選択」で「C コードとしてコンパイル(/TC)」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/c6a2c5a6-aa39-42f2-84fa-38cbb3f9c588.png)

### プリコンパイル済みヘッダーを使用しない
C/C++の「プリコンパイル済みヘッダー」を選択、「プリコンパイル済みヘッダー」で「プリコンパイル済みヘッダーを使用しない」にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/f8b30f2c-3c17-45e3-bf82-5c5094c1f9e3.png)

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

### 追加の依存関係
リンカーの「入力」を選択、「追加の依存ファイル」に「postgres.lib」を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/574c674c-406a-4abc-81d8-431f72171d6a.png)
「親またはプロジェクトの既定値から継承」のチェックは外さないでください。
単純な拡張機能では必要ありませんが、サーバー関数にリンクする場合は必要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/47f235ac-ee77-4944-b50b-154a7f5e0fda.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/e344edac-4405-4853-a74f-9ca49c823fc8.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/40a10fe5-0d6f-418c-898c-c5dc9755f641.png)

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

### 既存ファイルの削除
既存ファイルの「dllmain.cpp」と「pch.cpp」は不要なので削除します。

# プログラム
## 構成
```
pg_fibonacci/
├── pg_fibonacci.c
├── pg_fibonacci.control
├── pg_fibonacci--1.0.sql
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/211af8c1-1e3c-4dd7-9b80-ac8f09a242e4.png)

## C言語ソース pg_fibonacci.c 
`pg_fibonacci.c`を追加します。

```c:pg_fibonacci.c
#include "postgres.h"
#include "fmgr.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

#ifdef _WIN32
PGDLLEXPORT Datum fibonacci(PG_FUNCTION_ARGS);
#endif

PG_FUNCTION_INFO_V1(fibonacci);

Datum
fibonacci(PG_FUNCTION_ARGS)
{
    int32 n = PG_GETARG_INT32(0);

    if (n < 0)
        ereport(ERROR, (errmsg("Negative argument not allowed")));

    int64 a = 0, b = 1, temp;
    for (int i = 0; i < n; i++) {
        temp = a + b;
        a = b;
        b = temp;
    }

    PG_RETURN_INT64(a);
}
```

## コントロールファイル pg_fibonacci.control
`pg_fibonacci.control`を追加、プロパティにて「ビルドから除外」で「はい」にします。

```ini:pg_fibonacci.control
comment = 'Fibonacci function extension'
default_version = '1.0'
module_pathname = '$libdir/pg_fibonacci'
relocatable = true
```

## SQL定義ファイル pg_fibonacci--1.0.sql
`pg_fibonacci--1.0.sql`を追加、プロパティにて「ビルドから除外」で「はい」にします。

```sql:pg_fibonacci--1.0.sql
CREATE FUNCTION fibonacci(integer) RETURNS bigint
AS 'MODULE_PATHNAME', 'fibonacci'
LANGUAGE C STRICT;
```

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

|ファイル|配置先|
|:--|:--|
|pg_fibonacci.dll|C:\Program Files\PostgreSQL\14\lib\ |
|pg_fibonacci.control|C:\Program Files\PostgreSQL\14\share\extension\ |
|pg_fibonacci--1.0.sql|同上

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

### 嵌まったこと
WindowsではC言語ソース内で下記の定義をしないと`CREATE EXTENSION`にて`could not find function`エラーになります。
[Stuck getting sample C function working on Windows version - stackoverflow](https://stackoverflow.com/questions/39964233/stuck-getting-sample-c-function-working-on-windows-version)

```
#ifdef _WIN32
PGDLLEXPORT Datum fibonacci(PG_FUNCTION_ARGS);
#endif
```

:::note alert
SQL   : could not find function "fibonacci" in file "C:/Program Files/PostgreSQL/14/lib/pg_fibonacci.dll"
:::

## 検証
```sql
SELECT fibonacci(10); -- 55
SELECT fibonacci(50); -- 12586269025
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/33aa2fab-bc80-487d-9d8e-014efcfd148f.png)

# 最後に
今回の`pg_fibonacci.c`は、ChatGPTで生成してもらいました。
設定が面倒な方は、GitHubから取得して改良してもらえばいいでしょう。

https://github.com/yaju/pg_fibonacci