Windows版PostgreSQLでpg_repackを使ってみたかった

# はじめに
サーバーリプレースの検証をしている。
Windows Server 2008R2 + Oracle11g から、 Windows Server 2016 + PostgreSQL 9.6 にすることになった。Oracleは生き残りをかけて、ライセンス料金を引き上げるという戦略ミスをしていると思う。

PostgreSQLは追記型アーキテクチャを採用しているので、定期的に VACUUM FULL + REINDEX をするなどが別途必要となる。

VACUUM の運用を調べていく中で、最小限のロックでテーブルの再編成をしてくれる「pg_repack」を見つけた。
[運用で役立つツールpg_repackのご紹介](https://qiita.com/bwtakacy/items/3be7a3e1540ccef7c9e3)

しかし、Windows上で「pg_repack」をビルドすることが出来たものの、実際に使用してみると障害エラー 0x000000C5 が発生して動作しなかった。
なので、ブログを書いて情報を募ろうと思ったのでした。

【2018/03/12追記】
stackoverflow の回答を見つけたが、根本解決はまだかな。
[Call of statically link function crash everytimes on windows 8/10 but not 7](https://stackoverflow.com/questions/45819855/call-of-statically-link-function-crash-everytimes-on-windows-8-10-but-not-7/45827816
)
[Dynamic linking to a .exe instead of .dll cause crash on windows 10](https://stackoverflow.com/questions/45957125/dynamic-linking-to-a-exe-instead-of-dll-cause-crash-on-windows-10)

日本語訳を意訳する限りでは、Windows 10のシステム上の問題であり、postgresを静的ライブラリーにしてpg_repackをビルドすれば解決できるけど、拡張機能であるpg_repackのためにpostgres本体をそうしないと出来ないのでは本末転倒だよね。

【2018/03/18追記】
上記の質問者の方のgithubを見つけ、pg_repack.2013.sln があったので、Visual Studio Express 2013 for Windows Desktop 日本語版を[ダウンロード](http://download.microsoft.com/download/E/F/2/EF21D164-8A66-4F08-B71F-26702B180E11/VS2013_RTM_DskExp_JPN.iso)してビルドしました。
https://github.com/BertrandAreal/pg_repack/tree/master/msvc

Windows 10の障害エラーになるのは変わらないです。時系列から言えばフォークした後にWindows 7では動作したけど、Windows 10では動作しないから質問している。

>But I'm a little annoyed that the project pg_repack need a references on postgres.exe. I solve the issue by generating a static library of postgres in order to link with pg_repack. It works well (on windows 7/8/10), but I don't know if thete is a easier solution. Build a static library for each version of postgres is a huge work (relativly to the original purpose of build the tiny project of pg_repack)

静的ライブラリーにしたpostgresを生成して、pg_repackにリンクすればWindows 7/8/10で動作するようです。
postgres.exe内のモジュールを使う際に、Windows 10だと正しいアドレスにならないので障害エラーになる。静的ライブラリーにすればそれが解消されるのかな。
postgres.exeをバイナリーエディタでPEヘッダーを書き換えてDLLのフラグを立ててみたけど、自分の環境では駄目した。

【2018/03/21追記】
[VC++でDLLから.libファイルを作成するには？](https://ameblo.jp/nana-2007-july/entry-10101317623.html)を参考にpostgres.exe から postgres2.libを作成して、postgres.lib の代わりに postgres2.lib をリンクしたみたけど結果は変わらず。

1. dumpbin /exports postgres.exe > postgres.txt
2. postgres.txt から postgres.def を作成
3. lib /DEF:postgres.def /MACHINE:X86 /NAME:postgres.exe /out:postgres2.lib

``` c:pgut.c
void
pgut_init(int argc, char **argv)
{
	if (PROGRAM_NAME == NULL)
	{
		PROGRAM_NAME = get_progname(argv[0]);
		// set_pglocale_pgservice(argv[0], "pgscripts");
```
set_pglocale_pgservice をコメントアウトすれば、help と version コマンドは動作するようになるので、ここが悪さしているのは明白なんですけどね。
このままだと、pg_repack -d postgres で障害エラーになってしまう。

【2018/03/26追記】
PostgreSQLのメーリングリストで質問をしています。
[[pgsql-jp: 41977] Windows10上でPostgreSQL用の拡張機能(EXE)を動かすと障害エラーになる](https://ml.postgresql.jp/pipermail/pgsql-jp/2018-March/016933.html)

# 環境
## 会社
* Windows Server 2016(64bit) + PostgreSQL 9.6.3(64bit)
* Windows Server 2012R2(64bit) + PostgreSQL 9.6.3(64bit)
* Windows 7 Professional(64bit) + PostgreSQL 9.6.3(64bit)

Visual Studio 2015 Professional
Visual Studio 2010 Professional
Visual Studio 2013 Express Edition

## 自宅
* Windows 10 Home(64bit) + PostgreSQL 9.6.7(32bit)

Visual Studio 2015 community
Visual Studio 2010 Ultimate


# ビルド方法
[pg_repack - github](https://github.com/reorg/pg_repack)
>Windows OS上ではMicrosoft Visual C++ 2010を利用してビルドすることができます。 msvc ディレクトリ配下にプロジェクトファイルがあります。
https://github.com/reorg/pg_repack/blob/master/doc/pg_repack_jp.rst

msvc ディレクトリのコミットが5年前ですが、何事も無く動くと期待したんだけど、ビルドエラーになるんですよね。

## ビルド準備
Visual Studio 2015 Professional で説明していきます。
ビルドが通るように以下の手順で変更していきます。

2018/03/11時点で最新版のpg_repack 1.4.2を[ダウンロード](https://github.com/reorg/pg_repack/releases)します。
解凍したpg_repackを\<PostgreSQLのインストール先>のcontribディレクトリに移動します。
**※必ずしもcontribディレクトリである必要はありません。**

1. Visual Studioで、msvcフォルダにある pg_repack.2010.sln を開きます。
※下図は変更後のプラットフォームです。初期は「Windows7.1SDK」です。
![ソリューションエクスプローラー.png](https://qiita-image-store.s3.amazonaws.com/0/14053/f4347013-93cb-1b94-f587-3a5a0afe39c1.png)

2. ソリューション構成を「8.3」から「9.0」に変更します。
![ソリューション構成.png](https://qiita-image-store.s3.amazonaws.com/0/14053/19b0e204-b32b-07c6-8dab-6d57d78617b6.png)

3. bin.2010のプロパティページ画面を開いて、構成プロパティの[全般]にて[プラットフォーム ツールセット]を「Visual Studio 2010(v100)」または「Visual Studio 2013(v120)」にします。
理由は後述します。 
![bin全般.png](https://qiita-image-store.s3.amazonaws.com/0/14053/cd456fdc-1fba-c40b-7167-c0e105d000c2.png)

4. 構成プロパティの[VC++ ディレクトリ]の[インクルードディレクトリ]が「9.0」となっているところを「9.6」に変更します。
![インクルードディレクトリ.png](https://qiita-image-store.s3.amazonaws.com/0/14053/dbd1a306-bc5d-1d51-1b8a-f804d7fcfbf2.png)

5. [ライブラリディレクトリ]でも「9.0」から「9.6」に変更します。
![ライブラリディレクトリ.png](https://qiita-image-store.s3.amazonaws.com/0/14053/6480e2eb-4ac5-680d-dcef-0e00927499cb.png)

6. 構成プロパティの[リンカー]の[入力]の[追加の依存ファイル]で「postgres.lib」を先頭に追加します。
![追加の依存ファイル.png](https://qiita-image-store.s3.amazonaws.com/0/14053/d26062c5-610b-cc87-602d-0dbfbdad5463.png)

7. lib.2010のプロパティページ画面を開いてbin.2010同様に[プラットフォーム ツールセット]を変更します。
![lib全般.png](https://qiita-image-store.s3.amazonaws.com/0/14053/d8f327ba-eeb6-34cf-0ac4-d2e2097b601f.png)

8. bin.2010同様に構成プロパティの[VC++ ディレクトリ]のインクルードディレクトリとライブラリディレクトリも「9.0」となっているところを「9.6」に変更します。

## ビルドエラー対応
### lib.2010プロジェクト
ソース ファイルを開けません。'..\lib\pgut\pgut-be.c': No such file or directory
**対応**
pgut-be.c は、1.4.xから存在しなくなったので、プロジェクトから除外する

### bin.2010プロジェクト
プラットフォームがVisual Studio 2010以下の場合、下記エラーになります。
構文エラー : '.' pg_repack\bin\pgut\pgut-fe.c

これはC言語(C99)の新機能である指示付きの初期化子（designated initializer）が対応していないためです。コメントアウトして書き換えてください。
**※Visual Studio 2013では、ビルドエラーになりません。**

``` c:pgut-fe.c
/*
worker_conns workers   = {
	.num_workers     = 0,
	.conns           = NULL
};
*/
worker_conns workers = { 0, 0, NULL };
```

## リンカエラー対応
コンパイル手順で対応済みです。
### 依存ファイル不足
構成プロパティの[リンカー]の[入力]の[追加の依存ファイル]で「postgres.lib」が無いまたは先頭に追加しないと下記エラーがでました。

```
外部シンボル "set_pglocale_pgservice" は未解決です。
外部シンボル "pgwin32_select" は未解決です。
外部シンボル "__imp__dclass" は未解決です。
```

### stdio問題
プラットフォームがVisual Studio 2015(v140)以降の場合、下記エラーになります。

```
外部シンボル "__imp___iob_func" は未解決です。
```
VS2015からstdio問題があるようです。

* [___iob_func リンクエラー](https://tenshil.blogspot.jp/2017/06/visualstudio-2015-error-lnk2019.html)
* [OpenMVGをVisualStudio2015でビルド](codecogs.com/eqneditor)

### 32bit版
個人PCでは、32bit版で試しました。一応つまずいたところを書いてみます。

構成プロパティの[リンカー]の[入力]の[追加の依存ファイル]で「libintl-8.lib」が見つからないので削除して、「postgres.lib」を先頭に追加します。

最初「libintl-8.lib」が見つからないため、DLLからlibを作成したのですが別のエラーになったので「libintl-8.lib」の代わりに「postgres.lib」にしたらエラーが消えました。
[VC++でDLLから.libファイルを作成するには？](https://ameblo.jp/nana-2007-july/entry-10101317623.html)

```
dumpbin /exports libintl-8.dll > libintl-8.txt
lib /DEF:libintl-8.def /MACHINE:X86 /out:libintl-8.lib
```

# 登録
ビルドが正常終了となり、pg_repack.exeとpg_repack.dllとpg_repack.libが生成できました。

pg_repackは、PostgreSQLの拡張モジュールなので、Create ExtensionでPostgreSQLに登録させせる必要があります。
msvc版は、MAKEFILEで自動で置換してくれるところまでは対応していないので、登録用に準備する必要があります。

## 準備
### pg_repack.control
libディレクトリにある pg_repack.control.in ファイルを名称変更して「pg_repack.control」します。
内容の変更箇所は、`REPACK_VERSION`をバージョン番号(1.4.2)に書き換えるだけです。

``` bat:pg_repack.control
# pg_repack extension
comment = 'Reorganize tables in PostgreSQL databases with minimal locks'
default_version = '1.4.2'
module_pathname = '$libdir/pg_repack'
relocatable = false
```
### pg_repack.sql
libディレクトリにある pg_repack.sql.in ファイルをを名称変更して「pg_repack--1.4.2.sql」します。
内容の変更箇所は、`REPACK_VERSION`をバージョン番号(1.4.2)に書き換えるだけです。

``` bat:pg_repack--1.4.2.sql
CREATE FUNCTION repack.version_sql() RETURNS text AS
$$SELECT 'pg_repack 1.4.2'::text$$
LANGUAGE SQL IMMUTABLE STRICT;
```

## インストール
下記の表を元にインストールに必要なファイルを手動でコピーします。

|対象ファイル名|コピー先|
|:--|:--|
|pg_repack.exe|\<PostgreSQLのインストール先>\bin|
|pg_repack.dll|\<PostgreSQLのインストール先>\lib|
|pg_repack.lib|\<PostgreSQLのインストール先>\lib|
|pg_repack.control と pg_repack--1.4.2.sql|\<PostgreSQLのインストール先>\share\extension|

## pg_repackの登録
PostgreSQLへのpg_bigmの登録はLinux環境と同じです。
公式のドキュメント [pg_repack インストール](https://github.com/reorg/pg_repack/blob/master/doc/pg_repack_jp.rst#id5)

```
psql -c "CREATE EXTENSION pg_repack" -d your_database
=# CREATE EXTENSION pg_repack;
CREATE EXTENSION
```

# 動作
Windows Server 2016 と Windows 10上で先に簡単なコマンドを試してみましたが、障害エラー 0x000000C5 が発生して動作できませんでした。

```
pg_repack --help
pg_repack --version
```

原因を探るために、pg_repack.c をHello worldだけにして見ましたが結果は変わらず。どうも、postgres.exe を動かそうとして障害エラーになるようです。

Windows 7 とWindows Server 2012R2 上では、help と version コマンドは動作したので、これはいけるかもと思って下記サイトを参考に肥大化したテーブルを作成して、VACUUMをしてもサイズが変わらないことを確認して、期待してWindows Server 2012R2 上で実行しました。
[運用で役立つツールpg_repackのご紹介](https://qiita.com/bwtakacy/items/3be7a3e1540ccef7c9e3)

```
pg_repack -d postgres -t tbl
```

**障害エラー 0x000000C5 が発生して動作できませんでした。**

# 最後に
Windows ServerでもOracleからPostgreSQLに変更する企業が増えてくると思うので、Windowsでもpg_repackが使えるようになるといいんですけどね。

自分の知識ではこれ以上分からないので、Windows上でも動作できた方がおりましたら方法をご教示して頂けると助かります。

# 参照
* [PostgreSQLでWebサービスを運用するためにやっていること](http://techblog.lclco.com/entry/2016/01/27/032402)
* [pg_repack のインストール](http://a4dosanddos.hatenablog.com/entry/2016/05/06/121708)
* [PostgreSQL のテーブルを無停止で最適化する](http://everything-you-do-is-practice.blogspot.jp/2017/09/pgreorg-24365.html)
* [PostgreSQLの運用・監視にまつわるエトセトラ - Slideshare](https://www.slideshare.net/hadoopxnttdata/postgresql-operations-monitoring-etc)
* [Inside vacuum - 第一回PostgreSQLプレ勉強会](https://www.slideshare.net/masahikosawada98/inside-vacuum)
* [公式のWindows版PostgreSQLパッケージ用の拡張機能のビルドシステムの作り方](http://www.clear-code.com/blog/2015/5/11.html)
* [Compiling PostgreSQL extensions with Visual Studio on Windows](https://blog.2ndquadrant.com/compiling-postgresql-extensions-visual-studio-windows/)
* [C言語系/memos/gcc/関数の属性機能("\_\_attribute\_\_")](https://www.glamenv-septzen.net/view/577)
* [include無しにclassが書けない](https://okwave.jp/qa/q8764028.html)
* [VC++でDLLから.libファイルを作成するには？](https://ameblo.jp/nana-2007-july/entry-10101317623.html)
* [___iob_func リンクエラー](https://tenshil.blogspot.jp/2017/06/visualstudio-2015-error-lnk2019.html)
* [OpenMVGをVisualStudio2015でビルド](codecogs.com/eqneditor)
* [Windows版PostgreSQLでpg_bigmを使う方法(非公式)](https://qiita.com/sawada_masahiko/items/e7e2fb101e276580ccbf)
* [PostgreSQL 8.2以前のpostgres.libファイルの作成](http://dodonpa.la.coocan.jp/pg_c_func_extra.htm)