PostgreSQL 9.3 の JSON サポートについて（長いよッ）

Postgresql 9.3でJSON関連のサポートが強化されている。

* JSON型　（>=9.2）
* JSON演算子 (読み込み専用アクセッサ)
* JSONサポート関数 (JSON データ変換)

9.3リリースの目玉機能だが、使ってみた人が少ないのか一向に情報が上がってこない。
ドキュメントを見てもなんだか面倒くさそうで自分から使う気は起きない。

実際の使い勝手はどうなのか、たまたま開発で触る機会があったのでまとめておく。
PostgreSQL自体そんなに詳しいわけではないので、間違いがあったら突っ込んでほしい。

> 追記: PostgreSQL 9.4 のリリースで jsonb の登場するなど状況がかなり変わっている。
> 9.4 ついてもフォローしたので参考にしてほしい。

> * [PostgreSQL 9.4 の JSON サポートについて](http://qiita.com/kumazo@github/items/9010f9dad134b9d9d16d)
> * [PostgreSQL JSON の応用メモ](http://qiita.com/kumazo@github/items/19361acffddb6be10ac2)
 
## テーブルデータをJSONに変換する

データをJSON型に変換する関数としてto_json(), row_to_json(), array_to_json(), json_agg()などが用意されている。

単純にテーブルの行をJSONに変換するならto_json()やrow_to_json()を使う。

```sql
CREATE TABLE hoge (
    id INT, 
    name TEXT, 
    flag BOOLEAN, 
    at TIMESTAMPTZ
);
INSERT INTO hoge VALUES (1, '太郎', TRUE, now()), (2, '花子', FALSE, NULL);

SELECT to_json(hoge) FROM hoge;
                                 to_json                               
-------------------------------------------------------------------------
 {"id":1,"name":"太郎","flag":true,"at":"2014-01-01 01:23:45.123456+09"}
 {"id":2,"name":"花子","flag":false,"at":null}
(2 rows)
```

行データがJSONオブジェクト表現に変換されているのわかる。
（引数にはto_json(hoge.\*)とも書けるがto_json(\*)ではエラーとなった。）

row_to_json()でも同様の結果が得られるが、こちらではオプション引数として整形フラグが追加されていて、これにTRUEを与えると改行が入って少し見やすいJSON出力になる。

```sql
# select row_to_json(hoge, TRUE) from hoge WHERE id  = 1;
              row_to_json
----------------------------------------
 {"id":1,                              +
  "name":"太郎",                       +
  "flag":true,                         +
  "at":2014-01-01 01:23:45.123456+09"}
(1 行)
```

配列をJSON配列として変換用する関数として、array_to_json()が用意されている。
試しに行を配列に集約してからこの関数に渡してみると、レコード（行）を要素とするJSON配列として展開された。

```sql
SELECT array_to_json( array_agg(hoge)) FROM hoge;
                                                      array_to_json                                                         
-------------------------------------------------------------------------------------------------------------------------
 [{"id":1,"name":"太郎","flag":true,"at":"2014-03-13 11:55:58.812004+09"},{"id":2,"name":"花子","flag":false,"at":null}]
(1 row)
```

もっとも、複数行をJSON配列として集約したいのなら json_agg() を使うのが正しい。
こちらなら行ごとに改行コードが挿入されるので見やすい。

```sql
SELECT json_agg(hoge) FROM hoge;
                                  json_agg                                  
----------------------------------------------------------------------------
 [{"id":1,"name":"太郎","flag":true,"at":"2014-03-13 11:55:58.812004+09"}, +
  {"id":2,"name":"花子","flag":false,"at":null}]
(1 row)
```

ここまでは簡単。
しかし、カラム指定したり複数のテーブルをJOINしたSELECTの結果を返そうとすると、もう一発ではいかなくなる。

```sql
-- リストは渡せない
SELECT row_to_json(hoge.id, hoge.name) FROM hoge;
ERROR:  関数row_to_json(integer, text)は存在しません

-- ROWを使えば・・・コレジャナイ！
SELECT row_to_json( ROW(hoge.id, hoge.name)) FROM hoge;
     row_to_json      
----------------------
 {"f1":1,"f2":"太郎"}
 {"f1":2,"f2":"花子"}
(2 rows)
```

JSON関数には行(row)というより何らかの定義されたレコード（テーブル/複合型）にして渡す必要があるようだ。
さもなければ、いったんサブクエリやビューにするか、CTEを経由させるといった工夫をかませる。

```sql
-- CTE
WITH src AS ( 
  SELECT u.id, g.name AS group_name, u.name AS user_name
  FROM users AS u INNER JOIN groups AS g ON g.id = u.group_id
)
SELECT row_to_json(src) FROM src;
```

複合型や配列型をカラムにを持つようなテーブルを定義した場合、ちゃんとJSONのツリー状の階層構造として展開される。

```sql
CREATE TABLE tako (
    id serial,
    rec hoge, -- table 
    arr text[],
    jb json
);

INSERT INTO tako(rec, arr, jb)
SELECT 
    ROW(hoge.*)::hoge, 
    '{"JSON", "Qiita"}', 
    '{"info":{"owner":"qiita"}}'
FROM hoge
;

SELECT * FROM tako;
 id |                    rec                     |     arr      |             jb
----+--------------------------------------------+--------------+----------------------------
  1 | (1,太郎,t,"2015-07-01 00:00:00.000000+09") | {JSON,Qiita} | {"info":{"owner":"qiita"}}
  2 | (2,花子,f,)                                | {JSON,Qiita} | {"info":{"owner":"qiita"}}
(2 rows)

SELECT row_to_json(tako.*, true) FROM tako;
                                   row_to_json
---------------------------------------------------------------------------------
 {"id":1,                                                                       +
  "rec":{"id":1,"name":"太郎","flag":true,"at":"2015-07-01 00:00:00.000000+09"},+
  "arr":["JSON","Qiita"],                                                       +
  "jb":{"info":{"owner":"qiita"}}}
 {"id":2,                                                                       +
  "rec":{"id":2,"name":"花子","flag":false,"at":null},                          +
  "arr":["JSON","Qiita"],                                                       +
  "jb":{"info":{"owner":"qiita"}}}
(2 rows)

```
## JSONをテーブルデータに変換してみる

JSON をテーブルデータに変換するにはjson_populate_record()やjson_populate_recordset()を使う。

```sql
-- JSONをテーブルデータに変換
SELECT * FROM json_populate_record( NULL::hoge, 
    '{"id":1,"name":"太郎","flag":true, "at":"2014-01-01"}');
 id | name | flag |           at           
----+------+------+------------------------
  1 | 太郎 | t    | 2014-01-01 00:00:00+09
(1 行)

-- JSON配列を行に変換
SELECT * FROM json_populate_recordset( NULL::hoge, 
    '[ {"id":1,"name":"太郎","flag":false, "at":"2012-01-01"},
       {"id":2,"name":"花子","flag":true, "at":null},
       {"id":3,"name":"次郎","flag":true, "aaaaaa":"あああああ"} ]'
)
WHERE flag = true
; 
 id | name | flag | at 
----+------+------+----
  2 | 花子 | t    | 
  3 | 次郎 | t    | 
(2 行)

```

変換関数の最初の引数にNULL::table_nameのように既存のテーブル名でキャストしたNULLを渡している。
これは関数に任意の型を渡す時のイデオムのようだ。
変換先となるとなる型には既存テーブルや CREATE TYPE で定義した複合型を指定する。

ちなみにNULLではなく初期化済みの変数（レコード）を渡してもいい。
その場合は変数の型が適用され、変数の内容をデフォルト値としてJSONの内容で上書きされたレコードが返される。

さて上記SQLをそのままINSERT文に応用すればJSONからテーブルにデータ投入できる。

```sql
-- 個別INSERT
INSERT INTO hoge 
  SELECT * FROM json_populate_record( NULL::hoge, 
    '{"id":1,"name":"太郎","flag":true, "at":"2012-01-01"}'
  )
;

-- 一括INSERT
INSERT INTO hoge 
  SELECT * FROM json_populate_recordset( NULL::hoge,
    '[ {"id":2,"name":"一郎"},
       {"id":3,"name":"二郎"},
       {"id":4,"name":"三郎"} ]'
  )
;
```

これでrow_to_json()やjson_agg()で吐き出したJSONをそのままロードできることになる。

JSONフィールドとテーブルカラムは名前でマッピングされるので、JSON内のフィールド順序は気にしなくてよい。
省略されたフィールドはNULL扱いになり、余分なフィールドは無視されるので、カラム対応がそろっていなくてもエラーにはならない。
フィールド名に大文字があると拾われないようなので注意が必要。

フィールド値のデータ型の変換は自動的に行われるが、型変換に失敗した場合、たとえば数字でない文字列をINTEGER型カラムに入れようとするれば当然エラーになる。

上記例では簡単のため、カラム名のリストを省略した記法を使っているが、もちろんSERIALやDEFAULT指定、制約等を活かすには、取り込むカラム名をまじめに指定する必要がある。

UPDATE文では以下のようになる。

```sql
UPDATE hoge SET
    name = j.name, flag = j.flag, at = j.at
FROM
    json_populate_record( NULL::hoge, 
        '{"id":2, "name":"貞子", "flag":false, "at":"2011-11-11"}'
    ) AS j
WHERE 
  j.id = hoge.id
;
```

UPDATE文に省略記法はないので、必要な全てのカラムで代入を明示する必要がある。
そのためJSONにはあらかじめ更新しない値も含めた全てのカラムの対応データを詰めておかなければならない。
万一、JSONに対応するフィールドがないとカラム値がNULLで更新されてしまう。
ここはできればMongoDBのように特定のフィールドだけをupdateできたい。

json_populate_record()の第１引数に更新前レコードを渡せば、JSONが持つフィールドの値だけを上書きしたレコードが用意できるはずだ。

```sql
INSERT INTO hoge VALUES (123, 'タロウ', TRUE, '2014-01-01');

UPDATE hoge SET
  name = j.name, flag = j.flag, at = j.at
FROM
  hoge AS org
  INNER JOIN json_populate_record( org,  -- 更新前レコード 
    '{"id":123, "name":"ゾフィー"}'       -- JSONで上書き
  ) AS j ON j.id = org.id
WHERE 
  j.id = hoge.id
;
UPDATE 1

SELECT * FROM hoge WHERE id = 101;
 id  |   name   | flag |           at           
-----+----------+------+------------------------
 123 | ゾフィー | t    | 2014-01-01 00:00:00+09
(1 行)
```

期待通りうまく行った。

カラムの多いテーブルの更新でCOALESCE()を多用したUPDATE文にウンザリしたことのある人には、ちょっとうれしいhackになるかもしれない。

おっと、上記SQLをよく見れば、テーブル全行のレコードに対してjson_populate_record()が適用されてしまうのではないか、という気がすごくする。
それでは実用性がないので無駄を回避するためにはもう一工夫必要だ。
次のように結合条件で絞り込んでおけば大丈夫だろう。

```sql
UPDATE hoge SET
  name = j.name, flag = j.flag, at = j.at
FROM
  hoge AS org
  INNER JOIN json_populate_record( org, 
    $1                                        -- パラメータ渡し
  ) AS j ON ($1::JSON->>'id')::INT = org.id   -- JSON演算子でidを参照
WHERE 
  j.id = hoge.id
;
```

これまでサンプルコードのJSONをリテラルで記述していたが、実際には上記の様に文字列データとしてパラメータ渡しするのが普通だろう。

'->>'がJSON演算子と呼ばれる拡張だ。
それは次で解説する。


## JSON型を使ってみる

テーブルカラムにJSON型を指定できる。
JSON型へのデータ投入時にJSON文字列はしっかり構文チェックされる。
database の文字コードがUTF-8でないと壊れる場合があるようだ。

```sql

CREATE TABLE hage (id SERIAL, json_data JSON);
INSERT INTO hage (json_data) VALUES ( '{"name":"太郎", "tags":["posgtres","Qiita"]}' );


SELECT json_data FROM hage;
                 json_data               
--------------------------------------------
 {"name":"太郎", "tags":["posgtres", "Qiita"]}
(1 行)


-- JSON演算子を使う
SELECT 
  json_data->>'name' AS name,
  json_data->'tags'->>1 AS tag
FROM hage;
 name |  tag
------+------
 太郎 | Qiita
(1 行)

-- to_json()では子ノードになる。
SELECT to_json(hage) FROM hage;
                          to_json                          
-------------------------------------------------------------------
 {"id":3,"json_data":{"name":"太郎", "tags":["posgtres", "Qiita"]}}
(1 行)

```

JSON型の実体はテキストなので、TEXT型と相互にキャストすることができる。
明示的にキャストしなくても、可能な限り自動的に型変換されるようだ。

```sql
  '{"name":"太郎"}'::JSON          --　生JSONをJSON型にキャスト
  '{"name":"太郎"}'::JSON->>'name' -- '太郎'
  '{"name":"太郎"}'->>'name'        -- 暗黙型変換
  json_name::TEXT                  --　'{"name":"太郎"}'
  json_name::VARCHAR(5)           -- '{"nam'
  json_name::VARCHAR(5)::JSON     -- ERROR:  json型の入力構文が無効です

```

JSON演算子を使って、JSON内のフィールドにアクセスできる。

```sql
  json_book->>'title'   -- 'Waht's the "42" ?'
  json_book->'title'    -- '"What's the \"42\" ?"' 
  json_book->'chapters'->1->>'title' -- 階層をたどる
  json_book#>>'{chapters,1,title}'   -- 階層パスを指定

  -- 値のキャストが必要
  (json_book->>'price')::INTEGER > 1000    

  -- 比較演算子は提供されない
  json_data1 = json_data2  -- ERROR:  演算子が存在しません: json = json 

```

'->>' 演算子にはJSONのオブジェクトフィールド名や配列インデックスを指定し、その値を取得することができる。
フィールドが存在しない場合、NULLを返す。

'#>>' 演算子にはフィールド名や配列インデックスのリスト化した文字列を与えることでJSON構造のパスを指定することができる。
ワイルドカード等はなく、JSONPath的な柔軟性は望めない。
同様の機能を提供する関数としてjson_extract_path(), json_extract_path_text()も提供されている。

これらの演算子は値をTEXT型で返すので必要に応じてキャスト等で型変換が必要になる。

JSON演算子にはもう一組の別バージョンの'->'演算子と'#>'演算子が用意されていて、これらはTEXT型ではなくJSON型でフィールド値を返す。
値がスカラならそのJSON表現を返すがこれはあまり使い道がない。
一方、値がオブジェクトや配列ならJSON型の子ノードとして返されるので、続けて階層を辿るのに使える。

演算子の一覧表は以下の本家ドキュメントを参照してほしい。
* [表 9-39. JSON 演算子](http://www.postgresql.jp/document/9.3/html/functions-json.html#FUNCTIONS-JSON-OP-TABLE)

JSON演算子は参照専用だ。
つまりgetだけでsetはできない。
関数でもJSONの値を変更したり構造を操作する手段は用意されていないので、JSON型データをSQL上で更新するのは難しい。

基本的にPostgreSQLのJSONサポートとは、JSONテキストパーサを提供しました、ということらしい。（PostgreSQL 9.4 では jsonb の登場で状況が変わっている。）
JSON演算子も実質的にパーサ関数であり、フィールド名が文字列パラメータとして渡されているにすぎないと考えられる。

JSON演算子は文法的にはSQL文のいたるところで使用が許されるが、評価の度にパーサ関数が実行されていると考えれば、使いどころに気を使う。
特にWHEREで使用されれば、実行時に相当負荷を掛けることになることは通常の関数と同じだ。
JSON内部のフィールドにインデックスを張ることである程度この問題に対応できる。
JSONのインデックス化については次のセクションで取り上げる。

ところでJSON演算子が関数呼び出しと同等であるならば、応用としてカラムのパラメータ化に近いことが実現できそうだ。
プリペアドステートメントや関数でパラメータから与えられたフィールド名を実行時に動的に指定して評価する。
たとえば、JSON内に特定のフィールドが存在するかを調べたい場合、以下のように表現できる。

```sql

-- 指定のフィールドが存在するものを選択
SELECT id, jbspec->>$1 AS val FROM book WHERE (book.jbspec->>$1)::TEXT IS NOT NULL;

```

検索条件だけではなく、集約する出力項目や、ORDER BY句のソート対象を動的に切り替えたら面白いことができるかもしれない。

## JSONにインデックスを張る

SQLでJSON型の内部データを扱うなら、フィールドにインデックスを張らないと、パフォーマンス的に使い物にならない。
JSON文字列を都度パースするコストは馬鹿にならないからだ。

JSON内部のフィールドに対するインデックスを定義するのは難しくない。

```sql
CREATE INDEX blog_title_index ON blogs (content->>'title');
```

いわゆる関数インデックスとやっていることは変わらないだろう。

JSONへのインデックスの実効性についてはすでに詳細な検証をされた方がいるので下記ページ参照されたい。

* SIOS "OSSよろず" ブログ出張所: PostgreSQL 9.3 の JSON データのインデックス<br>
  http://sios-oss.blogspot.jp/2014/01/postgresql-93-json.html

インデックスを作成しても、テキストベースの値を使っている限りその性能を十分に活かせない。
この問題はJSONのバイナリフォーマットであるJSONBである程度改善される見込みだ。（後述）

ところで、JSON 内部にインデックスを張るためには DB 管理者がJSONの内部構造と用途を知っていなければならない。
つまり、データベースのスキーマ設計にJSONの内部構造が含まれるということだ。
JSON型を導入する利点がスキーマレスな半構造化による柔軟な運用にあるならこれは矛盾ではないのか。
それは本当にJSONを使ってやりたかったことなのか、そこにも検討の余地がある。

ドキュメント指向のデータストアしてJSON型の利用を考えているなら、当然、全文検索も気になるだろう。
個人的にPostgreSQLの全文検索の仕掛けをよく理解していないので、今回は検証しきれていないが、単純に考えればJSON型は所詮テキストなので難しくなはず。

JSON構造を辿って値だけを収集するようない手軽なツールは提供されていないが、構造を無視して正規表現などでフラットな文字列に変換するのは難しくない。
何ならJSONをTEXTにキャストしてまるごと処理しても案外実用に耐えるのではないか、と考えるのは素人判断だろうか。

> 追記: JSON インデックスについては PostgreSQL 9.4 の jsonb でかなり対応された。
> 以下で検証したので参考まで。
> 
> * [jsonb のインデックスサポート](http://qiita.com/kumazo@github/items/9010f9dad134b9d9d16d#jsonb-%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88) - PostgreSQL 
9.4 の JSON サポートについて

## その他

* 本稿で紹介していないがJSONを解析するサポート関数がいくつか用意されている。ストアドプロシージャでの使用を想定しているようだ。
* hstore から JSONに変換する関数が用意され、キャストも可能。しかし逆はできない。

## JSONの使いどころ

### ドキュメント指向DBとして

JSONのDBと聞いてまずMongoDBが思い浮かぶ。

MongoDBはドキュメント指向の代表的なNoSQLデータベースで、 JSONを基本的なデータフォーマットとして使う。
内部的にはそのバイナリ形式であるBSONに変換してデータをストアしている。

PostgreSQLでもJSONサポートを使えば、MongoDBのまねごとができそうな気がしないでもない。

本稿でJSONを弄ってみた感想としては、操作性や性能からいってもMongoDBと比較するまで成熟していない。
現状では単なるJSONバリデータ/パーサのサポートの域を出ず、特に更新系、MongoDBで言えばsaveはできるがupdateができないのはアプリケーション側の負担になるだろう。
PostgreSQLとしてもパラダイムの異なるMongoDBに対抗意識があるとは思えず、用途によって住み分けることになるだろう・・・などと当たり障りのないこと考えていた。

が、本稿をちんたら書いているうちにPostgreSQLのほうで動きがあった。
なんと2014年秋頃リリース予定のバージョン9.4では、jsonbというバイナリ版のJSON型が導入されるという。（後述）

ポスグレさん本気です。

### CSVの代替として

先に示したように、テーブルをJSONに変換するのは簡単だ。
たとえばファイルに吐き出すようなデータ移行やバックアップでCSVの代わりに使えるかもしれない。
特にカラムに配列や複合型を持つ複雑なテーブルデータの場合、外部ツールからも扱いやすくなるだろう。
実運用に導入するならパフォーマンスの問題や特殊文字の扱いなどで苦労する前に十分な検証作業が必要になる。
それならテーブルデータをファイルに吐き出すCOPYコマンドが将来JSON書式をサポートする可能性に期待して様子見するのもアリだ。

### 既存テーブルのインターフェースとして

テーブルとJSONを相互変換できるのなら、JSONベースのRESTfulで薄く汎用的なAPIが簡単に実現できそうな妄想が膨らむ。

更新系SQLの作り込みは避けられないので、そこをどこかで自動生成できればなんとかなるかも。

### ハッシュとして

たとえば コードを表示用文字列に変換しているところがあれば、JSONをハッシュ的に使うことができる。

```sql
SELECT
    id,
    status,
    '{"001":"審査中" '
    ',"002":"差戻し" '
    ',"003":"却下"   '
    ',"004":"承認"}  '::JSON->>status
    AS status_name
FROM request;
```

ときに読者の抱えるデータベースには「コードマスター」なるテーブルがあるのではないだろうか。
（それは今やOTLT（One True Lookup Table）と呼ばれる有名なアンチパターンに分類されているのだが・・・）
コードマスターテーブルを何回もJOINするのが苦痛であれば、いったんJSONデータとしてハッシュ化しておけば少しは楽になれるかもしれない。

> 追記: 何がいいたいのかこの文章だけではさっぱりわからない。
> OTLT のハッシュ化のイメージは以下を参照してほしい。
>
> * [2.4. OTLT を JSON でなんとかする](http://qiita.com/kumazo@github/items/19361acffddb6be10ac2#24-otlt-%E3%82%92-json-%E3%81%A7%E3%81%AA%E3%82%93%E3%81%A8%E3%81%8B%E3%81%99%E3%82%8B) - PostgreSQL JSON の応用メモ

### ロギング先として

今流行りのFluentdもまたJSONでログデータをやり取りする。

JSON型を使えばPostgreSQLが流し込み先として優位な選択肢になるのではないか。
FluentdのログレコードをJSONのまま保存し、解析はSQLで行う。
ログにあわせたテーブル定義するのとは違って、ログの項目の変更にも柔軟に対応できる。

JSON型のINSERTに走る書式検証のコストが問題になるなら、カラムをTEXT型にして生JSONとして格納し、解析時にJSONに変換するという手もある。

もう、対応したプラグインもありそうだが、どうか。

> 追記: Fluentdプラグインについては以下で検証したので参考まで
>
> * [2.5. ログちゃんぽん](http://qiita.com/kumazo@github/items/19361acffddb6be10ac2#25-%E3%83%AD%E3%82%B0%E3%81%A1%E3%82%83%E3%82%93%E3%81%BD%E3%82%93) - PostgreSQL JSON の応用メモ

### 「その他カラム」として

これまでも、テキスト型やBLOB型などでDBの関知しない構造でアプリケーション側で使う雑多なデータを保存するカラムがあらかじめ用意されることはままあった。
ちょっとしたフラグの追加、UIの状態やプレファレンス設定、タグのような属性の集まりのようなデータを詰め込めこんだ４次元ポケットのようなカラムで、テーブル定義の変更を避けつつ柔軟に運用するための苦肉の策である。

中身は大抵その場しのぎのCSVかKey=Vakueリスト形式で、もっと複雑ならXMLやシリアラライズしたオブジェクトを使うこともあるだろうが、JSON型にしておけば少しはメンテナンスしやすくなるだろう。

### ストアドプロシージャのデバッグツールとして

JSONデータをSQLで操作するのは難しいが、ストアドプロシージャ/ファンクションでプログラム的にできないこともない。
そのためのJSONサポート関数も用意されているが、それらを使ってもPL/SQLとJSONのパラダイムのギャップはいかんともしがたい。
JSON操作はアプリ側に任せてDBとしては丸ごとストアするだけにとどめておいたほうが幸せだろう。
（PL/JavaScriptがサポートされれば状況は変わるかもしれないが・・・）

もっともストプロのプログラマなら、JSONを扱う気が全くなくてもto_json()系の存在だけでも知っておいて損はない。
デバッグなどで変数やテーブルの内容を調べたいときに、JSONを簡易書式としてお手軽に使えるからだ。

```sql
CREATE OR REPLACE FUNCTION add_tags(article_id int, user_id int, tag_names text[]) 
RETURNS void AS $$
DECLARE
    params record;
    new_tag tags;
    err record;
BEGIN
    -- trace log
    RAISE LOG 'ENTER add_tags():%', to_json(($1, $2, $3));
    
    -- with names
    SELECT article_id, user_id, tag_names INTO params;
    RAISE LOG 'ENTER add_tags():%', to_json(params);

    -- dump
    INSERT INTO tags(name) VALUES (tag_names[1]) RETURNING * INTO new_tag;
    RAISE DEBUG E'new tag\n%', row_to_json(new_tag, TRUE);
    
    -- custom error
    SELECT 404 AS code, 'NOT FOUND' AS message, article_id INTO err;
    RAISE SQLSTATE 'XXXXX' USING MESSAGE = to_json(err);

END;
$$ LANGUAGE plpgsql;

SET client_min_messages TO DEBUG;

SELECT  add_tags(1234, 5678, ARRAY['postgres', 'json']);
LOG:  ENTER add_tags():{"f1":1234,"f2":5678,"f3":["postgres","json"]}
LOG:  ENTER add_tags():{"article_id":1234,"user_id":5678,"tag_names":["postgres","json"]}
DEBUG:  new tag
{"id":7,
 "name":"postgres",
 "banned":false,
 "created_at":"2014-03-05 05:15:32.979789+09"}
ERROR:  {"code":404,"message":"NOT FOUND","article_id":1234}
```

## jsonbについて

今秋リリース予定のPostgreSQL 9.4でjsonbという新たな型が導入されるという。

* PostgreSQL、BSON(バイナリJSON)に対応 | マイナビニュース  
http://news.mynavi.jp/news/2014/03/27/168/

jsonbはJSONのバイナリフォーマットで、テキストベースのJSONよりも効率的にデータを扱うことができるという。(~~上記記事のいうBSONに準拠しているのかは確証が見つからなかった~~ < JSONB は BSON とは関係のない独自の内部フォーマットだった)

既に開発版の本家ドキュメントでその仕様が伺える。
インデックス周りがかなり改善されるらしい。

* PostgreSQL 9.4devel Documentation 8.14. JSON Types  
~~http://www.postgresql.org/docs/devel/static/datatype-json.html~~  
http://www.postgresql.org/docs/9.4/static/datatype-json.html

現状のJSONサポートよりも機能は充実している。

* PostgreSQL 9.4devel Documentation 9.15. JSON Functions and Operators  
~~http://www.postgresql.org/docs/devel/static/functions-json.html~~  
http://www.postgresql.org/docs/9.4/static/functions-json.html

既存のJSON型と同様の使い勝手で、相互に変換もできる（ので本稿はムダにはならないw）。
jsonb用に演算子も増えたが、フィールドの追加削除やフィールド値の変更といった更新ができるとは書いていないようなので、これは実際に検証してみないとわからない。
一方、比較演算子が追加されていてこれはうれしい。

その一方なんと、類似したデータ構造であるhstoreの開発を停止し、jsonbに注力するという。
PostgresSQL開発陣の意気込みが伺えるというもの。

~~9.4 のリリース後に検証する時間がとれたらまた投稿するかも。~~
続編を投稿した。

* PostgreSQL 9.4 の JSON サポートについて - Qiita  
http://qiita.com/kumazo@github/items/9010f9dad134b9d9d16d

## 参考

PostgreSQL 9.3.2文書 8.14. JSONデータ型
http://www.postgresql.jp/document/9.3/html/datatype-json.html
PostgreSQL 9.3.2文書 9.15. JSON関数と演算子
http://www.postgresql.jp/document/9.3/html/functions-json.html
PostgreSQL 9.3.2文書9.20. 集約関数
http://www.postgresql.jp/document/9.3/html/functions-aggregate.html

What can you do with PostgreSQL and JSON? | clarkdave.net
http://clarkdave.net/2013/06/what-can-you-do-with-postgresql-and-json/

SIOS "OSSよろず" ブログ出張所: PostgreSQL9.3 の新機能 ～JSON 編～
http://sios-oss.blogspot.jp/2013/09/postgresql93-json.html
SIOS "OSSよろず" ブログ出張所: PostgreSQL 9.3 の JSON データのインデックス
http://sios-oss.blogspot.jp/2014/01/postgresql-93-json.html

What's new in PostgreSQL 9.3 - PostgreSQL wiki
JSON: Additional functionality
https://wiki.postgresql.org/wiki/What's_new_in_PostgreSQL_9.3#JSON:_Additional_functionality

PostgreSQL、BSON(バイナリJSON)に対応 | マイナビニュース
http://news.mynavi.jp/news/2014/03/27/168/

PostgreSQL 9.4devel Documentation 8.14. JSON Types
http://www.postgresql.org/docs/devel/static/datatype-json.html
PostgreSQL 9.4devel Documentation 9.15. JSON Functions and Operators
http://www.postgresql.org/docs/devel/static/functions-json.html