JSONB演算子の優先順位ではまるとか

JSONBを使った履歴情報integer(履歴情報つきinteger試作 - 日々の記録 別館参照)を作っているときに、ちょっと嵌った現象があったので、備忘のために書いておく。

事象

JSONB演算子 || を使ったときに、以下の事象が起きた(ように勘違いした)。

  • 式1 || 式2 だと期待どおりの結果になる
  • 式2 || 式1 だとNULLになってしまう

例を示す。

test=# \pset null ぬるぽ
Null display is "ぬるぽ".
test=# SELECT '{"A":["C","D"]}'::jsonb->'A'  || '["X","Y"]'::jsonb ;
       ?column?       
----------------------
 ["C", "D", "X", "Y"]
(1 row)

test=# SELECT '["X","Y"]'::jsonb || '{"A":["C","D"]}'::jsonb->'A' ;
 ?column? 
----------
 ぬるぽ
(1 row)

test=#

アイエエエ!ぬるぽぬるぽナンデ!

原因

まあ、なんということはなく、演算子の優先順位の問題っぽい。
どうやら、連結演算子(||)のほうが キーに依るJSONオブジェクトフィールド取得演算子(->)よりも高いっぽい。
つまり、

SELECT '["X","Y"]'::jsonb || '{"A":["C","D"]}'::jsonb->'A' ;

というのは、

SELECT ( '["X","Y"]'::jsonb || '{"A":["C","D"]}'::jsonb )->'A' ;

というのと同じになる。

["X", "Y", {"A": ["C", "D"]}]

というJSONBオブジェクトに対して、'A'というキーでJSONBオブジェクトを取り出そうとするから、NULLとなってしまうというわけ。

式の見た目からすると、ちょっと直感的ではない気がするけど、そういうことらしい。

PostgrerSQL文書の表4.2 演算子の優先順位(高いものから低いものへ)を見ると、 || も -> も特に規定されていないので、(その他の演算子)と同じ枠になる。
なので、単純に左結合の規則が適用されてしまうと。

言われてみれば当たり前だが、連結演算子(||)って、なんとなく優先順位が低いように思っちゃたのが敗因か。