PG16：Set arbitrary GUC options during initdb

# はじめに

にゃーん。趣味でポスグレをやっている者だ。
今回はPostgreSQL 16のinitdbに追加されたGUC設定機能について書いてみました。

# 概要

|項目|内容|
|:--|:--|
|タイトル|[Set arbitrary GUC options during initdb](https://commitfest.postgresql.org/42/4143/)|
|Topic|System Administration|
|Last Modified|2023-03-22|
|ステータス|commited|
|概要|initdb時に生成されるGUCをオプションで指定可能になった。|

# 変更内容
PostgreSQLサーバアプリケーション``initdb``に任意のGUCを渡すオプションが追加されました。

```
-c name=value
--set name=value
```
複数のGUCを設定する場合には、このオプションを複数指定します。
``initdb --help``でもこのオプションが表示されます。

```bash
$ initdb --help
initdb initializes a PostgreSQL database cluster.

Usage:
  initdb [OPTION]... [DATADIR]
（略）
Less commonly used options:
  -c, --set NAME=VALUE      override default setting for server parameter
  -d, --debug               generate lots of debugging output
      --discard-caches      set debug_discard_caches=1
（略）
Report bugs to <pgsql-bugs@lists.postgresql.org>.
PostgreSQL home page: <https://www.postgresql.org/>
$
```

# 実行例

## オプション指定の例

### 任意のportを指定する
自分が自宅でレプリケーション検証をするときには、同一サーバ上で異なる``port``を指定したデータベースクラスタを複数生成して検証しています。
また、複数のバージョンを同一サーバ上で動かしているので、バージョンごとに``port``を変える必要があり、``initdb``後の``port``変更(``echo "port=xxxxx" >> $PGDATA/postgresql.conf`` )を毎回のようにやっていました。
※ちなみに自分のport付与規則は先頭2桁をメジャーバージョン番号、以降3桁は用途に応じた通番をつけてます）

PostgreSQL 16のinitdbでは以下のように実行することで、指定したportの値をpostgresql.confに書き込んでくれます。ちょっと便利。

```bash
$ ~/pgsql/master/bin/initdb -c "port=16001" -U postgres -D /tmp/pg16-1
（略）
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Tokyo
creating configuration files ... ok
（略）
Success. You can now start the database server using:
    /home/ec2-user/pgsql/master/bin/pg_ctl -D /tmp/pg16-1 -l logfile start

$
```
``-c``オプションで指定したGUCについては生成時のログには特に出力されないようですね。``-d``（debugオプション）付与時にも出力はされないようです。

さて、データベースクラスタが無事に生成されたので、データベースクラスタディレクトリ直下のpostgresql.conf内のportがデフォルトの5432ではなく、指定した16001になっているか確認します。

```bash
$ grep -n "port = " /tmp/pg16-1/postgresql.conf
64:port = 16001                         # (change requires restart)
$
```
きちんとpostgresql.conf内のportの値が16001になっています。
portを設定している行数は64なので、末尾に追加しているというわけではなく、postgresql.confファイルの生成途中に、``-c``オプションによる設定をしているようです。

### 複数のGUCを設定する例
今度は``port``（デフォルト5432）を16002に、``shared_buffers``（今の環境だとデフォルト128MB）の設定を65536kB（=64MB）に変更してみます。
複数のGUCを指定する場合には、``-c name=value``の組を複数指定します。

```bash
$ ~/pgsql/master/bin/initdb -c "port=16002" -c "shared_buffers=65536kB" -U postgres -D /tmp/pg16-2
（略）
creating directory /tmp/pg16-2 ... okcreating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Tokyo
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
（略）
Success. You can now start the database server using:
    /home/ec2-user/pgsql/master/bin/pg_ctl -D /tmp/pg16-2 -l logfile start

$
```
``shared_buffers``がシステムデフォルトの12MBで生成したかのように表示が見えちゃうのがあれですが・・・。

無事に生成されたのでpostgresql.confの内容を確認します。

```bash
$ egrep -n '(port = |shared_buffers = )' /tmp/pg16-2/postgresql.conf
64:port = 16002                         # (change requires restart)
130:shared_buffers = 65536kB            # min 128kB
$
```
指定した``port``と``shared_buffers``がpostgresql.confに書き込まれています。
shared_buffersに指定した65536kBは64MBには変換されずそのまま書き込まれているようですね。

### 同じGUCを複数指定した場合
同じGUCを複数指定した場合は、最後に指定したものが有効になるようです。

```bash
$ ~/pgsql/master/bin/initdb -c "port=16002" -c "port=16003" -c "port=16001" -U postgres -D /tmp/pg16
（略）
    /home/ec2-user/pgsql/master/bin/pg_ctl -D /tmp/pg16 -l logfile start

$ grep -n "port = " /tmp/pg16/postgresql.conf
64:port = 16001                         # (change requires restart)
$
```

### 存在しないオプションを指定した場合
存在しないオプションを指定した場合は、``initdb``実行時にエラーになります。

```bash
$ ~/pgsql/master/bin/initdb -D /tmp/pg16-1 -U postgres -c "hoge=5432"
（略）
creating configuration files ... ok
running bootstrap script ... 2023-05-19 11:22:51.222 GMT [557] LOG:  unrecognized configuration parameter "hoge" in file "/tmp/pg16-1/postgresql.conf" line 821
2023-05-19 11:22:51.222 GMT [557] FATAL:  configuration file "/tmp/pg16-1/postgresql.conf" contains errors
child process exited with exit code 1
initdb: removing data directory "/tmp/pg16-1"
$
```

### 不正な値を指定した場合
不正な値を指定した場合にも``initdb``実行時にエラーになります。きちんと値域もチェックしているようですね。

```bash
$ ~/pgsql/master/bin/initdb -D /tmp/pg16-1 -U postgres -c "port=0"
（略）
creating configuration files ... ok
running bootstrap script ... 2023-05-19 11:25:31.982 GMT [681] LOG:  0 is outside the valid range for parameter "port" (1 .. 65535)
2023-05-19 20:25:31.982 JST [681] FATAL:  configuration file "/tmp/pg16-1/postgresql.conf" contains errors
child process exited with exit code 1
initdb: removing data directory "/tmp/pg16-1"
$ 

```
# おわりに
普通にPostgreSQLを運用する場合には、最初の1回だけ実行する``initdb``ですが、検証時にはしばしばデータベースクラスタを再生成することがあります。そういうときに、GUCが指定できるのはありがたい機能ですね。
PostgreSQL 16のお気に入り新機能になりそうです。