CREATE SEQUENCE AS構文

# はじめに
にゃーん
この記事は、[PostgreSQL 10全部ぬこ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pg10-nuko-only) の1日目のエントリです。
一人でPostgreSQL 10のマイナーな新機能を紹介していくというこの無謀な試み、果たしてどうなるのやら・・・

# CREATE SEQUENCEとは何か
CREATE SEQUENCEは名前の通り、シーケンスオブジェクトを生成するコマンドです。PostgreSQLの場合、データ型の宣言として、serial, bigserialを指定するときに、暗黙的にシーケンスオブジェクトが生成されるので、このコマンドを直接実行することは少ないかもしれません。
なお、コマンド自体はPostgreSQL 7.1のころから存在するコマンドで、今更何か変更があるのかとちょっとびっくりした覚えがあります。

# 10で何が変わったか
PostgreSQL 9.6のCREATE SEQUENCEコマンドの構文はこんな感じ。

```
CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] name 
    [ INCREMENT [ BY ] increment ]
    [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
    [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { table_name.column_name | NONE } ]
```

10では、こんな構文になっています。

```
CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] name
    [ AS data_type ]
    [ INCREMENT [ BY ] increment ]
    [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]
    [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ]
    [ OWNED BY { table_name.column_name | NONE } ]
```

違いは```[AS data_type]```の追加ですね。
ここのdata_typeには以下のデータ型の名称が指定可能です。

* smallint (int2)
* integer (int4)
* bigint (int8)

```[AS data_type]```datatypeを指定しない場合（デフォルト）はbigintとして扱われます。これはPostgreSQL 9.6までのSEQUENCEと同じ挙動になります。

もう一つ留意すべきことは、```AS data_type```でsmallintを指定したとしても、別にシーケンスオブジェクトの大きさが小さくなるわけではないということ。上記でどのタイプを指定しても、結局bigint(8バイト)の領域として管理されるっぽい。なので、データサイズの節約のためには役に立たないようです。

# 実行例
## シンプルな実行例
まずはオプションなしで```AS data_type```句のみ設定してみる。

```
test=# CREATE SEQUENCE seq_1 AS smallint;
CREATE SEQUENCE
test=# CREATE SEQUENCE seq_2 AS integer;
CREATE SEQUENCE
test=# CREATE SEQUENCE seq_3 AS bigint;
CREATE SEQUENCE
test=#
```

psqlにはシーケンスの一覧を確認する \ds, \ds+ メタコマンドがあるので作成したシーケンスを確認する。

```
test=# \ds+
                        List of relations
 Schema | Name  |   Type   |  Owner   |    Size    | Description 
--------+-------+----------+----------+------------+-------------
 public | seq_1 | sequence | postgres | 8192 bytes | 
 public | seq_2 | sequence | postgres | 8192 bytes | 
 public | seq_3 | sequence | postgres | 8192 bytes | 
(3 rows)
```

## pg_sequence システムカタログ
さっき、\ds コマンドでの表示を見たのだが。あの一覧表示では、そのシーケンスの型や範囲を知ることができない。意外と使えん奴である。
代わりに、PostgreSQL 10からは、pg_sequenceシステムカタログが追加され、そのカタログ内容を見ることで、初期値・加算値・上限値を確認することができる。

```
test=# TABLE pg_sequence;
 seqrelid | seqtypid | seqstart | seqincrement |       seqmax        | seqmin | seqcache | seqcycle 
----------+----------+----------+--------------+---------------------+--------+----------+----------
    41768 |       21 |        1 |            1 |               32767 |      1 |        1 | f
    41770 |       23 |        1 |            1 |          2147483647 |      1 |        1 | f
    41772 |       20 |        1 |            1 | 9223372036854775807 |      1 |        1 | f
(3 rows)
```

他のシステムカタログ(pg_class, pg_type)と結合させて見やすくしてもOk。

```
test=# SELECT c.relname,t.typname, s.*  
  FROM pg_sequence as s, pg_class as c, pg_type t 
  WHERE s.seqrelid = c.oid AND s.seqtypid = t.oid;
 relname | typname | seqrelid | seqtypid | seqstart | seqincrement |       seqmax        | seqmin | seqcache | seqcycle 
---------+---------+----------+----------+----------+--------------+---------------------+--------+----------+----------
 seq_1   | int2    |    41768 |       21 |        1 |            1 |               32767 |      1 |        1 | f
 seq_2   | int4    |    41770 |       23 |        1 |            1 |          2147483647 |      1 |        1 | f
 seq_3   | int8    |    41772 |       20 |        1 |            1 | 9223372036854775807 |      1 |        1 | f
(3 rows)
```

## 値域はどうチェックされるのか
さて、実際にsmaillint, integerと指定した場合、それぞれの値域でチェックできるのか確認してみる。

```
test=# SELECT setval('seq_1',65536);
ERROR:  setval: value 65536 is out of bounds for sequence "seq_1" (1..32767)
test=# SELECT setval('seq_2',2147483648);
ERROR:  setval: value 2147483648 is out of bounds for sequence "seq_2" (1..2147483647)
test=# 
```

エラーメッセージに下限・上限も表示されますね。

# まとめ
この機能、これまで明示的にsmallintやintegerの値域をシーケンス作成時に設定しなければならなかったのを、smaillintやintegerという記述を可能とすることで、簡単に作成可能になったということかな。
合わせて、システムカタログとしてシーケンスの属性を分離して、シーケンスに関する情報が見やすくなった、というのもあるか。

# 参考：該当するリリースノート
本エントリに関連するPostgreSQL 10リリースノートの記載です。

E.2.2. Migration to Version 10
> * Move sequences' metadata fields into a new pg_sequence system catalog (Peter Eisentraut)

E.2.3.4. Utility Commands
> * Add CREATE SEQUENCE AS command to create a sequence matching an integer data type (Peter Eisentraut)

# おわりに
たった1つのエントリを書くのに2時間かかってしまった。こんな調子で25日分書いていけるのかっ！