PG16:ALTER TABLE ... SET STORAGE default

# はじめに

にゃーん。趣味でポスグレをやっている者だ。

この記事は[PostgreSQL 16 全部ぬこ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pg16-all-nuko) 12日目の記事です。
今回はALTER TABLEコマンドのちょっとした変更について書いてみます。

# 概要

|項目|内容|
|:--|:--|
|タイトル|[ALTER TABLE ... SET STORAGE default](https://commitfest.postgresql.org/40/3830/)|
|Topic|Server Features|
|ステータス|commited|
|Last Modified|2022-11-10|
|概要|ALTER TABLEのSET STORAGEオプションに関する変更|

# 変更内容
発端となったメール([[PATCH] ALTER TABLE ... SET STORAGE default](https://www.postgresql.org/message-id/flat/CAJ7c6TMX9ui+6y3TQFaXJYVpZyBukvqhQbVDJ8OUokeLRhtnpA@mail.gmail.com))に書かれていたのは、
* ALTER TABLEに``SET COMPRESSION default``がある。
* でも、``SET STORAGE default``がないのは変では？
* 一度設定したストレージ種別を簡単に元に戻せないのではないか。

という内容のようです。

## SET STORAGE設定
PostgreSQLはページ(通常8192byte)という固定長の領域を使ってテーブルやインデックスの値を管理しています。
では、8192byteを超えるような長大な可変長データはどうやって管理しているのでしょうか？

PostgreSQLでは[TOAST](https://www.postgresql.jp/document/14/html/storage-toast.html)と呼ばれる方法を使って長大なデータ（だいたい2Kbyteを超えるもの）を管理しています。TOASTは別領域（TOASTテーブル）に追い出して分割格納したり、対象の領域を圧縮したりします。
TOASTには4つの格納戦略があり、それを利用者が列単位で指定することができます。

|戦略名|内容|
|:--|:--|
|PLAIN|TOASTテーブルへの追い出しや圧縮を行わない。<br>短い固定長データ型(integerとか)はこの戦略になる。|
|EXTENDED|TOASTテーブルへの追い出しと圧縮を許可する。<br>組み込みの可変長データ(textとか)はこの戦略になる。|
|EXTERNAL|TOASTテーブルへの追い出しを行う。データ量は大きくなるが、少しだけ取り出しが早い。<br>この戦略がデフォルトで設定されている組み込みデータ型はたぶんないはず。|
|MAIN|圧縮は許容するが、極力TOASTテーブルへの追い出しは行わない。短めの可変長データ(numericとか)はこの戦略になる。|

格納戦略は、``psql``の``\d+ <テーブル名>``などのメタコマンドで確認することができます。
以下は、PostgreSQL 15での表示例です。
``Storage``列に格納戦略が表示されています。

```sql
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | extended |             |              |
Access method: heap

=#
```

格納戦略はデータ型によりデフォルトの戦略が設定されていますが、``ALTER TABLE ... SET STORAGE``コマンドにより変更することができます。

:::note info 
あえて格納戦略の変更が必要なケースはほとんどない、と自分は思っていますが・・・。
:::

以下は、``ALTER TABLE``コマンドで、text型の``tdata``列の格納戦略を``external``に変更する例です。

```sql
=# ALTER TABLE test ALTER COLUMN tdata SET STORAGE EXTERNAL;
ALTER TABLE
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | external |             |              |
Access method: heap

=#
```

## PostgreSQL 15まで
PostgreSQL 15までは、格納戦略を変更したあとデータ型デフォルトの格納戦略に戻すためには、明示的にその格納戦略を指定する必要がありました。

```sql
=# ALTER TABLE test ALTER COLUMN tdata SET STORAGE EXTENDED;
ALTER TABLE
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | extended |             |              |
Access method: heap

=#
```

## PostgreSQL 16から
PostgreSQL 16からは、格納戦略を変更したあとデータ型デフォルトの格納戦略に戻すために、``SET STORAGE default``という指定が可能になりました。

``ALTER TABLE``で``text``型の``tdata``の格納戦略を``external``に変更しておきます。

```sql
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | extended |             |              |
Access method: heap

=# ALTER TABLE test ALTER COLUMN tdata SET STORAGE EXTERNAL;
ALTER TABLE
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | external |             |              |
Access method: heap

=#
```

この状態から``tdata``の格納戦略をデフォルト(``extended``)に戻すために、``ALTER TABLE ... SET STORAGE default``を実行します。

```sql
=# ALTER TABLE test ALTER COLUMN tdata SET STORAGE default ;
ALTER TABLE
=# \d+ test
                                               Table "public.test"
 Column |       Type       | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer          |           |          |         | plain    |             |              |
 n_data | numeric          |           |          |         | main     |             |              |
 f_data | double precision |           |          |         | plain    |             |              |
 tdata  | text             |           |          |         | extended |             |              |
Access method: heap

=#
```

``tdata``の格納戦略が``extended``に変更されたことが確認できました。

## 余談
この``SET STORAGE default``という指定方法ですが、MLでの議論では``RESET STORAGE``とかのほうが良いのでは？という話も出ていたようですが、既に``SET COMPRESSION compression_method``の``compression_method``に``default``を使っているから、そっちにならおう、という経緯があったみたいです（自分がMLの内容を読み違えてなければ・・・）。

# おわりに
今回は``ALTER TABLE``で指定する格納戦略指定に関するちょっとした改善について書きました。

格納戦略については、以前自分も実験はしてPostgreSQLアンカンファレンスで発表したのですが（[TOAST](https://speakerdeck.com/nuko_yokohama/toast-postgresql)）、こうかはばつぐんだ！という用途はまだ良くわかっていないです・・・。